Rev 3955: Fix an edge case with deleted files and criss-cross merges in http://bazaar.launchpad.net/%7Evila/bzr/bzr.integration

Vincent Ladeuil v.ladeuil+lp at free.fr
Fri Jan 23 07:43:08 GMT 2009


At http://bazaar.launchpad.net/%7Evila/bzr/bzr.integration

------------------------------------------------------------
revno: 3955
revision-id: v.ladeuil+lp at free.fr-20090123074235-dzu6zadxlbhwj7ww
parent: pqm at pqm.ubuntu.com-20090123042837-r1lyxrbk6nd5pp3g
parent: v.ladeuil+lp at free.fr-20090122133817-cerv5t33wgw2m5qj
committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
branch nick: bzr.integration
timestamp: Fri 2009-01-23 08:42:35 +0100
message:
  Fix an edge case with deleted files and criss-cross merges
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
  bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
    ------------------------------------------------------------
    revno: 3948.1.3
    revision-id: v.ladeuil+lp at free.fr-20090122133817-cerv5t33wgw2m5qj
    parent: v.ladeuil+lp at free.fr-20090121103302-tx6xsuogm6kga9jt
    committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
    branch nick: spurious-conflicts
    timestamp: Thu 2009-01-22 14:38:17 +0100
    message:
      Cleanup.
      
      * bzrlib/tests/test_merge.py:
      (TestMergerEntriesLCA.test_not_in_other_or_lca,
      TestMergerEntriesLCA.test_not_in_other_mod_in_lca1_not_in_lca2):
      Delete useless vars.
    modified:
      bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
    ------------------------------------------------------------
    revno: 3948.1.2
    revision-id: v.ladeuil+lp at free.fr-20090121103302-tx6xsuogm6kga9jt
    parent: john at arbash-meinel.com-20090120181350-wzl4x01ns3rly9y5
    committer: Vincent Ladeuil <v.ladeuil+lp at free.fr>
    branch nick: spurious-conflicts
    timestamp: Wed 2009-01-21 11:33:02 +0100
    message:
      * bzrlib/merge.py:
      (Merger.find_base): Add some mutter calls to help debug.
    modified:
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
    ------------------------------------------------------------
    revno: 3948.1.1
    revision-id: john at arbash-meinel.com-20090120181350-wzl4x01ns3rly9y5
    parent: pqm at pqm.ubuntu.com-20090120044335-pwr2rshr1yu6vzti
    committer: John Arbash Meinel <john at arbash-meinel.com>
    branch nick: 1.12-lca-deleted
    timestamp: Tue 2009-01-20 12:13:50 -0600
    message:
      Fix an edge case with deleted files and criss-cross merges.
      
      We wanted to be careful that if you had a criss-cross merge and both sides
      resolved the conflict in a different way, that you got a conflict.
      However, the original test was actually testing when one LCA dominated the
      other, which is a case where we shouldn't conflict.
      
      Test cases updated appropriately.
    modified:
      NEWS                           NEWS-20050323055033-4e00b5db738777ff
      bzrlib/merge.py                merge.py-20050513021216-953b65a438527106
      bzrlib/tests/test_merge.py     testmerge.py-20050905070950-c1b5aa49ff911024
