DC resource. More merge tests.
[redakcja.git] / lib / wlrepo / backend_mercurial.py
index 1271122..dfcef09 100644 (file)
@@ -8,7 +8,7 @@ __doc__ = """RAL implementation over Mercurial"""
 import mercurial
 from mercurial import localrepo as hglrepo
 from mercurial import ui as hgui
 import mercurial
 from mercurial import localrepo as hglrepo
 from mercurial import ui as hgui
-from mercurial.node import hex as to_hex
+from mercurial.node import nullid
 import re
 import wlrepo
 
 import re
 import wlrepo
 
@@ -60,6 +60,10 @@ class MercurialLibrary(wlrepo.Library):
         finally:
             lock.release()
 
         finally:
             lock.release()
 
+    @property
+    def ospath(self):
+        return self._ospath
+
     @property
     def main_cabinet(self):
         return self._maincab
     @property
     def main_cabinet(self):
         return self._maincab
@@ -68,19 +72,22 @@ class MercurialLibrary(wlrepo.Library):
         return self.cabinet(docid, user, create=False).retrieve()
 
     def cabinet(self, docid, user, create=False):
         return self.cabinet(docid, user, create=False).retrieve()
 
     def cabinet(self, docid, user, create=False):
+        docid = self._sanitize_string(docid)
+        user = self._sanitize_string(user)
+        
         bname = self._bname(user, docid)
 
         lock = self._lock(True)
         try:
             if self._has_branch(bname):
         bname = self._bname(user, docid)
 
         lock = self._lock(True)
         try:
             if self._has_branch(bname):
-                return MercurialCabinet(self, bname, doc=docid, user=user)
+                return MercurialCabinet(self, doc=docid, user=user)
 
             if not create:
                 raise wlrepo.CabinetNotFound(bname)
 
             # check if the docid exists in the main cabinet
             needs_touch = not self._maincab.exists(docid)            
 
             if not create:
                 raise wlrepo.CabinetNotFound(bname)
 
             # check if the docid exists in the main cabinet
             needs_touch = not self._maincab.exists(docid)            
-            cab = MercurialCabinet(self, bname, doc=docid, user=user)
+            cab = MercurialCabinet(self, doc=docid, user=user)
 
             name, fileid = cab._filename(None)
 
 
             name, fileid = cab._filename(None)
 
@@ -91,10 +98,11 @@ class MercurialLibrary(wlrepo.Library):
                 
                 garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]                
                 l._filesrm(garbage)
                 
                 garbage = [fid for (fid, did) in l._filelist() if not did.startswith(docid)]                
                 l._filesrm(garbage)
+                print "removed: ", garbage
 
             # create the branch
             self._create_branch(bname, before_commit=cleanup_action)
 
             # create the branch
             self._create_branch(bname, before_commit=cleanup_action)
-            return MercurialCabinet(self, bname, doc=docid, user=user)
+            return MercurialCabinet(self, doc=docid, user=user)
         finally:
             lock.release()
             
         finally:
             lock.release()
             
@@ -161,6 +169,9 @@ class MercurialLibrary(wlrepo.Library):
 
     def _filectx(self, fileid, branchid):
         return self._hgrepo.filectx(fileid, changeid=branchid)
 
     def _filectx(self, fileid, branchid):
         return self._hgrepo.filectx(fileid, changeid=branchid)
+
+    def _changectx(self, nodeid):
+        return self._hgrepo.changectx(nodeid)
     
     #
     # BASIC BRANCH routines
     
     #
     # BASIC BRANCH routines
@@ -209,10 +220,10 @@ class MercurialLibrary(wlrepo.Library):
         self._checkout(self._branch_tip(branchname))
         return branchname        
 
         self._checkout(self._branch_tip(branchname))
         return branchname        
 
-    #
-    # Merges
-    #
-    
+    def shelf(self, nodeid=None):
+        if nodeid is None:
+            nodeid = self._maincab._name
+        return MercurialShelf(self, self._changectx(nodeid))   
 
 
     #
 
 
     #
@@ -227,19 +238,18 @@ class MercurialLibrary(wlrepo.Library):
 
 class MercurialCabinet(wlrepo.Cabinet):
     
 
 class MercurialCabinet(wlrepo.Cabinet):
     
-    def __init__(self, library, branchname, doc=None, user=None):
+    def __init__(self, library, branchname=None, doc=None, user=None):
         if doc and user:
             super(MercurialCabinet, self).__init__(library, doc=doc, user=user)
         if doc and user:
             super(MercurialCabinet, self).__init__(library, doc=doc, user=user)
