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