-------------- next part --------------
=== modified file 'NEWS'
--- a/NEWS	2009-01-23 02:29:42 +0000
+++ b/NEWS	2009-01-23 07:42:35 +0000
@@ -12,6 +12,14 @@
     * Progress bars now show the rate of activity for some sftp 
       operations, and they are drawn different.  (Martin Pool, #172741)
 
+  BUG FIXES:
+
+    * There was a bug in how we handled resolving when a file is deleted
+      in one branch, and modified in the other. If there was a criss-cross
+      merge, we would cause the deletion to conflict a second time.
+      (Vincent Ladeuil, John Arbash Meinel)
+
+
 NOT RELEASED YET
 ----------------
 

=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2009-01-14 12:27:02 +0000
+++ b/bzrlib/merge.py	2009-01-21 10:33:02 +0000
@@ -390,6 +390,7 @@
             if self._is_criss_cross:
                 warning('Warning: criss-cross merge encountered.  See bzr'
                         ' help criss-cross.')
+                mutter('Criss-cross lcas: %r' % lcas)
                 interesting_revision_ids = [self.base_rev_id]
                 interesting_revision_ids.extend(lcas)
                 interesting_trees = dict((t.get_revision_id(), t)
@@ -405,6 +406,7 @@
                 self.base_tree = self.revision_tree(self.base_rev_id)
         self.base_is_ancestor = True
         self.base_is_other_ancestor = True
+        mutter('Base revid: %r' % self.base_rev_id)
 
     def set_base(self, base_revision):
         """Set the base revision to use for the merge.
@@ -794,16 +796,12 @@
             content_changed = True
             if kind_winner == 'this':
                 # No kind change in OTHER, see if there are *any* changes
-                if other_ie.kind == None:
-                    # No content and 'this' wins the kind, so skip this?
-                    # continue
-                    pass
-                elif other_ie.kind == 'directory':
+                if other_ie.kind == 'directory':
                     if parent_id_winner == 'this' and name_winner == 'this':
                         # No change for this directory in OTHER, skip
                         continue
                     content_changed = False
-                elif other_ie.kind == 'file':
+                elif other_ie.kind is None or other_ie.kind == 'file':
                     def get_sha1(ie, tree):
                         if ie.kind != 'file':
                             return None

=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py	2008-09-05 03:11:40 +0000
+++ b/bzrlib/tests/test_merge.py	2009-01-22 13:38:17 +0000
@@ -1410,7 +1410,12 @@
         #       B C  B nothing, C deletes foo
         #       |X|
         #       D E  D restores foo (same as B), E leaves it deleted
-        # We should emit an entry for this
+        # Analysis:
+        #   A => B, no changes
+        #   A => C, delete foo (C should supersede B)
+        #   C => D, restore foo
+        #   C => E, no changes
+        # D would then win 'cleanly' and no record would be given
         builder = self.get_builder()
         builder.build_snapshot('A-id', None,
             [('add', (u'', 'a-root-id', 'directory', None)),
@@ -1423,7 +1428,36 @@
         merge_obj = self.make_merge_obj(builder, 'E-id')
 
         entries = list(merge_obj._entries_lca())
-        root_id = 'a-root-id'
+        self.assertEqual([], entries)
+
+    def test_not_in_other_mod_in_lca1_not_in_lca2(self):
+        #       A    base, introduces 'foo'
+        #       |\
+        #       B C  B changes 'foo', C deletes foo
+        #       |X|
+        #       D E  D restores foo (same as B), E leaves it deleted (as C)
+        # Analysis:
+        #   A => B, modified foo
+        #   A => C, delete foo, C does not supersede B
+        #   B => D, no changes
+        #   C => D, resolve in favor of B
+        #   B => E, resolve in favor of E
+        #   C => E, no changes
+        # In this case, we have a conflict of how the changes were resolved. E
+        # picked C and D picked B, so we should issue a conflict
+        builder = self.get_builder()
+        builder.build_snapshot('A-id', None,
+            [('add', (u'', 'a-root-id', 'directory', None)),
+             ('add', (u'foo', 'foo-id', 'file', 'content\n'))])
+        builder.build_snapshot('B-id', ['A-id'], [
+            ('modify', ('foo-id', 'new-content\n'))])
+        builder.build_snapshot('C-id', ['A-id'],
+            [('unversion', 'foo-id')])
+        builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
+        builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
+        merge_obj = self.make_merge_obj(builder, 'E-id')
+
+        entries = list(merge_obj._entries_lca())
         self.assertEqual([('foo-id', True,
                            ((root_id, [root_id, None]), None, root_id),
                            ((u'foo', [u'foo', None]), None, 'foo'),
@@ -1431,6 +1465,21 @@
                          ], entries)
 
     def test_only_in_one_lca(self):
+        #   A   add only root
+        #   |\
+        #   B C B nothing, C add file
+        #   |X|
+        #   D E D still has nothing, E removes file
+        # Analysis:
+        #   B => D, no change
+        #   C => D, removed the file
+        #   B => E, no change
+        #   C => E, removed the file
+        # Thus D & E have identical changes, and this is a no-op
+        # Alternatively:
+        #   A => B, no change
+        #   A => C, add file, thus C supersedes B
+        #   w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
         builder = self.get_builder()
         builder.build_snapshot('A-id', None,
             [('add', (u'', 'a-root-id', 'directory', None))])
@@ -1444,11 +1493,7 @@
 
         entries = list(merge_obj._entries_lca())
         root_id = 'a-root-id'
-        self.assertEqual([('a-id', True,
-                           ((None, [None, root_id]), None, None),
-                           ((None, [None, u'a']), None, None),
-                           ((None, [None, False]), None, None)),
-                         ], entries)
+        self.assertEqual([], entries)
 
     def test_only_in_other(self):
         builder = self.get_builder()



More information about the bazaar-commits mailing list