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.document import MercurialDocument
14 from wlrepo.mercurial_backend import MercurialRevision
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:] for key in \
74 self._hgrepo.branchmap() if key.startswith("$doc:") ]
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 ctx = self._changectx(revid)
102 raise RevisionNotFound(revid)
104 if self._revcache.has_key(ctx):
105 return self._revcache[ctx]
107 return MercurialRevision(self, ctx)
109 def fulldocid(self, docid, user=None):
112 fulldocid += '$user:' + user
113 fulldocid += '$doc:' + docid
117 def has_revision(self, revid):
121 except error.RepoError:
124 def document_create(self, docid):
125 # check if it already exists
126 fullid = self.fulldocid(docid)
128 if self.has_revision(fullid):
129 raise LibraryException("Document already exists!");
132 self._create_branch(fullid)
133 return self.document_for_rev(fullid)
143 def _lock(self, write_mode=False):
144 return self._hgrepo.wlock() # no support for read/write mode yet
146 def _transaction(self, write_mode, action):
147 lock = self._lock(write_mode)
154 # Basic repo manipulation
157 def _checkout(self, rev, force=True):
158 return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None))
160 def _merge(self, rev):
161 """ Merge the revision into current working directory """
162 return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None))
164 def _common_ancestor(self, revA, revB):
165 return self._hgrepo[revA].ancestor(self.repo[revB])
167 def _commit(self, message, user=u"library"):
168 return self._hgrepo.commit(text=message, user=user)
171 def _fileexists(self, fileid):
172 return (fileid in self._hgrepo[None])
174 def _fileadd(self, fileid):
175 return self._hgrepo.add([fileid])
177 def _filesadd(self, fileid_list):
178 return self._hgrepo.add(fileid_list)
180 def _filerm(self, fileid):
181 return self._hgrepo.remove([fileid])
183 def _filesrm(self, fileid_list):
184 return self._hgrepo.remove(fileid_list)
186 def _fileopen(self, path, mode):
187 return self._hgrepo.wopener(path, mode)
189 def _filectx(self, fileid, revid):
190 return self._hgrepo.filectx(fileid, changeid=revid)
192 def _changectx(self, nodeid):
193 return self._hgrepo.changectx(nodeid)
196 # BASIC BRANCH routines
199 def _bname(self, user, docid):
200 """Returns a branch name for a given document and user."""
201 docid = self._sanitize_string(docid)
202 uname = self._sanitize_string(user)
203 return "personal_" + uname + "_file_" + docid;
205 def _has_branch(self, name):
206 return self._hgrepo.branchmap().has_key(self._sanitize_string(name))
208 def _branch_tip(self, name):
209 name = self._sanitize_string(name)
210 return self._hgrepo.branchtags()[name]
212 def _create_branch(self, name, parent=None, before_commit=None):
213 name = self._sanitize_string(name)
215 if self._has_branch(name): return # just exit
218 parentrev = self._hgrepo['$branchbase'].node()
220 parentrev = parent.hgrev()
222 self._checkout(parentrev)
223 self._hgrepo.dirstate.setbranch(name)
225 if before_commit: before_commit(self)
227 self._commit("$ADMN$ Initial commit for branch '%s'." % name, user='$library')
229 def _switch_to_branch(self, branchname):
230 current = self._hgrepo[None].branch()
232 if current == branchname:
233 return current # quick exit
235 self._checkout(self._branch_tip(branchname))
243 def _sanitize_string(s):
244 if isinstance(s, unicode):
245 s = s.encode('utf-8')
248 raise ValueError('Whitespace is forbidden!')