-        else:
+            self._branchname = library._bname(user=user, docid=doc)
+        elif branchname:
             super(MercurialCabinet, self).__init__(library, name=branchname)
             super(MercurialCabinet, self).__init__(library, name=branchname)
-            
-        self._branchname = branchname
+            self._branchname = branchname
+        else:
+            raise ValueError("Provide either doc/user or branchname")
 
     def shelf(self, selector=None):
 
     def shelf(self, selector=None):
-        if selector is not None:
-            raise NotImplementedException()
-
-        return MercurialShelf(self, self._hgtip())
+        return self._library.shelf(self._branchname)
 
     def documents(self):        
         return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist()))
 
     def documents(self):        
         return self._execute_in_branch(action=lambda l, c: (e[1] for e in l._filelist()))
@@ -247,10 +257,18 @@ class MercurialCabinet(wlrepo.Cabinet):
     def retrieve(self, part=None, shelf=None):
         name, fileid = self._filename(part)
 
     def retrieve(self, part=None, shelf=None):
         name, fileid = self._filename(part)
 
+        print "Retrieving document %s from cab %s" % (name, self._name)
+
         if fileid is None:
             raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.")
         if fileid is None:
             raise wlrepo.LibraryException("Can't retrieve main document from main cabinet.")
+
+        def retrieve_action(l,c):
+            if l._fileexists(fileid):
+                return MercurialDocument(c, name=name, fileid=fileid)
+            print "File %s not found " % fileid
+            return None
                 
                 
-        return self._execute_in_branch(lambda l, c: MercurialDocument(c, name, fileid))
+        return self._execute_in_branch(retrieve_action)        
 
     def create(self, name, initial_data):
         name, fileid = self._filename(name)
 
     def create(self, name, initial_data):
         name, fileid = self._filename(name)
@@ -289,15 +307,15 @@ class MercurialCabinet(wlrepo.Cabinet):
 
     def _filename(self, part):
         part = self._library._sanitize_string(part)
 
     def _filename(self, part):
         part = self._library._sanitize_string(part)
-        fileid = None
+        docid = None
 
         if self._maindoc == '':
 
         if self._maindoc == '':
-            if part is None: return [None, None]
-            fileid = part
+            if part is None: rreeturn [None, None]
+            docid = part
         else:
         else:
-            fileid = self._maindoc + (('$' + part) if part else '')
+            docid = self._maindoc + (('$' + part) if part else '')
 
 
-        return fileid, 'pub_' + fileid + '.xml'
+        return docid, 'pub_' + docid + '.xml'
 
     def _fileopener(self):
         return self._library._fileopener()
 
     def _fileopener(self):
         return self._library._fileopener()
@@ -308,17 +326,19 @@ class MercurialCabinet(wlrepo.Cabinet):
     def _filectx(self, fileid):
         return self._library._filectx(fileid, self._branchname)
 
     def _filectx(self, fileid):
         return self._library._filectx(fileid, self._branchname)
 
+    def ismain(self):
+        return (self._library.main_cabinet == self)
+
 class MercurialDocument(wlrepo.Document):
 
     def __init__(self, cabinet, name, fileid):
         super(MercurialDocument, self).__init__(cabinet, name=name)
         self._opener = self._cabinet._fileopener()
         self._fileid = fileid
 class MercurialDocument(wlrepo.Document):
 
     def __init__(self, cabinet, name, fileid):
         super(MercurialDocument, self).__init__(cabinet, name=name)
         self._opener = self._cabinet._fileopener()
         self._fileid = fileid
-        self._refresh()
+        self.refresh()
 
 
-    def _refresh(self):
-        self._filectx = self._cabinet._filectx(self._fileid)
-        self._shelf = MercurialShelf(self, self._filectx.node())
+    def refresh(self):
+        self._filectx = self._cabinet._filectx(self._fileid)        
 
     def read(self):
         return self._opener(self._filectx.path(), "r").read()
 
     def read(self):
         return self._opener(self._filectx.path(), "r").read()
@@ -331,96 +351,117 @@ class MercurialDocument(wlrepo.Document):
         self.library._commit(self._fileid, message, user)
 
     def update(self):
         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)
