Rev 4710: Start working on the ability to drop a .BASE file for --weave and --lca merge. in http://bazaar.launchpad.net/~jameinel/bzr/2.0-40412-show-base-weave

John Arbash Meinel john at arbash-meinel.com
Fri Dec 4 00:59:24 GMT 2009


At http://bazaar.launchpad.net/~jameinel/bzr/2.0-40412-show-base-weave

------------------------------------------------------------
revno: 4710
revision-id: john at arbash-meinel.com-20091204005909-r5htm5irxu7n44di
parent: john at arbash-meinel.com-20091204003251-67jf15ez18n9b3th
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.0-40412-show-base-weave
timestamp: Thu 2009-12-03 18:59:09 -0600
message:
  Start working on the ability to drop a .BASE file for --weave and --lca merge.
  
  The main issue at this point is what to do about LCA's 'conflicted' states.
-------------- next part --------------
=== modified file 'bzrlib/merge.py'
--- a/bzrlib/merge.py	2009-12-04 00:32:51 +0000
+++ b/bzrlib/merge.py	2009-12-04 00:59:09 +0000
@@ -1416,17 +1416,20 @@
     supports_reverse_cherrypick = False
     history_based = True
 
-    def _merged_lines(self, file_id):
-        """Generate the merged lines.
-        There is no distinction between lines that are meant to contain <<<<<<<
-        and conflicts.
-        """
-        if self.cherrypick:
-            base = self.base_tree
-        else:
-            base = None
-        plan = self.this_tree.plan_file_merge(file_id, self.other_tree,
+    def _generate_merge_plan(self, file_id, base):
+        return self.this_tree.plan_file_merge(file_id, self.other_tree,
                                               base=base)
+
+    def _merged_lines(self, file_id):
+        """Generate the merged lines.
+        There is no distinction between lines that are meant to contain <<<<<<<
+        and conflicts.
+        """
+        if self.cherrypick:
+            base = self.base_tree
+        else:
+            base = None
+        plan = self._generate_merge_plan(file_id, base)
         if 'merge' in debug.debug_flags:
             plan = list(plan)
             trans_id = self.tt.trans_id_file_id(file_id)
@@ -1435,51 +1438,41 @@
             self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
         textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
             '>>>>>>> MERGE-SOURCE\n')
-        return textmerge.merge_lines(self.reprocess)
+        lines, conflicts = textmerge.merge_lines(self.reprocess)
+        if conflicts:
+            base_lines = textmerge.base_from_plan()
+        else:
+            base_lines = None
+        return lines, base_lines
 
     def text_merge(self, file_id, trans_id):
         """Perform a (weave) text merge for a given file and file-id.
         If conflicts are encountered, .THIS and .OTHER files will be emitted,
         and a conflict will be noted.
         """
-        lines, conflicts = self._merged_lines(file_id)
+        lines, base_lines = self._merged_lines(file_id)
         lines = list(lines)
         # Note we're checking whether the OUTPUT is binary in this case,
         # because we don't want to get into weave merge guts.
         check_text_lines(lines)
         self.tt.create_file(lines, trans_id)
-        if conflicts:
+        if base_lines is not None:
+            # Conflict
             self._raw_conflicts.append(('text conflict', trans_id))
             name = self.tt.final_name(trans_id)
             parent_id = self.tt.final_parent(trans_id)
             file_group = self._dump_conflicts(name, parent_id, file_id,
-                                              no_base=True)
+                                              no_base=False,
+                                              base_lines=base_lines)
             file_group.append(trans_id)
 
 
 class LCAMerger(WeaveMerger):
 
-    def _merged_lines(self, file_id):
-        """Generate the merged lines.
-        There is no distinction between lines that are meant to contain <<<<<<<
-        and conflicts.
-        """
-        if self.cherrypick:
-            base = self.base_tree
-        else:
-            base = None
-        plan = self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
+    def _generate_merge_plan(self, file_id, base):
+        return self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
                                                   base=base)
-        if 'merge' in debug.debug_flags:
-            plan = list(plan)
-            trans_id = self.tt.trans_id_file_id(file_id)
-            name = self.tt.final_name(trans_id) + '.plan'
-            contents = ('%10s|%s' % l for l in plan)
-            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
-        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
-            '>>>>>>> MERGE-SOURCE\n')
-        return textmerge.merge_lines(self.reprocess)
-
+        
 
 class Diff3Merger(Merge3Merger):
     """Three-way merger using external diff3 for text merging"""

=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py	2009-08-17 22:08:21 +0000
+++ b/bzrlib/versionedfile.py	2009-12-04 00:59:09 +0000
@@ -1419,7 +1419,7 @@
     def __init__(self, plan, a_marker=TextMerge.A_MARKER,
                  b_marker=TextMerge.B_MARKER):
         TextMerge.__init__(self, a_marker, b_marker)
-        self.plan = plan
+        self.plan = list(plan)
 
     def _merge_struct(self):
         lines_a = []
@@ -1483,6 +1483,56 @@
         for struct in outstanding_struct():
             yield struct
 
+    def base_from_plan(self):
+        """Construct a BASE file from the plan text."""
+        base_lines = []
+        for state, line in self.plan:
+            # XXX: We need to figure out what to do for 'conflicted-a' and
+            #      'conflicted-b' lines. Here is a rough outline of the
+            #      options. Also, I tested this using the 'weave' failure.
+            #      Where you have a criss-cross merge, where both a & b
+            #      introduce a line in the same place. The merge conflicts, and
+            #      both include both lines, but put themselves first.
+            #           MN
+            #          /   \
+            #        MaN   MbN
+            #         |  X  |
+            #        MabN MbaN
+            #          \   /
+            #           ???
+            #      1) Include them in .BASE, as they are present in one LCA
+            #         (but not in all of them). In my test, that led to all
+            #         LCA's getting merged together into a big text, which
+            #         seems correct.
+            #         In testing, this gives BASE of MbabN, and the standard
+            #         3-way diff then looks like BASE => THIS is deleting the
+            #         first line 'b', and BASE => OTHER is deleting the second
+            #         line 'b'. Which means that diff3 of THIS BASE OTHER gives
+            #         MaN (no conflicts)
+            #      2) Exclude them in .BASE, because they aren't in all BASEs.
+            #         diff3 then sees 'b' being added by both sides before and
+            #         after 'a'. Which gives MbabN (no conflicts)
+            if state in ('killed-a', 'killed-b', 'killed-both', 'unchanged',
+                         'conflicted-a', 'conflicted-b'):
+                # If unchanged, then this line is straight from base. If a or b
+                # or both killed the line, then it *used* to be in base.
+                # If 'conflicted-a' or b, then it is new vs one base, but old
+                # versus another base. Which means it was present in *one* of
+                # the bases, so we'll include it.
+                base_lines.append(line)
+            else:
+                if state not in ('killed-base', 'irrelevant',
+                                 'ghost-a', 'ghost-b',
+                                 'new-a', 'new-b'):
+                    # killed-base, irrelevant means it doesn't apply
+                    # ghost-a/ghost-b are harder to say for sure, but they
+                    # aren't in the 'inc_c' which means they aren't in the
+                    # shared base of a & b. So we don't include them.
+                    # And obviously if the line is newly inserted, it isn't in
+                    # base
+                    raise AssertionError('Unknown state: %s' % (state,))
+        return base_lines
+
 
 class WeaveMerge(PlanWeaveMerge):
     """Weave merge that takes a VersionedFile and two versions as its input."""



More information about the bazaar-commits mailing list