X-Git-Url: https://git.mdrn.pl/redakcja.git/blobdiff_plain/b2d72160e2a68991c66ea6017b871d7f42c0b29d..470817c5149ca294bd929fb29632a91c985aef0a:/lib/wlrepo/backend_mercurial.py diff --git a/lib/wlrepo/backend_mercurial.py b/lib/wlrepo/backend_mercurial.py index 38f01da6..12711222 100644 --- a/lib/wlrepo/backend_mercurial.py +++ b/lib/wlrepo/backend_mercurial.py @@ -1,4 +1,5 @@ # -*- encoding: utf-8 -*- + __author__ = "Łukasz Rekucki" __date__ = "$2009-09-18 10:49:24$" @@ -7,6 +8,7 @@ __doc__ = """RAL implementation over Mercurial""" import mercurial from mercurial import localrepo as hglrepo from mercurial import ui as hgui +from mercurial.node import hex as to_hex import re import wlrepo @@ -36,8 +38,8 @@ class MercurialLibrary(wlrepo.Library): try: self._hgrepo = hglrepo.localrepository(self._hgui, path) except mercurial.error.RepoError: - raise wlrepo.LibraryException("[HGLibrary]Not a valid repository at path '%s'." % path) - elif kwargs['create']: + raise wlrepo.LibraryException("[HGLibrary] Not a valid repository at path '%s'." % path) + elif kwargs.get('create', False): os.makedirs(path) try: self._hgrepo = hglrepo.localrepository(self._hgui, path, create=1) @@ -62,6 +64,9 @@ class MercurialLibrary(wlrepo.Library): def main_cabinet(self): return self._maincab + def document(self, docid, user): + return self.cabinet(docid, user, create=False).retrieve() + def cabinet(self, docid, user, create=False): bname = self._bname(user, docid) @@ -71,23 +76,20 @@ class MercurialLibrary(wlrepo.Library): return MercurialCabinet(self, bname, doc=docid, user=user) if not create: - raise wlrepo.CabinetNotFound(docid, user) + raise wlrepo.CabinetNotFound(bname) # check if the docid exists in the main cabinet - needs_touch = not self._maincab.exists(docid) - print "Creating branch: ", needs_touch + needs_touch = not self._maincab.exists(docid) cab = MercurialCabinet(self, bname, doc=docid, user=user) - fileid = cab._fileid(None) + name, fileid = cab._filename(None) def cleanup_action(l): - if needs_touch: - print "Touch for file", docid + if needs_touch: l._fileopener()(fileid, "w").write('') l._fileadd(fileid) - garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)] - print "Garbage: ", garbage + garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)] l._filesrm(garbage) # create the branch @@ -128,10 +130,8 @@ class MercurialLibrary(wlrepo.Library): def _common_ancestor(self, revA, revB): return self._hgrepo[revA].ancestor(self.repo[revB]) - def _commit(self, message, user="library"): - return self._hgrepo.commit(\ - text=self._sanitize_string(message), \ - user=self._sanitize_string(user)) + def _commit(self, message, user=u"library"): + return self._hgrepo.commit(text=message, user=user) def _fileexists(self, fileid): @@ -158,6 +158,9 @@ class MercurialLibrary(wlrepo.Library): def _fileopener(self): return self._hgrepo.wopener + + def _filectx(self, fileid, branchid): + return self._hgrepo.filectx(fileid, changeid=branchid) # # BASIC BRANCH routines @@ -191,7 +194,6 @@ class MercurialLibrary(wlrepo.Library): if before_commit: before_commit(self) - print "commiting" self._commit("[AUTO] Initial commit for branch '%s'." % name, user='library') # revert back to main @@ -218,11 +220,10 @@ class MercurialLibrary(wlrepo.Library): # @staticmethod - def _sanitize_string(s): - if isinstance(s, unicode): # - return s.encode('utf-8') - else: # it's a string, so we have no idea what encoding it is - return s + def _sanitize_string(s): + if isinstance(s, unicode): + s = s.encode('utf-8') + return s class MercurialCabinet(wlrepo.Cabinet): @@ -234,19 +235,25 @@ class MercurialCabinet(wlrepo.Cabinet): self._branchname = branchname + def shelf(self, selector=None): + if selector is not None: + raise NotImplementedException() + + return MercurialShelf(self, self._hgtip()) + def documents(self): - return self._execute_in_branch(action=lambda l, c: ( e[1] for e in l._filelist()) ) + return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist())) - def retrieve(self, part=None, shelve=None): - fileid = self._fileid(part) + def retrieve(self, part=None, shelf=None): + name, fileid = self._filename(part) if fileid is None: raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.") - return self._execute_in_branch(lambda l, c: MercurialDocument(c, fileid)) + return self._execute_in_branch(lambda l, c: MercurialDocument(c, name, fileid)) - def create(self, name, initial_data=''): - fileid = self._fileid(name) + def create(self, name, initial_data): + name, fileid = self._filename(name) if name is None: raise ValueError("Can't create main doc for maincabinet.") @@ -257,18 +264,18 @@ class MercurialCabinet(wlrepo.Cabinet): fd = l._fileopener()(fileid, "w") fd.write(initial_data) - l._fileadd(fileid) - l._commit("File '%d' created.") - - return MercurialDocument(c, fileid) + fd.close() + l._fileadd(fileid) + l._commit("File '%s' created." % fileid) + return MercurialDocument(c, fileid=fileid, name=name) return self._execute_in_branch(create_action) - def exists(self, part=None, shelve=None): - fileid = self._fileid(part) + def exists(self, part=None, shelf=None): + name, filepath = self._filename(part) - if fileid is None: return false - return self._execute_in_branch(lambda l, c: l._fileexists(fileid)) + if filepath is None: return False + return self._execute_in_branch(lambda l, c: l._fileexists(filepath)) def _execute_in_branch(self, action, write=False): def switch_action(library): @@ -280,16 +287,17 @@ class MercurialCabinet(wlrepo.Cabinet): return self._library._transaction(write_mode=write, action=switch_action) - def _fileid(self, part): + def _filename(self, part): + part = self._library._sanitize_string(part) fileid = None if self._maindoc == '': - if part is None: return None + if part is None: return [None, None] fileid = part else: fileid = self._maindoc + (('$' + part) if part else '') - return 'pub_' + fileid + '.xml' + return fileid, 'pub_' + fileid + '.xml' def _fileopener(self): return self._library._fileopener() @@ -297,17 +305,143 @@ class MercurialCabinet(wlrepo.Cabinet): def _hgtip(self): return self._library._branch_tip(self._branchname) + def _filectx(self, fileid): + return self._library._filectx(fileid, self._branchname) + class MercurialDocument(wlrepo.Document): - def __init__(self, cabinet, fileid): - super(MercurialDocument, self).__init__(cabinet, fileid) - self._opener = self._cabinet._fileopener() + def __init__(self, cabinet, name, fileid): + super(MercurialDocument, self).__init__(cabinet, name=name) + self._opener = self._cabinet._fileopener() + self._fileid = fileid + self._refresh() + + def _refresh(self): + self._filectx = self._cabinet._filectx(self._fileid) + self._shelf = MercurialShelf(self, self._filectx.node()) def read(self): - return self._opener(self._name, "r").read() + return self._opener(self._filectx.path(), "r").read() def write(self, data): - return self._opener(self._name, "w").write(data) + return self._opener(self._filectx.path(), "w").write(data) + + def commit(self, message, user): + self.library._fileadd(self._fileid) + self.library._commit(self._fileid, message, user) + + def update(self): + if self._cabinet.is_main(): + return True # always up-to-date + + mdoc = self.library.document(self._fileid) + + mshelf = mdoc.shelf() + shelf = self.shelf() + + if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf): + shelf.merge_with(mshelf) + + return rc.ALL_OK + + def share(self, message, user): + if self._cabinet.is_main(): + return True # always shared + + main = self.shared_version() + local = self.shelf() + + no_changes = True + + # Case 1: + # * local + # | + # * <- can also be here! + # /| + # / | + # main * * + # | | + # The local branch has been recently updated, + # so we don't need to update yet again, but we need to + # merge down to default branch, even if there was + # no commit's since last update + + if main.ancestorof(local): + main.merge_with(local, user=user, message=message) + no_changes = False + + # Case 2: + # + # main * * local + # |\ | + # | \| + # | * + # | | + # + # Default has no changes, to update from this branch + # since the last merge of local to default. + elif main.has_common_ancestor(local): + if not local.parentof(main): + main.merge_with(local, user=user, message=message) + no_changes = False + + # Case 3: + # main * + # | + # * <- this case overlaps with previos one + # |\ + # | \ + # | * local + # | | + # + # There was a recent merge to the defaul branch and + # no changes to local branch recently. + # + # Use the fact, that user is prepared to see changes, to + # update his branch if there are any + elif local.ancestorof(main): + if not local.parentof(main): + local.merge_with(main, user=user, message='Local branch update.') + no_changes = False + else: + local.merge_with(main, user=user, message='Local branch update.') + + self._refresh() + local = self.shelf() + + main.merge_with(local, user=user, message=message) + + def shared(self): + return self.library.document(self._fileid) + + @property + def size(self): + return self._filectx.size() + + + def shelf(self): + return self._shelf + + @property + def last_modified(self): + return self._filectx.date() + + def __str__(self): + return u"Document(%s->%s)" % (self._cabinet.name, self._name) + + +class MercurialShelf(wlrepo.Shelf): + + def __init__(self, cabinet, revision): + super(MercurialShelf, self).__init__(cabinet) + self._revision = revision + + @property + def _rev(self): + return _revision + + def __str__(self): + return to_hex(self._revision) class MergeStatus(object): @@ -335,5 +469,4 @@ class UpdateStatus(object): return bool(len(self.modified) + len(self.added) + \ len(self.removed) + len(self.deleted)) - -__all__ = ["MercurialLibrary", "MercurialCabinet", "MercurialDocument"] \ No newline at end of file +__all__ = ["MercurialLibrary"] \ No newline at end of file