Rev 3527: Add the ability to prune extra tails from the parent_map. in http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/weave_merge

John Arbash Meinel john at arbash-meinel.com
Sun Jul 13 01:30:55 BST 2008


At http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/weave_merge

------------------------------------------------------------
revno: 3527
revision-id: john at arbash-meinel.com-20080713003042-ak1b725ck5vmpbmi
parent: john at arbash-meinel.com-20080712180630-5hc93d3s2vff9olo
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: weave_merge
timestamp: Sat 2008-07-12 19:30:42 -0500
message:
  Add the ability to prune extra tails from the parent_map.
  
  Now we can get the right answer without having to add extra history.
-------------- next part --------------
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2008-07-12 18:06:30 +0000
+++ b/bzrlib/merge.py	2008-07-13 00:30:42 +0000
@@ -1493,6 +1493,9 @@
             parent_map[base_key] = ()
         culled_parent_map, child_map, tails = self._remove_external_references(
             parent_map)
+        # Remove all the tails but base_key
+        tails.remove(base_key)
+        self._prune_tails(culled_parent_map, child_map, tails)
         return culled_parent_map
 
     @staticmethod
@@ -1524,8 +1527,30 @@
             filtered_parent_map[key] = culled_parent_keys
         return filtered_parent_map, child_map, tails
 
-    def _prune_tails(self, parent_map, tails_to_remove, child_map):
-        """Remove tails from the parent map."""
+    @staticmethod
+    def _prune_tails(parent_map, child_map, tails_to_remove):
+        """Remove tails from the parent map.
+        
+        This will remove the supplied revisions until no more children have 0
+        parents.
+
+        :param parent_map: A dict of {child: [parents]}, this dictionary will
+            be modified in place.
+        :param tails_to_remove: A list of tips that should be removed,
+            this list will be consumed
+        :param child_map: The reverse dict of parent_map ({parent: [children]})
+            this dict will be modified
+        :return: None, parent_map will be modified in place.
+        """
+        while tails_to_remove:
+            next = tails_to_remove.pop()
+            parent_map.pop(next)
+            children = child_map.pop(next)
+            for child in children:
+                child_parents = parent_map[child]
+                child_parents.remove(next)
+                if len(child_parents) == 0:
+                    tails_to_remove.append(child)
 
     def _get_interesting_texts(self, parent_map):
         """Return a dict of texts we are interested in.

=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py	2008-07-12 18:06:30 +0000
+++ b/bzrlib/tests/test_merge.py	2008-07-13 00:30:42 +0000
@@ -632,20 +632,12 @@
         # Merge G & H but supersede an old line in B
         self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
         plan = self.plan_merge_vf.plan_merge('I', 'J')
-        # This has a slight incorrect value, as it considers the lines in A to
-        # be 'killed-base', because they show up as new in both B and C, and
-        # then E has to resolve which ones are the 'real' ones.
-        # However, I believe this is okay as the important lines will all
-        # derive from E.
         self.assertEqual([
                           ('unchanged', 'D\n'),
                           ('unchanged', 'a\n'),
                           ('killed-b', 'B\n'),
                           ('new-b', 'J\n'),
                           ('unchanged', 'b\n'),
-                          ('killed-base', 'c\n'), # Not technically correct
-                          ('killed-base', 'a\n'), # as they came from A
-                          ('killed-base', 'b\n'), # but the final text is right
                           ('unchanged', 'C\n'),
                           ('unchanged', 'c\n'),
                           ('unchanged', 'F\n')],
@@ -812,6 +804,38 @@
             [3, 4],
             {1: [3], 2: [3, 4], 3: [5], 4: []})
 
+    def assertPruneTails(self, pruned_map, tails, parent_map):
+        child_map = {}
+        for key, parent_keys in parent_map.iteritems():
+            child_map.setdefault(key, [])
+            for pkey in parent_keys:
+                child_map.setdefault(pkey, []).append(key)
+        _PlanMerge._prune_tails(parent_map, child_map, tails)
+        self.assertEqual(pruned_map, parent_map)
+
+    def test__prune_tails(self):
+        # Nothing requested to prune
+        self.assertPruneTails({1: [], 2: [], 3: []}, [],
+                              {1: [], 2: [], 3: []})
+        # Prune a single entry
+        self.assertPruneTails({1: [], 3: []}, [2],
+                              {1: [], 2: [], 3: []})
+        # Prune a chain
+        self.assertPruneTails({1: []}, [3],
+                              {1: [], 2: [3], 3: []})
+        # Prune a chain with a diamond
+        self.assertPruneTails({1: []}, [5],
+                              {1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
+        # Prune a partial chain
+        self.assertPruneTails({1: [6], 6:[]}, [5],
+                              {1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
+                               6: []})
+        # Prune a chain with multiple tips, that pulls out intermediates
+        self.assertPruneTails({1:[3], 3:[]}, [4, 5],
+                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
+        self.assertPruneTails({1:[3], 3:[]}, [5, 4],
+                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
+
     def test_subtract_plans(self):
         old_plan = [
         ('unchanged', 'a\n'),



More information about the bazaar-commits mailing list