1 # -*- encoding: utf-8 -*-
3 __author__= "Ćukasz Rekucki"
4 __date__ = "$2009-09-25 09:33:02$"
5 __doc__ = "Module documentation."
8 from mercurial import localrepo as hglrepo
9 from mercurial import ui as hgui
10 from mercurial import error
13 from wlrepo.mercurial_backend import MercurialRevision
14 from wlrepo.mercurial_backend.document import MercurialDocument
16 class MergeStatus(object):
17 def __init__(self, mstatus):
18 self.updated = mstatus[0]
19 self.merged = mstatus[1]
20 self.removed = mstatus[2]
21 self.unresolved = mstatus[3]
24 return self.unresolved == 0
26 class UpdateStatus(object):
28 def __init__(self, mstatus):
29 self.modified = mstatus[0]
30 self.added = mstatus[1]
31 self.removed = mstatus[2]
32 self.deleted = mstatus[3]
33 self.untracked = mstatus[4]
34 self.ignored = mstatus[5]
35 self.clean = mstatus[6]
37 def has_changes(self):
38 return bool(len(self.modified) + len(self.added) + \
39 len(self.removed) + len(self.deleted))
41 class MercurialLibrary(wlrepo.Library):
42 """Mercurial implementation of the Library API"""
44 def __init__(self, path, **kwargs):
45 super(wlrepo.Library, self).__init__( ** kwargs)
50 self._hgui = hgui.ui()
51 self._hgui.config('ui', 'quiet', 'true')
52 self._hgui.config('ui', 'interactive', 'false')
55 self._ospath = self._sanitize_string(os.path.realpath(path))
57 if os.path.isdir(path):
59 self._hgrepo = hglrepo.localrepository(self._hgui, path)
60 except mercurial.error.RepoError:
61 raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path)
62 elif kwargs.get('create', False):
65 self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1)
66 except mercurial.error.RepoError:
67 raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path)
69 raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path)
73 return [ key[5:].decode('utf-8') for key in \
74 self._hgrepo.branchmap() if key.startswith("$doc:") ]
78 return self._ospath.decode('utf-8')
80 def document_for_rev(self, revision):
82 raise ValueError("Revision can't be None.")
84 if not isinstance(revision, MercurialRevision):
85 rev = self.get_revision(revision)
89 if not self._doccache.has_key(str(rev)):
90 self._doccache[str(rev)] = MercurialDocument(self, rev)
92 # every revision is a document
93 return self._doccache[str(rev)]
95 def document(self, docid, user=None):
96 return self.document_for_rev(self.fulldocid(docid, user))
98 def get_revision(self, revid):
99 revid = self._sanitize_string(revid).decode('utf-8')
101 print "Looking up rev %r (%s)" %(revid, type(revid))
104 # THIS IS THE MOST BRAIN-DEAD API I EVER SEEN
105 # WHY DO ALL THE OTHER METHODS SIMPLY
106 # FAIL WHEN GIVEN UNICODE, WHEN THIS WORKS ONLY!! WITH IT
108 ctx = self._changectx( revid )
109 except mercurial.error.RepoError, e:
110 raise wlrepo.RevisionNotFound(revid)
113 raise wlrepo.RevisionNotFound(revid)
115 if self._revcache.has_key(ctx):
116 return self._revcache[ctx]
118 return MercurialRevision(self, ctx)
120 def fulldocid(self, docid, user=None):
123 fulldocid += u'$user:' + user
124 fulldocid += u'$doc:' + docid
127 def has_revision(self, revid):
131 except mercurial.error.RepoError:
134 def document_create(self, docid):
137 # check if it already exists
138 fullid = self.fulldocid(docid)
140 if self.has_revision(fullid):
141 raise wlrepo.DocumentAlreadyExists(u"Document %s already exists!" % docid);
144 self._create_branch(self._sanitize_string(fullid))
145 return self.document_for_rev(fullid)
155 def lock(self, write_mode=False):
156 return self._hgrepo.wlock() # no support for read/write mode yet
158 def _transaction(self, write_mode, action):
159 lock = self.lock(write_mode)
166 # Basic repo manipulation
169 def _checkout(self, rev, force=True):
170 return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None))
172 def _merge(self, rev):
173 """ Merge the revision into current working directory """
174 return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None))
176 def _common_ancestor(self, revA, revB):
177 return self._hgrepo[revA].ancestor(self.repo[revB])
179 def _commit(self, message, user=u"library"):
180 return self._hgrepo.commit(text=message, user=user)
183 def _fileexists(self, fileid):
184 return (fileid in self._hgrepo[None])
186 def _fileadd(self, fileid):
187 return self._hgrepo.add([fileid])
189 def _filesadd(self, fileid_list):
190 return self._hgrepo.add(fileid_list)
192 def _filerm(self, fileid):
193 return self._hgrepo.remove([fileid])
195 def _filesrm(self, fileid_list):
196 return self._hgrepo.remove(fileid_list)
198 def _fileopen(self, path, mode):
199 return self._hgrepo.wopener(path, mode)
201 def _filectx(self, fileid, revid):
202 return self._hgrepo.filectx(fileid, changeid=revid)
204 def _changectx(self, nodeid):
205 return self._hgrepo.changectx(nodeid)
208 return self._hgrepo.rollback()
211 # BASIC BRANCH routines
214 def _bname(self, user, docid):
215 """Returns a branch name for a given document and user."""
216 docid = self._sanitize_string(docid)
217 uname = self._sanitize_string(user)
218 return "personal_" + uname + "_file_" + docid;
220 def _has_branch(self, name):
221 return self._hgrepo.branchmap().has_key(self._sanitize_string(name))
223 def _branch_tip(self, name):
224 name = self._sanitize_string(name)
225 return self._hgrepo.branchtags()[name]
227 def _create_branch(self, name, parent=None, before_commit=None):
228 name = self._sanitize_string(name)
230 if self._has_branch(name): return # just exit
233 parentrev = self._hgrepo['$branchbase'].node()
235 parentrev = parent.hgrev()
237 self._checkout(parentrev)
238 self._hgrepo.dirstate.setbranch(name)
240 if before_commit: before_commit(self)
242 self._commit("$ADMN$ Initial commit for branch '%s'." % name, user='$library')
244 def _switch_to_branch(self, branchname):
245 current = self._hgrepo[None].branch()
247 if current == branchname:
248 return current # quick exit
250 self._checkout(self._branch_tip(branchname))
258 def _sanitize_string(s):
262 if isinstance(s, unicode):
263 s = s.encode('utf-8')