Title: [commits] (morgen) [11275] (0.7alpha3 branch) Andi's fix for the "modified in one view, deleted in the other" repository merge issue, bug 6266.
Revision
11275
Author
morgen
Date
2006-07-28 08:02:20 -0700 (Fri, 28 Jul 2006)

Log Message

(0.7alpha3 branch) Andi's fix for the "modified in one view, deleted in the other" repository merge issue, bug 6266. r=morgen

Modified Paths

Diff

Modified: branches/0.7alpha3/chandler/parcels/osaf/sharing/Sharing.py (11274 => 11275)

--- branches/0.7alpha3/chandler/parcels/osaf/sharing/Sharing.py	2006-07-28 13:38:16 UTC (rev 11274)
+++ branches/0.7alpha3/chandler/parcels/osaf/sharing/Sharing.py	2006-07-28 15:02:20 UTC (rev 11275)
@@ -26,6 +26,7 @@
 from repository.schema.Types import Type
 from repository.util.Lob import Lob
 from chandlerdb.item.ItemError import NoSuchAttributeError
+from repository.persistence.RepositoryError import MergeError
 from PyICU import ICUtzinfo
 import M2Crypto.BIO, WebDAV, twisted.web.http, zanshin.webdav, wx
 from cStringIO import StringIO
@@ -78,6 +79,13 @@
         # 'value' is the one from the *this* view
         # getattr(item, attribute) is the value from a different view
 
