Rev 4711: Add the criss-cross flip-flop 'bug' for weave 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 01:56:41 GMT 2009
At http://bazaar.launchpad.net/~jameinel/bzr/2.0-40412-show-base-weave
------------------------------------------------------------
revno: 4711
revision-id: john at arbash-meinel.com-20091204015626-s3igiwbj5qiirwoa
parent: john at arbash-meinel.com-20091204005909-r5htm5irxu7n44di
committer: John Arbash Meinel <john at arbash-meinel.com>
branch nick: 2.0-40412-show-base-weave
timestamp: Thu 2009-12-03 19:56:26 -0600
message:
Add the criss-cross flip-flop 'bug' for weave merge.
This is the reason lca merge was created. However, also show where lca
merge fails when there is a more complex ancestry. Since it thinks that
a missing line is a source of flip-flop errors, when really it is just
a different ancestry.
-------------- next part --------------
=== modified file 'bzrlib/tests/test_merge.py'
--- a/bzrlib/tests/test_merge.py 2009-09-15 11:22:27 +0000
+++ b/bzrlib/tests/test_merge.py 2009-12-04 01:56:26 +0000
@@ -38,6 +38,7 @@
from bzrlib.workingtree import WorkingTree
from bzrlib.transform import TreeTransform
+
class TestMerge(TestCaseWithTransport):
"""Test appending more than one revision"""
@@ -524,6 +525,12 @@
self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
+ def test_base_from_plan(self):
+ self.setup_plan_merge()
+ plan = self.plan_merge_vf.plan_merge('B', 'C')
+ pwm = versionedfile.PlanWeaveMerge(plan)
+ self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
+
def test_unique_lines(self):
plan = self.setup_plan_merge()
self.assertEqual(plan._unique_lines(
@@ -827,6 +834,111 @@
('unchanged', 'f\n'),
('unchanged', 'g\n')],
list(plan))
+ plan = self.plan_merge_vf.plan_lca_merge('F', 'G')
+ # This is one of the main differences between plan_merge and
+ # plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
+ # because 'x' was not present in one of the bases. However, in this
+ # case it is spurious because 'x' does not exist in the global base A.
+ self.assertEqual([
+ ('unchanged', 'h\n'),
+ ('unchanged', 'a\n'),
+ ('conflicted-a', 'x\n'),
+ ('new-b', 'z\n'),
+ ('unchanged', 'c\n'),
+ ('unchanged', 'd\n'),
+ ('unchanged', 'y\n'),
+ ('unchanged', 'f\n'),
+ ('unchanged', 'g\n')],
+ list(plan))
+
+ def test_criss_cross_flip_flop(self):
+ # This is specificly trying to trigger problems when using limited
+ # ancestry and weaves. The ancestry graph looks like:
+ # XX unused ancestor, should not show up in the weave
+ # |
+ # A Unique LCA
+ # / \
+ # B C B & C both introduce a new line
+ # |\ /|
+ # | X |
+ # |/ \|
+ # D E B & C are both merged, so both are common ancestors
+ # In the process of merging, both sides order the new
+ # lines differently
+ #
+ self.add_rev('root', 'XX', [], 'qrs')
+ self.add_rev('root', 'A', ['XX'], 'abcdef')
+ self.add_rev('root', 'B', ['A'], 'abcdgef')
+ self.add_rev('root', 'C', ['A'], 'abcdhef')
+ self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
+ self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
+ plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
+ self.assertEqual([
+ ('unchanged', 'a\n'),
+ ('unchanged', 'b\n'),
+ ('unchanged', 'c\n'),
+ ('unchanged', 'd\n'),
+ ('new-b', 'h\n'),
+ ('unchanged', 'g\n'),
+ ('killed-b', 'h\n'),
+ ('unchanged', 'e\n'),
+ ('unchanged', 'f\n'),
+ ], plan)
+ pwm = versionedfile.PlanWeaveMerge(plan)
+ self.assertEqualDiff('\n'.join('abcdghef') + '\n',
+ ''.join(pwm.base_from_plan()))
+ # Reversing the order reverses the merge plan, and final order of 'hg'
+ # => 'gh'
+ plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
+ self.assertEqual([
+ ('unchanged', 'a\n'),
+ ('unchanged', 'b\n'),
+ ('unchanged', 'c\n'),
+ ('unchanged', 'd\n'),
+ ('new-b', 'g\n'),
+ ('unchanged', 'h\n'),
+ ('killed-b', 'g\n'),
+ ('unchanged', 'e\n'),
+ ('unchanged', 'f\n'),
+ ], plan)
+ pwm = versionedfile.PlanWeaveMerge(plan)
+ self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
+ ''.join(pwm.base_from_plan()))
+ # This is where lca differs, in that it (fairly correctly) determines
+ # that there is a conflict because both sides resolved the merge
+ # differently
+ plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
+ self.assertEqual([
+ ('unchanged', 'a\n'),
+ ('unchanged', 'b\n'),
+ ('unchanged', 'c\n'),
+ ('unchanged', 'd\n'),
+ ('conflicted-b', 'h\n'),
+ ('unchanged', 'g\n'),
+ ('conflicted-a', 'h\n'),
+ ('unchanged', 'e\n'),
+ ('unchanged', 'f\n'),
+ ], plan)
+ pwm = versionedfile.PlanWeaveMerge(plan)
+ self.assertEqualDiff('\n'.join('abcdhghef') + '\n',
+ ''.join(pwm.base_from_plan()))
+ # Reversing it changes what line is doubled, but still gives a
+ # double-conflict
+ plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
+ self.assertEqual([
+ ('unchanged', 'a\n'),
+ ('unchanged', 'b\n'),
+ ('unchanged', 'c\n'),
+ ('unchanged', 'd\n'),
+ ('conflicted-b', 'g\n'),
+ ('unchanged', 'h\n'),
+ ('conflicted-a', 'g\n'),
+ ('unchanged', 'e\n'),
+ ('unchanged', 'f\n'),
+ ], plan)
+ pwm = versionedfile.PlanWeaveMerge(plan)
+ self.assertEqualDiff('\n'.join('abcdghgef') + '\n',
+ ''.join(pwm.base_from_plan()))
def assertRemoveExternalReferences(self, filtered_parent_map,
child_map, tails, parent_map):
=== modified file 'bzrlib/versionedfile.py'
--- a/bzrlib/versionedfile.py 2009-12-04 00:59:09 +0000
+++ b/bzrlib/versionedfile.py 2009-12-04 01:56:26 +0000
@@ -1512,6 +1512,12 @@
# 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)
+ # Also note that --weave output isn't a great representation,
+ # as it produces a 'clean' flip-flop. If you merge MabN =>
+ # MbaN you get MabN, if you reverse it you get the reverse.
+ # The BASE in both cases is just the current text, with the
+ # 'other' 'b' line shown as killed-in-other. Which is why it
+ # merges cleanly.
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
More information about the bazaar-commits
mailing list