Rev 3518: Hack in a Weave merger that includes all possibly relevant texts. in http://bzr.arbash-meinel.com/branches/bzr/1.6-dev/merge3_per_file

John Arbash Meinel john at arbash-meinel.com
Wed Jun 25 23:41:51 BST 2008


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

------------------------------------------------------------
revno: 3518
revision-id: john at arbash-meinel.com-20080625224143-ndnv3fygtime27au
parent: john at arbash-meinel.com-20080625174816-r8mgsvobyaruyj7w
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: merge3_per_file
timestamp: Wed 2008-06-25 17:41:43 -0500
message:
  Hack in a Weave merger that includes all possibly relevant texts.
  
  It seems it fixes this problem, so we may need to re-think how we are doing the partial merges.
-------------- next part --------------
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2008-06-25 17:48:16 +0000
+++ b/bzrlib/merge.py	2008-06-25 22:41:43 +0000
@@ -27,6 +27,7 @@
     patiencediff,
     registry,
     revision as _mod_revision,
+    tsort,
     )
 from bzrlib.branch import Branch
 from bzrlib.conflicts import ConflictList, Conflict
@@ -822,6 +823,13 @@
         """
         base_first = bases[0]
         for base in bases[1:]:
+            # XXX: This is not technically correct. A value does not always
+            #      supersede None. Sometimes you get None because someone
+            #      deleted an object. The correct resolution would probably be
+            #      to recurse back into the LCA of these bases until we get
+            #      some sort of convergance. (Eventually you always reach NULL
+            #      where everything is None). That would let us know if this
+            #      None is a "didn't exist yet" or a "deleted".
             if base is not None and base_first != base:
                 break
         else: # All the bases are identical, we can just use 3-way
@@ -1198,6 +1206,7 @@
     supports_show_base = False
     supports_reverse_cherrypick = False
     history_based = True
+    full_texts_merge = False
 
     def _merged_lines(self, file_id):
         """Generate the merged lines.
@@ -1209,7 +1218,8 @@
         else:
             base = None
         plan = self.this_tree.plan_file_merge(file_id, self.other_tree,
-                                              base=base)
+                                              base=base,
+                                              full_texts_merge=self.full_texts_merge)
         if 'merge' in debug.debug_flags:
             plan = list(plan)
             trans_id = self.tt.trans_id_file_id(file_id)
@@ -1240,6 +1250,10 @@
             file_group.append(trans_id)
 
 
+class FullWeaveMerger(WeaveMerger):
+    full_texts_merge = True
+
+
 class LCAMerger(WeaveMerger):
 
     def _merged_lines(self, file_id):
@@ -1522,9 +1536,10 @@
 class _PlanMerge(_PlanMergeBase):
     """Plan an annotate merge using on-the-fly annotation"""
 
-    def __init__(self, a_rev, b_rev, vf):
+    def __init__(self, a_rev, b_rev, vf, full_texts_merge=False):
         # We don't call _PlanMergeBase, because we don't use most of what it
         # does
+        self.full_texts_merge = full_texts_merge
         self.a_rev = a_rev
         self.b_rev = b_rev
         self.vf = vf
@@ -1549,26 +1564,41 @@
         """Find all the ancestors back to a unique lca"""
         cur_ancestors = (self.a_rev, self.b_rev)
         mutter('finding lcas for %s:\n%s', self.vf, cur_ancestors)
-        ancestors = [cur_ancestors]
+        ancestors = set(cur_ancestors)
+        parent_map = {}
         while True:
             next_lcas = self.graph.find_lca(*cur_ancestors)
-            ancestors.append(next_lcas)
+            ancestors.update(next_lcas)
+            for rev_id in cur_ancestors:
+                parent_map[rev_id] = next_lcas
             if len(next_lcas) == 0: 
-                ancestors[-1] = (NULL_REVISION,)
+                ancestors.add(NULL_REVISION)
+                parent_map[NULL_REVISION] = ()
                 self.vf.add_lines(NULL_REVISION, [], [])
                 break
             elif len(next_lcas) == 1:
+                parent_map[next_lcas.pop()] = ()
                 break
             cur_ancestors = next_lcas
-        ancestors.reverse()
-        return ancestors
-
-    def _get_interesting_texts(self, lcas):
-        all_revision_ids = set()
-        for ancestors in lcas:
-            all_revision_ids.update(ancestors)
-        all_revision_ids.add(self.a_rev)
-        all_revision_ids.add(self.b_rev)
+        return parent_map
+
+    def _find_all_ancestors(self):
+        unique_lca = self.graph.find_unique_lca(self.a_rev, self.b_rev)
+        interesting = self.graph.find_unique_ancestors(self.a_rev, [unique_lca])
+        interesting.update(self.graph.find_unique_ancestors(self.b_rev,
+                                                            [unique_lca]))
+        parent_map = self.graph.get_parent_map(interesting)
+        interesting.add(unique_lca)
+        parent_map[unique_lca] = ()
+        culled_parent_map = {}
+        for rev_id, parent_ids in parent_map.iteritems():
+            real_parent_ids = [p_id for p_id in parent_ids
+                                     if p_id in parent_map]
+            culled_parent_map[rev_id] = real_parent_ids
+        return culled_parent_map
+
+    def _get_interesting_texts(self, parent_map):
+        all_revision_ids = set(parent_map)
 
         all_texts = dict(zip(all_revision_ids,
                              self.vf.get_line_list(all_revision_ids)))
