[Checkins] SVN: z3c.vcsync/trunk/src/z3c/vcsync/ Split off conflict checking into its own file.

Martijn Faassen faassen at infrae.com
Mon Apr 28 14:16:20 EDT 2008


Log message for revision 85828:
  Split off conflict checking into its own file.
  

Changed:
  U   z3c.vcsync/trunk/src/z3c/vcsync/README.txt
  A   z3c.vcsync/trunk/src/z3c/vcsync/conflict.txt
  U   z3c.vcsync/trunk/src/z3c/vcsync/tests.py

-=-
Modified: z3c.vcsync/trunk/src/z3c/vcsync/README.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-04-28 18:14:03 UTC (rev 85827)
+++ z3c.vcsync/trunk/src/z3c/vcsync/README.txt	2008-04-28 18:16:20 UTC (rev 85828)
@@ -268,7 +268,6 @@
 
 We have a second, empty tree that we will load objects into::
 
-  >>> last_revision_nr = 0
   >>> data2 = Container()
   >>> data2.__name__ = 'root'
   >>> state2 = TestState(data2)
@@ -317,134 +316,5 @@
   >>> data['sub']['qux'].payload
   30
 
-Conflicts
----------
 
-Let's now generate a conflict: we change the same object in both trees
-at the same time::
 
