1 ## -*- encoding: utf-8 -*-
3 #__author__ = "Łukasz Rekucki"
4 #__date__ = "$2009-09-18 10:49:24$"
6 #__doc__ = """RAL implementation over Mercurial"""
9 #from mercurial import localrepo as hglrepo
10 #from mercurial import ui as hgui
11 #from mercurial.node import nullid
15 #FILTER = re.compile(r"^pub_(.+)\.xml$", re.UNICODE)
17 #def default_filter(name):
18 # m = FILTER.match(name)
20 # return name, m.group(1)
23 #class MercurialLibrary(wlrepo.Library):
25 # def __init__(self, path, maincabinet="default", ** kwargs):
26 # super(wlrepo.Library, self).__init__( ** kwargs)
28 # self._hgui = hgui.ui()
29 # self._hgui.config('ui', 'quiet', 'true')
30 # self._hgui.config('ui', 'interactive', 'false')
33 # self._ospath = self._sanitize_string(os.path.realpath(path))
35 # maincabinet = self._sanitize_string(maincabinet)
37 # if os.path.isdir(path):
39 # self._hgrepo = hglrepo.localrepository(self._hgui, path)
40 # except mercurial.error.RepoError:
41 # raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path)
42 # elif kwargs.get('create', False):
45 # self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1)
46 # except mercurial.error.RepoError:
47 # raise wlrepo.LibraryException("[HGLibrary] Can't create a repository on path '%s'." % path)
49 # raise wlrepo.LibraryException("[HGLibrary] Can't open a library on path '%s'." % path)
51 # # fetch the main cabinet
52 # lock = self._hgrepo.lock()
54 # btags = self._hgrepo.branchtags()
56 # if not self._has_branch(maincabinet):
57 # raise wlrepo.LibraryException("[HGLibrary] No branch named '%s' to init main cabinet" % maincabinet)
59 # self._maincab = MercurialCabinet(self, maincabinet)
68 # def main_cabinet(self):
69 # return self._maincab
71 # def document(self, docid, user, part=None, shelve=None):
72 # return self.cabinet(docid, user, create=False).retrieve(part=part, shelve=shelve)
74 # def cabinet(self, docid, user, create=False):
75 # docid = self._sanitize_string(docid)
76 # user = self._sanitize_string(user)
78 # bname = self._bname(user, docid)
80 # lock = self._lock(True)
82 # if self._has_branch(bname):
83 # return MercurialCabinet(self, doc=docid, user=user)
86 # raise wlrepo.CabinetNotFound(bname)
88 # # check if the docid exists in the main cabinet
89 # needs_touch = not self._maincab.exists(docid)
90 # cab = MercurialCabinet(self, doc=docid, user=user)
92 # name, fileid = cab._filename(None)
94 # def cleanup_action(l):
96 # l._fileopener()(fileid, "w").write('')
99 # garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]
100 # l._filesrm(garbage)
101 # print "removed: ", garbage
103 # # create the branch
104 # self._create_branch(bname, before_commit=cleanup_action)
105 # return MercurialCabinet(self, doc=docid, user=user)
117 # def _lock(self, write_mode=False):
118 # return self._hgrepo.wlock() # no support for read/write mode yet
120 # def _transaction(self, write_mode, action):
121 # lock = self._lock(write_mode)
123 # return action(self)
128 # # Basic repo manipulation
131 # def _checkout(self, rev, force=True):
132 # return MergeStatus(mercurial.merge.update(self._hgrepo, rev, False, force, None))
134 # def _merge(self, rev):
135 # """ Merge the revision into current working directory """
136 # return MergeStatus(mercurial.merge.update(self._hgrepo, rev, True, False, None))
138 # def _common_ancestor(self, revA, revB):
139 # return self._hgrepo[revA].ancestor(self.repo[revB])
141 # def _commit(self, message, user=u"library"):
142 # return self._hgrepo.commit(text=message, user=user)
145 # def _fileexists(self, fileid):
146 # return (fileid in self._hgrepo[None])
148 # def _fileadd(self, fileid):
149 # return self._hgrepo.add([fileid])
151 # def _filesadd(self, fileid_list):
152 # return self._hgrepo.add(fileid_list)
154 # def _filerm(self, fileid):
155 # return self._hgrepo.remove([fileid])
157 # def _filesrm(self, fileid_list):
158 # return self._hgrepo.remove(fileid_list)
160 # def _filelist(self, filter=default_filter):
161 # for name in self._hgrepo[None]:
162 # result = filter(name)
163 # if result is None: continue
167 # def _fileopener(self):
168 # return self._hgrepo.wopener
170 # def _filectx(self, fileid, branchid):
171 # return self._hgrepo.filectx(fileid, changeid=branchid)
173 # def _changectx(self, nodeid):
174 # return self._hgrepo.changectx(nodeid)
177 # # BASIC BRANCH routines
180 # def _bname(self, user, docid):
181 # """Returns a branch name for a given document and user."""
182 # docid = self._sanitize_string(docid)
183 # uname = self._sanitize_string(user)
184 # return "personal_" + uname + "_file_" + docid;
186 # def _has_branch(self, name):
187 # return self._hgrepo.branchmap().has_key(self._sanitize_string(name))
189 # def _branch_tip(self, name):
190 # name = self._sanitize_string(name)
191 # return self._hgrepo.branchtags()[name]
193 # def _create_branch(self, name, parent=None, before_commit=None):
194 # name = self._sanitize_string(name)
196 # if self._has_branch(name): return # just exit
199 # parent = self._maincab
201 # parentrev = parent._hgtip()
203 # self._checkout(parentrev)
204 # self._hgrepo.dirstate.setbranch(name)
206 # if before_commit: before_commit(self)
208 # self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library')
210 # # revert back to main
211 # self._checkout(self._maincab._hgtip())
212 # return self._branch_tip(name)
214 # def _switch_to_branch(self, branchname):
215 # current = self._hgrepo[None].branch()
217 # if current == branchname:
218 # return current # quick exit
220 # self._checkout(self._branch_tip(branchname))
223 # def shelf(self, nodeid=None):
225 # nodeid = self._maincab._name
226 # return MercurialShelf(self, self._changectx(nodeid))
234 # def _sanitize_string(s):
235 # if isinstance(s, unicode):
236 # s = s.encode('utf-8')
239 #class MercurialCabinet(wlrepo.Cabinet):
241 # def __init__(self, library, branchname=None, doc=None, user=None):
243 # super(MercurialCabinet, self).__init__(library, doc=doc, user=user)
244 # self._branchname = library._bname(user=user, docid=doc)
246 # super(MercurialCabinet, self).__init__(library, name=branchname)
247 # self._branchname = branchname
249 # raise ValueError("Provide either doc/user or branchname")
252 # return self._library.shelf(self._branchname)
255 # return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist()))
257 # def retrieve(self, part=None, shelf=None):
258 # name, fileid = self._filename(part)
260 # print "Retrieving document %s from cab %s" % (name, self._name)
263 # raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.")
265 # def retrieve_action(l,c):
266 # if l._fileexists(fileid):
267 # return MercurialDocument(c, name=name, fileid=fileid)
268 # print "File %s not found " % fileid
271 # return self._execute_in_branch(retrieve_action)
273 # def create(self, name, initial_data):
274 # name, fileid = self._filename(name)
277 # raise ValueError("Can't create main doc for maincabinet.")
279 # def create_action(l, c):
280 # if l._fileexists(fileid):
281 # raise wlrepo.LibraryException("Can't create document '%s' in cabinet '%s' - it already exists" % (fileid, c.name))
283 # fd = l._fileopener()(fileid, "w")
284 # fd.write(initial_data)
287 # l._commit("File '%s' created." % fileid)
288 # return MercurialDocument(c, fileid=fileid, name=name)
290 # return self._execute_in_branch(create_action)
292 # def exists(self, part=None, shelf=None):
293 # name, filepath = self._filename(part)
295 # if filepath is None: return False
296 # return self._execute_in_branch(lambda l, c: l._fileexists(filepath))
298 # def _execute_in_branch(self, action, write=False):
299 # def switch_action(library):
300 # old = library._switch_to_branch(self._branchname)
302 # return action(library, self)
304 # library._switch_to_branch(old)
306 # return self._library._transaction(write_mode=write, action=switch_action)
309 # def _filename(self, docid):
310 # return self._partname(docid, 'xml')
312 # def _partname(self, docid, part):
313 # docid = self._library._sanitize_string(part)
314 # part = self._library._sanitize_string(part)
319 # if self._maindoc == '' and docid is None:
322 # return 'pub_' + docid + '.' + part
324 # def _fileopener(self):
325 # return self._library._fileopener()
328 # return self._library._branch_tip(self._branchname)
330 # def _filectx(self, fileid):
331 # return self._library._filectx(fileid, self._branchname)
334 # return (self._library.main_cabinet == self)
336 #class MercurialDocument(wlrepo.Document):
338 # def __init__(self, cabinet, docid):
339 # super(MercurialDocument, self).__init__(cabinet, name=docid)
340 # self._opener = self._cabinet._fileopener()
341 # self._docid = docid
344 # def _ctx(self, part):
345 # if not self._ctxs.has_key(part):
346 # self._ctxs[part] = self._cabinet._filectx(self._fileid())
347 # return self._ctxs[part]
349 # def _fileid(self, part='xml'):
350 # return self._cabinet._partname(self._docid, part)
352 # def read(self, part='xml'):
353 # return self._opener(self._ctx(part).path(), "r").read()
355 # def write(self, data, part='xml'):
356 # return self._opener(self._ctx(part).path(), "w").write(data)
358 # def commit(self, message, user):
359 # """Commit all parts of the document."""
360 # self.library._fileadd(self._fileid)
361 # self.library._commit(self._fileid, message, user)
364 # """Update parts of the document."""
365 # lock = self.library._lock()
367 # if self._cabinet.ismain():
368 # return True # always up-to-date
370 # user = self._cabinet.username or 'library'
371 # mdoc = self.library.document(self._fileid)
373 # mshelf = mdoc.shelf()
374 # shelf = self.shelf()
376 # if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf):
377 # shelf.merge_with(mshelf, user=user)
383 # def share(self, message):
384 # lock = self.library._lock()
386 # print "sharing from", self._cabinet, self._cabinet.username
388 # if self._cabinet.ismain():
389 # return True # always shared
391 # if self._cabinet.username is None:
392 # raise ValueError("Can only share documents from personal cabinets.")
394 # user = self._cabinet.username
396 # main = self.library.shelf()
397 # local = self.shelf()
404 # # * <- can also be here!
409 # # The local branch has been recently updated,
410 # # so we don't need to update yet again, but we need to
411 # # merge down to default branch, even if there was
412 # # no commit's since last update
414 # if main.ancestorof(local):
416 # main.merge_with(local, user=user, message=message)
426 # # Default has no changes, to update from this branch
427 # # since the last merge of local to default.
428 # elif local.has_common_ancestor(main):
430 # if not local.parentof(main):
431 # main.merge_with(local, user=user, message=message)
437 # # * <- this case overlaps with previos one
443 # # There was a recent merge to the defaul branch and
444 # # no changes to local branch recently.
446 # # Use the fact, that user is prepared to see changes, to
447 # # update his branch if there are any
448 # elif local.ancestorof(main):
450 # if not local.parentof(main):
451 # local.merge_with(main, user=user, message='Local branch update.')
455 # local.merge_with(main, user=user, message='Local branch update.')
456 # local = self.shelf()
457 # main.merge_with(local, user=user, message=message)
459 # print "no_changes: ", no_changes
465 # return self.library.main_cabinet.retrieve(self._name)
467 # def exists(self, part='xml'):
468 # return self._cabinet.exists(self._fileid(part))
472 # return self._filectx.size()
475 # return self._cabinet.shelf()
478 # def last_modified(self):
479 # return self._filectx.date()
482 # return u"Document(%s->%s)" % (self._cabinet.name, self._name)
484 # def __eq__(self, other):
485 # return self._filectx == other._filectx
489 #class MercurialShelf(wlrepo.Shelf):
491 # def __init__(self, lib, changectx):
492 # super(MercurialShelf, self).__init__(lib)
494 # if isinstance(changectx, str):
495 # self._changectx = lib._changectx(changectx)
497 # self._changectx = changectx
501 # return self._changectx.node()
504 # return self._changectx.hex()
506 # def __repr__(self):
507 # return "MercurialShelf(%s)" % self._changectx.hex()
509 # def ancestorof(self, other):
510 # nodes = list(other._changectx._parents)
511 # while nodes[0].node() != nullid:
513 # if v == self._changectx:
515 # nodes.extend( v._parents )
518 # def parentof(self, other):
519 # return self._changectx in other._changectx._parents
521 # def has_common_ancestor(self, other):
522 # a = self._changectx.ancestor(other._changectx)
523 # # print a, self._changectx.branch(), a.branch()
525 # return (a.branch() == self._changectx.branch())
527 # def merge_with(self, other, user, message):
528 # lock = self._library._lock(True)
530 # self._library._checkout(self._changectx.node())
531 # self._library._merge(other._changectx.node())
532 # self._library._commit(user=user, message=message)
536 # def __eq__(self, other):
537 # return self._changectx.node() == other._changectx.node()
540 #class MergeStatus(object):
541 # def __init__(self, mstatus):
542 # self.updated = mstatus[0]
543 # self.merged = mstatus[1]
544 # self.removed = mstatus[2]
545 # self.unresolved = mstatus[3]
548 # return self.unresolved == 0
550 #class UpdateStatus(object):
552 # def __init__(self, mstatus):
553 # self.modified = mstatus[0]
554 # self.added = mstatus[1]
555 # self.removed = mstatus[2]
556 # self.deleted = mstatus[3]
557 # self.untracked = mstatus[4]
558 # self.ignored = mstatus[5]
559 # self.clean = mstatus[6]
561 # def has_changes(self):
562 # return bool(len(self.modified) + len(self.added) + \
563 # len(self.removed) + len(self.deleted))
565 #__all__ = ["MercurialLibrary"]