@@ -1578,24 +1608,19 @@
         from bzrlib import weave
         self._weave = weave.Weave(weave_name='in_memory_weave',
                                   allow_reserved=True)
-        lcas = self._find_recursive_lcas()
-        mutter('Found:\n     => %s',
-               '\n     => '.join(str(sorted(lca)) for lca in lcas))
-
-        all_texts = self._get_interesting_texts(lcas)
-
-        last_parents = ()
-        for ancestors in lcas:
-            for ancestor in ancestors:
-                if self._weave.has_version(ancestor):
-                    # Most likely this happened because one node purely
-                    # dominated another in the per-file graph. That is okay, we
-                    # already have it in the weave, and the plan should be very
-                    # straightforward.
-                    continue
-                self._weave.add_lines(ancestor, last_parents,
-                                      all_texts[ancestor])
-            last_parents = ancestors
+        if self.full_texts_merge:
+            parent_map = self._find_all_ancestors()
+        else:
+            parent_map = self._find_recursive_lcas()
+        mutter('Found:\n%s', parent_map)
+
+        all_texts = self._get_interesting_texts(parent_map)
+
+
+        for revision_id in tsort.topo_sort(parent_map):
+            parent_ids = parent_map[revision_id]
+            self._weave.add_lines(revision_id, parent_ids,
+                                  all_texts[revision_id])
 
     def plan_merge(self):
         """Generate a 'plan' for merging the two revisions.

=== modified file 'bzrlib/option.py'
--- a/bzrlib/option.py	2008-04-24 07:22:53 +0000
+++ b/bzrlib/option.py	2008-06-25 22:41:43 +0000
@@ -474,6 +474,8 @@
                                    "Merge using external diff3")
 _merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
                                    "Weave-based merge")
+_merge_type_registry.register_lazy('full-weave', 'bzrlib.merge', 'FullWeaveMerger',
+                                   "Weave-based merge with all texts")
 _merge_type_registry.register_lazy('lca', 'bzrlib.merge', 'LCAMerger',
                                    "LCA-newness merge")
 

=== modified file 'bzrlib/tree.py'
--- a/bzrlib/tree.py	2008-06-20 20:07:26 +0000
+++ b/bzrlib/tree.py	2008-06-25 22:41:43 +0000
@@ -331,7 +331,8 @@
             last_revision_base = base._get_file_revision(file_id, vf, 'base:')
         return vf, last_revision_a, last_revision_b, last_revision_base
 
-    def plan_file_merge(self, file_id, other, base=None):
+    def plan_file_merge(self, file_id, other, base=None,
+                        full_texts_merge=False):
         """Generate a merge plan based on annotations.
 
         If the file contains uncommitted changes in this tree, they will be
@@ -342,7 +343,8 @@
         data = self._get_plan_merge_data(file_id, other, base)
         vf, last_revision_a, last_revision_b, last_revision_base = data
         return vf.plan_merge(last_revision_a, last_revision_b,
-                             last_revision_base)
+                             last_revision_base,
+                             full_texts_merge=full_texts_merge)
 
     def plan_file_lca_merge(self, file_id, other, base=None):
         """Generate a merge plan based lca-newness.

=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py	2008-06-21 01:21:08 +0000
+++ b/bzrlib/versionedfile.py	2008-06-25 22:41:43 +0000
@@ -543,11 +543,11 @@
     def __repr__(self):
         return '%s(%s)' % (self.__class__.__name__, self._file_id)
 
-    def plan_merge(self, ver_a, ver_b, base=None):
+    def plan_merge(self, ver_a, ver_b, base=None, full_texts_merge=False):
         """See VersionedFile.plan_merge"""
         from bzrlib.merge import _PlanMerge
         if base is None:
-            return _PlanMerge(ver_a, ver_b, self).plan_merge()
+            return _PlanMerge(ver_a, ver_b, self, full_texts_merge).plan_merge()
         old_plan = list(_PlanMerge(ver_a, base, self).plan_merge())
         new_plan = list(_PlanMerge(ver_a, ver_b, self).plan_merge())
         return _PlanMerge._subtract_plans(old_plan, new_plan)



More information about the bazaar-commits mailing list