-  >>> data['bar'].payload = 200
-  >>> current_synchronizer = s2
-  >>> data2['bar'].payload = 250
-
-Let's synchronize the second tree first. This won't generate a
-conflict yet by itself, but sets up for it::
-
-  >>> info = s2.sync("synchronize")
-
-Now we'll synchronize the first tree. This will generate a conflict, as
-we saved a different value from the second tree::
-
-  >>> current_synchronizer = s
-  >>> info = s.sync("synchronize")
-
-The conflict will have been resolved in favor of the first tree, as
-this synchronized last::
-
-  >>> data['bar'].payload
-  200
-
-When we synchronize from the second tree again, we will see the
-resolved value appear as well::
-
-  >>> current_synchronizer = s2
-  >>> info = s2.sync("synchronize")
-  >>> data2['bar'].payload
-  200
-
-The other version of the conflicting object is not gone. It is stored
-under a special ``found`` directory. We'll synchronize this as well::
-
-  >>> found_data = Container()
-  >>> found_data.__name__ = 'found'
-  >>> found_state = TestState(found_data)
-  >>> found_s = Synchronizer(checkout, found_state)
-  >>> current_synchronizer = found_s
-  >>> info = found_s.sync("synchronize")
-
-We see the conflicting value that was stored by the second tree in
-here::
-
-  >>> found_data['root']['bar'].payload
-  250
-
-Conflicts in subdirectories should also be resolved properly::
-
-  >>> current_synchronizer = s
-  >>> data['sub']['qux'].payload = 35 
-  >>> current_synchronizer = s2
-  >>> data2['sub']['qux'].payload = 36
-  >>> info = s2.sync("Synchronize")
-  >>> current_synchronizer = s
-  >>> info = s.sync("Synchronize")
-  >>> data['sub']['qux'].payload
-  35
-  >>> current_synchronizer = s2
-  >>> info = s2.sync("Synchronize")
-  >>> data2['sub']['qux'].payload
-  35
-
-The found version in this case will reside in the same subdirectory,
-``sub``::
-
-  >>> current_synchronizer = found_s
-  >>> info = found_s.sync("Synchronize")
-  >>> found_data['root']['sub']['qux'].payload
-  36
-
-Let's recreate this conflict again.
-
-Folder conflicts
-----------------
-
-Let's now examine a case of a conflict in case of containers.
-
-A user (we'll call him ``user1``) creates a new container in ``data``
-with some content in it, and synchronize it::
-
-  >>> current_synchronizer = s
-  >>> data['folder'] = Container()
-  >>> data['folder']['content'] = Item(14)
-  >>> info = s.sync("Synchronize")
-
-We'll synchronize this into ``data2`` so that the second user (``user2``) has
-access to it::
-
-  >>> current_synchronizer = s2
-  >>> info = s2.sync("Synchronize")
-
-``user1`` now throws away ``folder`` in ``data`` and synchronizes this,
-causing ``folder`` to be gone in SVN::
-
-  >>> current_synchronizer = s
-  >>> from z3c.vcsync.vc import get_object_path
-  >>> state._removed.append(get_object_path(data, data['folder']))
-  >>> del data['folder']
-  >>> info = s.sync("Synchronize")
-
-Meanwhile, ``user2`` happily alters data in ``folder`` by changing
-``content`` in instance 2::
-
-  >>> current_synchronizer = s2
-  >>> data2['folder']['content'].payload = 15
-
-Now ``user2`` does a synchronization too::
-
-  >>> info = s2.sync("Synchronize")
-
-All changes ``user2`` made are now gone, as ``folder`` is gone::
-
-  >>> 'folder' in data2
-  False
-
-The folder with its content can however be retrieved in the found data
-section::
-
-  >>> found_s = Synchronizer(checkout, found_state)
-  >>> current_synchronizer = found_s
-  >>> info = found_s.sync("synchronize")
-  >>> found_data['root']['folder']['content'].payload
-  15
-
-
-

Added: z3c.vcsync/trunk/src/z3c/vcsync/conflict.txt
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/conflict.txt	                        (rev 0)
+++ z3c.vcsync/trunk/src/z3c/vcsync/conflict.txt	2008-04-28 18:16:20 UTC (rev 85828)
@@ -0,0 +1,228 @@
+Conflicts
+=========
+
+When one user (user1) edits a piece of content, synchronizes, and
+another user (user2) edits this content too, synchronization can
+result in conflicts.
+
+Conflicts are resolved by letting the last synchronization "win" and
+by backing up the other version to a special "found" directory from
+which it can be recovered.
+
+Another form of conflict involves directories: user1 removes a
+container, synchronizes while user2 edits something in it. This is
+resolved by removing this container, but moving its contents to the
+"found" directory as well.
+
+Set up
+------
+
+Before we set up items, we need to tell it how to get the revision number
+of the last change. For now, this is just ``0``::
+
+  >>> from z3c.vcsync.tests import Container, Item
+  >>> def get_revision_nr(self):
+  ...     return 0
+  >>> Item.get_revision_nr = get_revision_nr
+
+Let's set up a simple tree::
+
+  >>> data1 = Container()
+  >>> data1.__name__ = 'root'
+  >>> data1['bar'] = Item(payload=1)
+  >>> data1['sub'] = Container()
+  >>> data1['sub']['qux'] = Item(payload=3)
+
+To create conflicts, we need to represent the state twice (once for
+each user). Let's represent this tree in ``state1`` first::
+
+  >>> from z3c.vcsync.tests import TestState
+  >>> state1 = TestState(data1)
+
+Let's make sure we can save and load the objects by grokking the
+right serializers, parser and factories::
+
+  >>> import grok  
+  >>> from z3c.vcsync.tests import (ItemSerializer, ItemParser, ItemFactory, 
+  ...    ContainerParser, ContainerFactory)
+  >>> grok.testing.grok('z3c.vcsync')
+  >>> grok.testing.grok_component('ItemSerializer', ItemSerializer)
+  True
+  >>> grok.testing.grok_component('ItemParser', ItemParser)
+  True
+  >>> grok.testing.grok_component('ItemFactory', ItemFactory)
+  True
+  >>> grok.testing.grok_component('ContainerParser', ContainerParser)
+  True
+  >>> grok.testing.grok_component('ContainerFactory', ContainerFactory)
+  True
+
+Let's set up an SVN repository and initial checkout::
+
+  >>> from z3c.vcsync.svn import SvnCheckout
+  >>> from z3c.vcsync.tests import svn_repo_wc
+  >>> repo, wc1 = svn_repo_wc()
+  >>> checkout1 = SvnCheckout(wc1)
+
+And a synchronizer::
+
+  >>> from z3c.vcsync.vc import Synchronizer
+  >>> s1 = Synchronizer(checkout1, state1)
+  >>> current_synchronizer = s1
+
+We can now return the correct revision nr::
+
+  >>> def get_revision_nr(self):
+  ...   return current_synchronizer.state.get_revision_nr()
+  >>> Item.get_revision_nr = get_revision_nr
+
+Let's synchronize the state to the server::
+
+  >>> info = s1.sync("Synchronize")
+
+And synchronize it back into another tree::
+
+  >>> data2 = Container()
+  >>> data2.__name__ = 'root'
+  >>> state2 = TestState(data2)
+
+  >>> import py
+  >>> wc2 = py.test.ensuretemp('wc2')
+  >>> wc2 = py.path.svnwc(wc2)
+  >>> wc2.checkout(repo)
+  >>> checkout2 = SvnCheckout(wc2)
+  >>> s2 = Synchronizer(checkout2, state2)
+  >>> current_synchronizer = s2
+  >>> info = s2.sync("Synchronize")
+
+File conflicts
+--------------
+
+Let's now generate a conflict: we change the same object in both trees
+at the same time::
+
+  >>> current_synchronizer = s1
+  >>> data1['bar'].payload = 200
+  >>> current_synchronizer = s2
+  >>> data2['bar'].payload = 250
+
+Let's synchronize the second tree first. This won't generate a
+conflict yet by itself, but sets up for it::
+
+  >>> info = s2.sync("synchronize")
+
+Now we'll synchronize the first tree. This will generate a conflict, as
+we saved a different value from the second tree::
+
+  >>> current_synchronizer = s1
+  >>> info = s1.sync("synchronize")
+
+The conflict will have been resolved in favor of the first tree, as
+this synchronized last::
+
+  >>> data1['bar'].payload
+  200
+
+When we synchronize from the second tree again, we will see the
+resolved value appear as well::
+
+  >>> current_synchronizer = s2
+  >>> info = s2.sync("synchronize")
+  >>> data2['bar'].payload
+  200
+
+The other version of the conflicting object is not gone. It is stored
+under a special ``found`` directory. We'll synchronize this as well::
+
+  >>> found_data = Container()
+  >>> found_data.__name__ = 'found'
+  >>> found_state = TestState(found_data)
+  >>> found_s = Synchronizer(checkout1, found_state)
+  >>> current_synchronizer = found_s
+  >>> info = found_s.sync("synchronize")
+
+We see the conflicting value that was stored by the second tree in
+here::
+
+  >>> found_data['root']['bar'].payload
+  250
+
+Conflicts in subdirectories should also be resolved properly::
+
+  >>> current_synchronizer = s1
+  >>> data1['sub']['qux'].payload = 35 
+  >>> current_synchronizer = s2
+  >>> data2['sub']['qux'].payload = 36
+  >>> info = s2.sync("Synchronize")
+  >>> current_synchronizer = s1
+  >>> info = s1.sync("Synchronize")
+  >>> data1['sub']['qux'].payload
+  35
+  >>> current_synchronizer = s2
+  >>> info = s2.sync("Synchronize")
+  >>> data2['sub']['qux'].payload
+  35
+
+The found version in this case will reside in the same subdirectory,
+``sub``::
+
+  >>> current_synchronizer = found_s
+  >>> info = found_s.sync("Synchronize")
+  >>> found_data['root']['sub']['qux'].payload
+  36
+
+Let's recreate this conflict again.
+
+Folder conflicts
+----------------
+
+Let's now examine a case of a conflict in case of containers.
+
+A user (we'll call him ``user1``) creates a new container in ``data``
+with some content in it, and synchronize it::
+
+  >>> current_synchronizer = s1
+  >>> data1['folder'] = Container()
+  >>> data1['folder']['content'] = Item(14)
+  >>> info = s1.sync("Synchronize")
+
+We'll synchronize this into ``data2`` so that the second user (``user2``) has
+access to it::
+
+  >>> current_synchronizer = s2
+  >>> info = s2.sync("Synchronize")
+
+``user1`` now throws away ``folder`` in ``data`` and synchronizes this,
+causing ``folder`` to be gone in SVN::
+
+  >>> current_synchronizer = s1
+  >>> from z3c.vcsync.vc import get_object_path
+  >>> state1._removed.append(get_object_path(data1, data1['folder']))
+  >>> del data1['folder']
+  >>> info = s1.sync("Synchronize")
+
+Meanwhile, ``user2`` happily alters data in ``folder`` by changing
+``content`` in instance 2::
+
+  >>> current_synchronizer = s2
+  >>> data2['folder']['content'].payload = 15
+
+Now ``user2`` does a synchronization too::
+
+  >>> info = s2.sync("Synchronize")
+
+All changes ``user2`` made are now gone, as ``folder`` is gone::
+
+  >>> 'folder' in data2
+  False
+
+The folder with its content can however be retrieved in the found data
+section::
+
+  >>> found_s = Synchronizer(checkout1, found_state)
+  >>> current_synchronizer = found_s
+  >>> info = found_s.sync("synchronize")
+  >>> found_data['root']['folder']['content'].payload
+  15
+
+

Modified: z3c.vcsync/trunk/src/z3c/vcsync/tests.py
===================================================================
--- z3c.vcsync/trunk/src/z3c/vcsync/tests.py	2008-04-28 18:14:03 UTC (rev 85827)
+++ z3c.vcsync/trunk/src/z3c/vcsync/tests.py	2008-04-28 18:16:20 UTC (rev 85828)
@@ -224,6 +224,14 @@
         tearDown=cleanUpZope,
         globs=globs,
         optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE),
+
+        doctest.DocFileSuite(
+        'conflict.txt',
+        setUp=setUpZope,
+        tearDown=cleanUpZope,
+        globs=globs,
+        optionflags=doctest.ELLIPSIS + doctest.NORMALIZE_WHITESPACE),
+
         ])
     return suite
 



More information about the Checkins mailing list