+        lock = self.library._lock()
+        try:
+            if self._cabinet.ismain():
+                return True # always up-to-date
 
 
-        mshelf = mdoc.shelf()
-        shelf = self.shelf()
+            user = self._cabinet.username or 'library'
+            mdoc = self.library.document(self._fileid)
 
 
-        if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf):
-            shelf.merge_with(mshelf)
+            mshelf = mdoc.shelf()
+            shelf = self.shelf()
 
 
-        return rc.ALL_OK
+            if not mshelf.ancestorof(shelf) and not shelf.parentof(mshelf):
+                shelf.merge_with(mshelf, user=user)
 
 
-    def share(self, message, user):
-        if self._cabinet.is_main():
-            return True # always shared
+            return True
+        finally:
+            lock.release()            
 
 
-        main = self.shared_version()
-        local = self.shelf()
+    def share(self, message):
+        lock = self.library._lock()
+        try:
+            print "sharing from", self._cabinet, self._cabinet.username
+            
+            if self._cabinet.ismain():
+                return True # always shared
 
 
-        no_changes = True
+            if self._cabinet.username is None:
+                raise ValueError("Can only share documents from personal cabinets.")
+            
+            user = self._cabinet.username
 
 
-        # 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
+            main = self.library.shelf()
+            local = self.shelf()
 
 
-        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):
+            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):
+                print "case 1"
                 main.merge_with(local, user=user, message=message)
                 no_changes = False
                 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):
+            # Case 2:
+            #
+            # main *  * local
+            #      |\ |
+            #      | \|
+            #      |  *
+            #      |  |
+            #
+            # Default has no changes, to update from this branch
+            # since the last merge of local to default.
+            elif local.has_common_ancestor(main):
+                print "case 2"
+                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):
+                print "case 3"
+                if not local.parentof(main):
+                    local.merge_with(main, user=user, message='Local branch update.')
+                    no_changes = False
+            else:
+                print "case 4"
                 local.merge_with(main, user=user, message='Local branch update.')
                 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()
+                local = self.shelf()
+                main.merge_with(local, user=user, message=message)
 
 
-            main.merge_with(local, user=user, message=message)
+            print "no_changes: ", no_changes
+            return no_changes
+        finally:
+            lock.release()
                
     def shared(self):
                
     def shared(self):
-        return self.library.document(self._fileid)
+        return self.library.main_cabinet.retrieve(self._name)
+
+    def exists(self):
+        return self._cabinet.exists(self._fileid)
 
     @property
     def size(self):
         return self._filectx.size()
 
     @property
     def size(self):
         return self._filectx.size()
-
     
     def shelf(self):
     
     def shelf(self):
-        return self._shelf
+        return MercurialShelf(self.library, self._filectx.node())
 
     @property
     def last_modified(self):
 
     @property
     def last_modified(self):
@@ -429,20 +470,61 @@ class MercurialDocument(wlrepo.Document):
     def __str__(self):
         return u"Document(%s->%s)" % (self._cabinet.name, self._name)
 
     def __str__(self):
         return u"Document(%s->%s)" % (self._cabinet.name, self._name)
 
+    def __eq__(self, other):
+        return self._filectx == other._filectx
+
+
 
 class MercurialShelf(wlrepo.Shelf):
 
 
 class MercurialShelf(wlrepo.Shelf):
 
-    def __init__(self, cabinet, revision):
-        super(MercurialShelf, self).__init__(cabinet)
-        self._revision = revision
+    def __init__(self, lib, changectx):
+        super(MercurialShelf, self).__init__(lib)
+
+        if isinstance(changectx, str):
+            self._changectx = lib._changectx(changectx)
+        else:
+            self._changectx = changectx
 
     @property
     def _rev(self):
 
     @property
     def _rev(self):
-        return _revision
+        return self._changectx.node()
 
     def __str__(self):
 
     def __str__(self):
-        return to_hex(self._revision)
+        return self._changectx.hex()
+
+    def __repr__(self):
+        return "MercurialShelf(%s)" % self._changectx.hex()
+
+    def ancestorof(self, other):
+        nodes = list(other._changectx._parents)
+        while nodes[0].node() != nullid:
+            v = nodes.pop(0)
+            if v == self._changectx:
+                return True
+            nodes.extend( v._parents )
+        return False
+
+    def parentof(self, other):
+        return self._changectx in other._changectx._parents
+
+    def has_common_ancestor(self, other):
+        a = self._changectx.ancestor(other._changectx)
+        # print a, self._changectx.branch(), a.branch()
 
 
+        return (a.branch() == self._changectx.branch())
+
+    def merge_with(self, other, user, message):
+        lock = self._library._lock(True)
+        try:
+            self._library._checkout(self._changectx.node())
+            self._library._merge(other._changectx.node())
+            self._library._commit(user=user, message=message)
+        finally:
+            lock.release()
+
+    def __eq__(self, other):
+        return self._changectx.node() == other._changectx.node()
+        
 
 class MergeStatus(object):
     def __init__(self, mstatus):
 
 class MergeStatus(object):
     def __init__(self, mstatus):