+        if code == MergeError.DELETE:
+            logger.debug("Sharing conflict on item %(item)s, "
+                "deleted locally, modified remotely",
+                { 'item' : item, }
+            )
+            return True
+
         logger.debug("Sharing conflict on item %(item)s, attribute "
             "%(attribute)s: %(local)s vs %(remote)s", {
                 'item' : item,

Modified: branches/0.7alpha3/chandler/repository/item/Item.py (11274 => 11275)

--- branches/0.7alpha3/chandler/repository/item/Item.py	2006-07-28 13:38:16 UTC (rev 11274)
+++ branches/0.7alpha3/chandler/repository/item/Item.py	2006-07-28 15:02:20 UTC (rev 11275)
@@ -1441,56 +1441,65 @@
                 self._status &= ~Item.DEFERRING
             
             else:
-                refs = self._references
-                values = self._values
-                others = []
+                self._delete(view, recursive, deletePolicy, _noMonitors, False)
 
-                self.setDirty(Item.NDIRTY)
-                self._status |= Item.DELETING
+    def _delete(self, view, recursive, deletePolicy, _noMonitors, _keepRoot):
 
-                if hasattr(type(self), 'onItemDelete'):
-                    self.onItemDelete(view, False)
+        refs = self._references
+        values = self._values
+        others = []
 
-                for child in self.iterChildren():
-                    child.delete(True, deletePolicy)
+        self.setDirty(Item.NDIRTY)
+        self._status |= Item.DELETING
 
-                if self.isWatched():
-                    view._notifyChange(self._itemChanged, 'remove',
-                                       ('itsKind',))
-                    self._status &= ~Item.WATCHED
+        if hasattr(type(self), 'onItemDelete'):
+            self.onItemDelete(view, False)
 
-                if 'watcherDispatch' in values:
-                    del values['watcherDispatch']
-                view._unregisterWatches(self)
+        for child in self.iterChildren():
+            child.delete(True, deletePolicy)
 
-                if 'monitors' in refs:
-                    for monitor in self.monitors:
-                        monitor.delete(True, None, None, True)
+        if self.isWatched():
+            view._notifyChange(self._itemChanged, 'remove',
+                               ('itsKind',))
+            self._status &= ~Item.WATCHED
 
-                for name in refs.keys():
-                    policy = (deletePolicy or
-                              self.getAttributeAspect(name, 'deletePolicy',
-                                                      False, None, 'remove'))
-                    if policy == 'cascade':
-                        value = refs._getRef(name)
-                        if value is not None:
-                            if value._isRefs():
-                                others.extend(value)
-                            else:
-                                others.append(value)
+        if 'watcherDispatch' in values:
+            del values['watcherDispatch']
+        view._unregisterWatches(self)
 
-                for other in others:
-                    if other.refCount(True) == 0:
-                        other.delete(recursive, deletePolicy)
+        if 'monitors' in refs:
+            for monitor in self.monitors:
+                monitor.delete(True, None, None, True)
 
-                self._setKind(None, _noMonitors)
+        for name in refs.keys():
+            policy = (deletePolicy or
+                      self.getAttributeAspect(name, 'deletePolicy',
+                                              False, None, 'remove'))
+            if policy == 'cascade':
+                value = refs._getRef(name)
+                if value is not None:
+                    if value._isRefs():
+                        others.extend(value)
+                    else:
+                        others.append(value)
 
-                self.itsParent._removeItem(self)
-                self._setRoot(None, view)
+        for other in others:
+            if other.refCount(True) == 0:
+                other.delete(recursive, deletePolicy)
 
-                self._status |= Item.DELETED | Item.STALE
-                self._status &= ~(Item.DELETING | Item.DEFERRED)
+        self._setKind(None, _noMonitors)
+        self.itsParent._removeItem(self)
 
+        if _keepRoot:  # during merge (to delete deferred children)
+            view._unregisterItem(self, False)
+            self._status |= Item.DELETED
+        else:
+            self._setRoot(None, view)
+            self._status |= Item.DELETED | Item.STALE
+
+        self._status |= Item.DELETED | Item.STALE
+        self._status &= ~(Item.DELETING | Item.DEFERRED)
+
     def _copyExport(self, view, cloudAlias, matches):
 
         uuid = self._uuid

Modified: branches/0.7alpha3/chandler/repository/persistence/DBRepositoryView.py (11274 => 11275)

--- branches/0.7alpha3/chandler/repository/persistence/DBRepositoryView.py	2006-07-28 13:38:16 UTC (rev 11274)
+++ branches/0.7alpha3/chandler/repository/persistence/DBRepositoryView.py	2006-07-28 15:02:20 UTC (rev 11275)
@@ -379,15 +379,36 @@
 
     def _refreshForwards(self, mergeFn, newVersion, notify):
 
-        history = []
-        refreshes = set()
-        deletes = set()
-        merges = {}
-        unloads = {}
-        dangling = []
+        scan = True
+        while scan:
+            history = []
+            refreshes = set()
+            deletes = set()
+            merges = {}
+            unloads = {}
+            dangling = []
 
-        self._scanHistory(self.itsVersion, newVersion,
-                          history, refreshes, merges, unloads, deletes)
+            self._scanHistory(self.itsVersion, newVersion, history,
+                              refreshes, merges, unloads, deletes)
+            scan = False
+
+            if merges:
+                # if item is deleted in other view, resolve conflict by
+                # deleting item locally (with mergeFn approval)
+                for uItem in deletes:
+                    if uItem in merges:
+                        item = self[uItem]
+                        if not item.isDeferred():
+                            if (mergeFn is None or
+                                not mergeFn(MergeError.DELETE, item,
+                                            None, None)):
+                                self._e_2_delete(item, newVersion)
+                        if not item.isDeleted():
+                            item.delete(True)
+                            if item.isDeferred():
+                                item._delete(self, True, None, False, True)
+                        scan = True
+
         oldVersion = self._version
         self._version = newVersion
 
@@ -695,7 +716,7 @@
                 elif item.itsVersion < version:
                     unloads[uItem] = item
 
-            elif uItem in self._deletedRegistry:
+            elif uItem in self._deletedRegistry and uItem not in deletes:
                 kind = self.find(uKind, False)
                 if kind is None:
                     self._e_1_delete(uItem, uKind, oldVersion, version)
@@ -824,6 +845,10 @@
 
         raise MergeError, ('delete', uItem, 'item %s was deleted in this version (%d) but has later changes in version (%d) where it is of kind %s' %(uItem, oldVersion, newVersion, uKind), MergeError.CHANGE)
 
+    def _e_2_delete(self, item, version):
+
+        raise MergeError, ('delete', item, 'item %s was changed in this view but was deleted in version (%d)' %(item._repr_(), version), MergeError.DELETE)
+
     def _e_1_name(self, list, key, newName, name):
         raise MergeError, ('name', type(self).__name__, 'element %s renamed to %s and %s' %(key, newName, name), MergeError.RENAME)
 

Modified: branches/0.7alpha3/chandler/repository/persistence/RepositoryError.py (11274 => 11275)

--- branches/0.7alpha3/chandler/repository/persistence/RepositoryError.py	2006-07-28 13:38:16 UTC (rev 11274)
+++ branches/0.7alpha3/chandler/repository/persistence/RepositoryError.py	2006-07-28 15:02:20 UTC (rev 11275)
@@ -103,6 +103,7 @@
     REF    = 5
     KIND   = 6
     CHANGE = 7
+    DELETE = 8
     
     codeNames = { BUG: 'BUG',
                   RENAME: 'RENAME',
@@ -111,7 +112,8 @@
                   VALUE: 'VALUE',
                   REF: 'REF',
                   KIND: 'KIND',
-                  CHANGE: 'CHANGE' }
+                  CHANGE: 'CHANGE',
+                  DELETE: 'DELETE' }
 
 
 class LoadError(RepositoryError):

Modified: branches/0.7alpha3/chandler/repository/tests/TestMerge.py (11274 => 11275)

--- branches/0.7alpha3/chandler/repository/tests/TestMerge.py	2006-07-28 13:38:16 UTC (rev 11274)
+++ branches/0.7alpha3/chandler/repository/tests/TestMerge.py	2006-07-28 15:02:20 UTC (rev 11275)
@@ -977,7 +977,48 @@
         main.commit()
         self.assert_(main.check(), 'main view did not check out')
 
+    def testMergeChangeDelete(self):
 
+        def mergeFn(code, item, attribute, newValue):
+            if code == MergeError.DELETE:
+                return True
+            return newValue
+
+        main = self.rep.view
+        cineguidePack = os.path.join(self.testdir, 'data', 'packs',
+                                     'cineguide.pack')
+        main.loadPack(cineguidePack)
+        main.commit()
+
+        view = self.rep.createView('view')
+        main = self.rep.setCurrentView(view)
+
+        k = view.findPath('//CineGuide/KHepburn')
+        c = k.itsParent
+        m1 = k.movies.first()
+        m1.delete()
+        view.commit()
+        
+        view = self.rep.setCurrentView(main)
+        main.deferDelete()
+        k = main.findPath('//CineGuide/KHepburn')
+        m1 = k.movies.first()
+        m1.title = 'Foo'
+        m1.director = k.movies.next(m1).director
+
+        try:
+            main.commit(None)
+        except MergeError, e:
+            self.assert_(e.getReasonCode() == MergeError.DELETE)
+        else:
+            if not m1.isDeleted():
+                self.assert_(False, "MergeError not caught")
+
+        main.commit(mergeFn)
+        self.assert_(m1.isDeleted())
+        self.assert_(main.check(), 'main view did not check out')
+
+
 if __name__ == "__main__":
 #    import hotshot
 #    profiler = hotshot.Profile('/tmp/TestItems.hotshot')




_______________________________________________
Commits mailing list
[email protected]
http://lists.osafoundation.org/mailman/listinfo/commits

Reply via email to