364
357
'c', but not 'b'.
366
359
this_tree = self.make_branch_and_tree('this')
367
self.build_tree_contents([('this/file', b"a\n")])
360
self.build_tree_contents([('this/file', "a\n")])
368
361
this_tree.add('file')
369
362
this_tree.commit('rev1')
370
other_tree = this_tree.controldir.sprout('other').open_workingtree()
371
self.build_tree_contents([('other/file', b"a\nb\n")])
372
other_tree.commit('rev2b', rev_id=b'rev2b')
373
self.build_tree_contents([('other/file', b"c\na\nb\n")])
374
other_tree.commit('rev3b', rev_id=b'rev3b')
363
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
364
self.build_tree_contents([('other/file', "a\nb\n")])
365
other_tree.commit('rev2b', rev_id='rev2b')
366
self.build_tree_contents([('other/file', "c\na\nb\n")])
367
other_tree.commit('rev3b', rev_id='rev3b')
375
368
this_tree.lock_write()
376
369
self.addCleanup(this_tree.unlock)
377
370
return this_tree, other_tree
379
372
def test_weave_cherrypick(self):
380
373
this_tree, other_tree = self.prepare_cherrypick()
381
merger = _mod_merge.Merger.from_revision_ids(
382
this_tree, b'rev3b', b'rev2b', other_tree.branch)
374
merger = _mod_merge.Merger.from_revision_ids(None,
375
this_tree, 'rev3b', 'rev2b', other_tree.branch)
383
376
merger.merge_type = _mod_merge.WeaveMerger
384
377
merger.do_merge()
385
self.assertFileEqual(b'c\na\n', 'this/file')
378
self.assertFileEqual('c\na\n', 'this/file')
387
380
def test_weave_cannot_reverse_cherrypick(self):
388
381
this_tree, other_tree = self.prepare_cherrypick()
389
merger = _mod_merge.Merger.from_revision_ids(
390
this_tree, b'rev2b', b'rev3b', other_tree.branch)
382
merger = _mod_merge.Merger.from_revision_ids(None,
383
this_tree, 'rev2b', 'rev3b', other_tree.branch)
391
384
merger.merge_type = _mod_merge.WeaveMerger
392
385
self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
394
387
def test_merge3_can_reverse_cherrypick(self):
395
388
this_tree, other_tree = self.prepare_cherrypick()
396
merger = _mod_merge.Merger.from_revision_ids(
397
this_tree, b'rev2b', b'rev3b', other_tree.branch)
389
merger = _mod_merge.Merger.from_revision_ids(None,
390
this_tree, 'rev2b', 'rev3b', other_tree.branch)
398
391
merger.merge_type = _mod_merge.Merge3Merger
399
392
merger.do_merge()
401
394
def test_merge3_will_detect_cherrypick(self):
402
395
this_tree = self.make_branch_and_tree('this')
403
self.build_tree_contents([('this/file', b"a\n")])
396
self.build_tree_contents([('this/file', "a\n")])
404
397
this_tree.add('file')
405
398
this_tree.commit('rev1')
406
other_tree = this_tree.controldir.sprout('other').open_workingtree()
407
self.build_tree_contents([('other/file', b"a\nb\n")])
408
other_tree.commit('rev2b', rev_id=b'rev2b')
409
self.build_tree_contents([('other/file', b"a\nb\nc\n")])
410
other_tree.commit('rev3b', rev_id=b'rev3b')
399
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
400
self.build_tree_contents([('other/file', "a\nb\n")])
401
other_tree.commit('rev2b', rev_id='rev2b')
402
self.build_tree_contents([('other/file', "a\nb\nc\n")])
403
other_tree.commit('rev3b', rev_id='rev3b')
411
404
this_tree.lock_write()
412
405
self.addCleanup(this_tree.unlock)
414
merger = _mod_merge.Merger.from_revision_ids(
415
this_tree, b'rev3b', b'rev2b', other_tree.branch)
407
merger = _mod_merge.Merger.from_revision_ids(None,
408
this_tree, 'rev3b', 'rev2b', other_tree.branch)
416
409
merger.merge_type = _mod_merge.Merge3Merger
417
410
merger.do_merge()
418
self.assertFileEqual(b'a\n'
422
b'>>>>>>> MERGE-SOURCE\n',
411
self.assertFileEqual('a\n'
415
'>>>>>>> MERGE-SOURCE\n',
425
418
def test_merge_reverse_revision_range(self):
444
437
def test_make_merger(self):
445
438
this_tree = self.make_branch_and_tree('this')
446
this_tree.commit('rev1', rev_id=b'rev1')
447
other_tree = this_tree.controldir.sprout('other').open_workingtree()
448
this_tree.commit('rev2', rev_id=b'rev2a')
449
other_tree.commit('rev2', rev_id=b'rev2b')
439
this_tree.commit('rev1', rev_id='rev1')
440
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
441
this_tree.commit('rev2', rev_id='rev2a')
442
other_tree.commit('rev2', rev_id='rev2b')
450
443
this_tree.lock_write()
451
444
self.addCleanup(this_tree.unlock)
452
merger = _mod_merge.Merger.from_revision_ids(
453
this_tree, b'rev2b', other_branch=other_tree.branch)
445
merger = _mod_merge.Merger.from_revision_ids(None,
446
this_tree, 'rev2b', other_branch=other_tree.branch)
454
447
merger.merge_type = _mod_merge.Merge3Merger
455
448
tree_merger = merger.make_merger()
456
449
self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
457
self.assertEqual(b'rev2b',
458
tree_merger.other_tree.get_revision_id())
459
self.assertEqual(b'rev1',
460
tree_merger.base_tree.get_revision_id())
450
self.assertEqual('rev2b',
451
tree_merger.other_tree.get_revision_id())
452
self.assertEqual('rev1',
453
tree_merger.base_tree.get_revision_id())
461
454
self.assertEqual(other_tree.branch, tree_merger.other_branch)
463
456
def test_make_preview_transform(self):
464
457
this_tree = self.make_branch_and_tree('this')
465
self.build_tree_contents([('this/file', b'1\n')])
466
this_tree.add('file', b'file-id')
467
this_tree.commit('rev1', rev_id=b'rev1')
468
other_tree = this_tree.controldir.sprout('other').open_workingtree()
469
self.build_tree_contents([('this/file', b'1\n2a\n')])
470
this_tree.commit('rev2', rev_id=b'rev2a')
471
self.build_tree_contents([('other/file', b'2b\n1\n')])
472
other_tree.commit('rev2', rev_id=b'rev2b')
458
self.build_tree_contents([('this/file', '1\n')])
459
this_tree.add('file', 'file-id')
460
this_tree.commit('rev1', rev_id='rev1')
461
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
462
self.build_tree_contents([('this/file', '1\n2a\n')])
463
this_tree.commit('rev2', rev_id='rev2a')
464
self.build_tree_contents([('other/file', '2b\n1\n')])
465
other_tree.commit('rev2', rev_id='rev2b')
473
466
this_tree.lock_write()
474
467
self.addCleanup(this_tree.unlock)
475
merger = _mod_merge.Merger.from_revision_ids(
476
this_tree, b'rev2b', other_branch=other_tree.branch)
468
merger = _mod_merge.Merger.from_revision_ids(None,
469
this_tree, 'rev2b', other_branch=other_tree.branch)
477
470
merger.merge_type = _mod_merge.Merge3Merger
478
471
tree_merger = merger.make_merger()
479
with tree_merger.make_preview_transform() as tt:
480
preview_tree = tt.get_preview_tree()
481
with this_tree.get_file('file') as tree_file:
482
self.assertEqual(b'1\n2a\n', tree_file.read())
483
with preview_tree.get_file('file') as preview_file:
484
self.assertEqual(b'2b\n1\n2a\n', preview_file.read())
472
tt = tree_merger.make_preview_transform()
473
self.addCleanup(tt.finalize)
474
preview_tree = tt.get_preview_tree()
475
tree_file = this_tree.get_file('file-id')
477
self.assertEqual('1\n2a\n', tree_file.read())
480
preview_file = preview_tree.get_file('file-id')
482
self.assertEqual('2b\n1\n2a\n', preview_file.read())
486
486
def test_do_merge(self):
487
487
this_tree = self.make_branch_and_tree('this')
488
self.build_tree_contents([('this/file', b'1\n')])
489
this_tree.add('file', b'file-id')
490
this_tree.commit('rev1', rev_id=b'rev1')
491
other_tree = this_tree.controldir.sprout('other').open_workingtree()
492
self.build_tree_contents([('this/file', b'1\n2a\n')])
493
this_tree.commit('rev2', rev_id=b'rev2a')
494
self.build_tree_contents([('other/file', b'2b\n1\n')])
495
other_tree.commit('rev2', rev_id=b'rev2b')
488
self.build_tree_contents([('this/file', '1\n')])
489
this_tree.add('file', 'file-id')
490
this_tree.commit('rev1', rev_id='rev1')
491
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
492
self.build_tree_contents([('this/file', '1\n2a\n')])
493
this_tree.commit('rev2', rev_id='rev2a')
494
self.build_tree_contents([('other/file', '2b\n1\n')])
495
other_tree.commit('rev2', rev_id='rev2b')
496
496
this_tree.lock_write()
497
497
self.addCleanup(this_tree.unlock)
498
merger = _mod_merge.Merger.from_revision_ids(
499
this_tree, b'rev2b', other_branch=other_tree.branch)
498
merger = _mod_merge.Merger.from_revision_ids(None,
499
this_tree, 'rev2b', other_branch=other_tree.branch)
500
500
merger.merge_type = _mod_merge.Merge3Merger
501
501
tree_merger = merger.make_merger()
502
502
tt = tree_merger.do_merge()
503
with this_tree.get_file('file') as tree_file:
504
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
503
tree_file = this_tree.get_file('file-id')
505
self.assertEqual('2b\n1\n2a\n', tree_file.read())
506
509
def test_merge_require_tree_root(self):
507
510
tree = self.make_branch_and_tree(".")
574
576
def add_uncommitted_version(self, key, parents, text):
575
577
self.plan_merge_vf.add_lines(key, parents,
576
[bytes([c]) + b'\n' for c in bytearray(text)])
578
[c+'\n' for c in text])
578
580
def setup_plan_merge(self):
579
self.add_rev(b'root', b'A', [], b'abc')
580
self.add_rev(b'root', b'B', [b'A'], b'acehg')
581
self.add_rev(b'root', b'C', [b'A'], b'fabg')
582
return _PlanMerge(b'B', b'C', self.plan_merge_vf, (b'root',))
581
self.add_rev('root', 'A', [], 'abc')
582
self.add_rev('root', 'B', ['A'], 'acehg')
583
self.add_rev('root', 'C', ['A'], 'fabg')
584
return _PlanMerge('B', 'C', self.plan_merge_vf, ('root',))
584
586
def setup_plan_merge_uncommitted(self):
585
self.add_version((b'root', b'A'), [], b'abc')
586
self.add_uncommitted_version(
587
(b'root', b'B:'), [(b'root', b'A')], b'acehg')
588
self.add_uncommitted_version(
589
(b'root', b'C:'), [(b'root', b'A')], b'fabg')
590
return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
587
self.add_version(('root', 'A'), [], 'abc')
588
self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
589
self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
590
return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
592
592
def test_base_from_plan(self):
593
593
self.setup_plan_merge()
594
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
594
plan = self.plan_merge_vf.plan_merge('B', 'C')
595
595
pwm = versionedfile.PlanWeaveMerge(plan)
596
self.assertEqual([b'a\n', b'b\n', b'c\n'], pwm.base_from_plan())
596
self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
598
598
def test_unique_lines(self):
599
599
plan = self.setup_plan_merge()
600
600
self.assertEqual(plan._unique_lines(
601
plan._get_matching_blocks(b'B', b'C')),
601
plan._get_matching_blocks('B', 'C')),
602
602
([1, 2, 3], [0, 2]))
604
604
def test_plan_merge(self):
605
605
self.setup_plan_merge()
606
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
606
plan = self.plan_merge_vf.plan_merge('B', 'C')
607
607
self.assertEqual([
609
('unchanged', b'a\n'),
610
('killed-a', b'b\n'),
611
('killed-b', b'c\n'),
609
('unchanged', 'a\n'),
618
618
def test_plan_merge_cherrypick(self):
619
self.add_rev(b'root', b'A', [], b'abc')
620
self.add_rev(b'root', b'B', [b'A'], b'abcde')
621
self.add_rev(b'root', b'C', [b'A'], b'abcefg')
622
self.add_rev(b'root', b'D', [b'A', b'B', b'C'], b'abcdegh')
623
my_plan = _PlanMerge(b'B', b'D', self.plan_merge_vf, (b'root',))
619
self.add_rev('root', 'A', [], 'abc')
620
self.add_rev('root', 'B', ['A'], 'abcde')
621
self.add_rev('root', 'C', ['A'], 'abcefg')
622
self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
623
my_plan = _PlanMerge('B', 'D', self.plan_merge_vf, ('root',))
624
624
# We shortcut when one text supersedes the other in the per-file graph.
625
625
# We don't actually need to compare the texts at this point.
626
626
self.assertEqual([
634
list(my_plan.plan_merge()))
634
list(my_plan.plan_merge()))
636
636
def test_plan_merge_no_common_ancestor(self):
637
self.add_rev(b'root', b'A', [], b'abc')
638
self.add_rev(b'root', b'B', [], b'xyz')
639
my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
637
self.add_rev('root', 'A', [], 'abc')
638
self.add_rev('root', 'B', [], 'xyz')
639
my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
640
640
self.assertEqual([
647
list(my_plan.plan_merge()))
647
list(my_plan.plan_merge()))
649
649
def test_plan_merge_tail_ancestors(self):
650
650
# The graph looks like this:
921
921
# XX unused ancestor, should not show up in the weave
925
925
# B C B & C both introduce a new line
929
929
# D E B & C are both merged, so both are common ancestors
930
930
# In the process of merging, both sides order the new
931
931
# lines differently
933
self.add_rev(b'root', b'XX', [], b'qrs')
934
self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
935
self.add_rev(b'root', b'B', [b'A'], b'abcdgef')
936
self.add_rev(b'root', b'C', [b'A'], b'abcdhef')
937
self.add_rev(b'root', b'D', [b'B', b'C'], b'abcdghef')
938
self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
939
plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
933
self.add_rev('root', 'XX', [], 'qrs')
934
self.add_rev('root', 'A', ['XX'], 'abcdef')
935
self.add_rev('root', 'B', ['A'], 'abcdgef')
936
self.add_rev('root', 'C', ['A'], 'abcdhef')
937
self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
938
self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
939
plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
940
940
self.assertEqual([
941
('unchanged', b'a\n'),
942
('unchanged', b'b\n'),
943
('unchanged', b'c\n'),
944
('unchanged', b'd\n'),
946
('unchanged', b'g\n'),
947
('killed-b', b'h\n'),
948
('unchanged', b'e\n'),
949
('unchanged', b'f\n'),
941
('unchanged', 'a\n'),
942
('unchanged', 'b\n'),
943
('unchanged', 'c\n'),
944
('unchanged', 'd\n'),
946
('unchanged', 'g\n'),
948
('unchanged', 'e\n'),
949
('unchanged', 'f\n'),
951
951
pwm = versionedfile.PlanWeaveMerge(plan)
952
self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
953
b''.join(pwm.base_from_plan()))
952
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
953
''.join(pwm.base_from_plan()))
954
954
# Reversing the order reverses the merge plan, and final order of 'hg'
956
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
956
plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
957
957
self.assertEqual([
958
('unchanged', b'a\n'),
959
('unchanged', b'b\n'),
960
('unchanged', b'c\n'),
961
('unchanged', b'd\n'),
963
('unchanged', b'h\n'),
964
('killed-b', b'g\n'),
965
('unchanged', b'e\n'),
966
('unchanged', b'f\n'),
958
('unchanged', 'a\n'),
959
('unchanged', 'b\n'),
960
('unchanged', 'c\n'),
961
('unchanged', 'd\n'),
963
('unchanged', 'h\n'),
965
('unchanged', 'e\n'),
966
('unchanged', 'f\n'),
968
968
pwm = versionedfile.PlanWeaveMerge(plan)
969
self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
970
b''.join(pwm.base_from_plan()))
969
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
970
''.join(pwm.base_from_plan()))
971
971
# This is where lca differs, in that it (fairly correctly) determines
972
972
# that there is a conflict because both sides resolved the merge
974
plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
974
plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
975
975
self.assertEqual([
976
('unchanged', b'a\n'),
977
('unchanged', b'b\n'),
978
('unchanged', b'c\n'),
979
('unchanged', b'd\n'),
980
('conflicted-b', b'h\n'),
981
('unchanged', b'g\n'),
982
('conflicted-a', b'h\n'),
983
('unchanged', b'e\n'),
984
('unchanged', b'f\n'),
976
('unchanged', 'a\n'),
977
('unchanged', 'b\n'),
978
('unchanged', 'c\n'),
979
('unchanged', 'd\n'),
980
('conflicted-b', 'h\n'),
981
('unchanged', 'g\n'),
982
('conflicted-a', 'h\n'),
983
('unchanged', 'e\n'),
984
('unchanged', 'f\n'),
986
986
pwm = versionedfile.PlanWeaveMerge(plan)
987
self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
988
b''.join(pwm.base_from_plan()))
987
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
988
''.join(pwm.base_from_plan()))
989
989
# Reversing it changes what line is doubled, but still gives a
990
990
# double-conflict
991
plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
991
plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
992
992
self.assertEqual([
993
('unchanged', b'a\n'),
994
('unchanged', b'b\n'),
995
('unchanged', b'c\n'),
996
('unchanged', b'd\n'),
997
('conflicted-b', b'g\n'),
998
('unchanged', b'h\n'),
999
('conflicted-a', b'g\n'),
1000
('unchanged', b'e\n'),
1001
('unchanged', b'f\n'),
993
('unchanged', 'a\n'),
994
('unchanged', 'b\n'),
995
('unchanged', 'c\n'),
996
('unchanged', 'd\n'),
997
('conflicted-b', 'g\n'),
998
('unchanged', 'h\n'),
999
('conflicted-a', 'g\n'),
1000
('unchanged', 'e\n'),
1001
('unchanged', 'f\n'),
1003
1003
pwm = versionedfile.PlanWeaveMerge(plan)
1004
self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
1005
b''.join(pwm.base_from_plan()))
1004
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
1005
''.join(pwm.base_from_plan()))
1007
1007
def assertRemoveExternalReferences(self, filtered_parent_map,
1008
1008
child_map, tails, parent_map):
1066
1066
self.assertPruneTails({1: []}, [5],
1067
1067
{1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
1068
1068
# Prune a partial chain
1069
self.assertPruneTails({1: [6], 6: []}, [5],
1069
self.assertPruneTails({1: [6], 6:[]}, [5],
1070
1070
{1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
1072
1072
# Prune a chain with multiple tips, that pulls out intermediates
1073
self.assertPruneTails({1: [3], 3: []}, [4, 5],
1074
{1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
1075
self.assertPruneTails({1: [3], 3: []}, [5, 4],
1076
{1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
1073
self.assertPruneTails({1:[3], 3:[]}, [4, 5],
1074
{1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1075
self.assertPruneTails({1:[3], 3:[]}, [5, 4],
1076
{1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1078
1078
def test_subtract_plans(self):
1080
('unchanged', b'a\n'),
1082
('killed-a', b'c\n'),
1085
('killed-b', b'f\n'),
1086
('killed-b', b'g\n'),
1080
('unchanged', 'a\n'),
1082
('killed-a', 'c\n'),
1085
('killed-b', 'f\n'),
1086
('killed-b', 'g\n'),
1089
('unchanged', b'a\n'),
1091
('killed-a', b'c\n'),
1094
('killed-b', b'f\n'),
1095
('killed-b', b'i\n'),
1089
('unchanged', 'a\n'),
1091
('killed-a', 'c\n'),
1094
('killed-b', 'f\n'),
1095
('killed-b', 'i\n'),
1097
1097
subtracted_plan = [
1098
('unchanged', b'a\n'),
1100
('killed-a', b'c\n'),
1102
('unchanged', b'f\n'),
1103
('killed-b', b'i\n'),
1098
('unchanged', 'a\n'),
1100
('killed-a', 'c\n'),
1102
('unchanged', 'f\n'),
1103
('killed-b', 'i\n'),
1105
1105
self.assertEqual(subtracted_plan,
1106
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1106
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1108
1108
def setup_merge_with_base(self):
1109
self.add_rev(b'root', b'COMMON', [], b'abc')
1110
self.add_rev(b'root', b'THIS', [b'COMMON'], b'abcd')
1111
self.add_rev(b'root', b'BASE', [b'COMMON'], b'eabc')
1112
self.add_rev(b'root', b'OTHER', [b'BASE'], b'eafb')
1109
self.add_rev('root', 'COMMON', [], 'abc')
1110
self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
1111
self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
1112
self.add_rev('root', 'OTHER', ['BASE'], 'eafb')
1114
1114
def test_plan_merge_with_base(self):
1115
1115
self.setup_merge_with_base()
1116
plan = self.plan_merge_vf.plan_merge(b'THIS', b'OTHER', b'BASE')
1117
self.assertEqual([('unchanged', b'a\n'),
1119
('unchanged', b'b\n'),
1120
('killed-b', b'c\n'),
1116
plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
1117
self.assertEqual([('unchanged', 'a\n'),
1119
('unchanged', 'b\n'),
1120
('killed-b', 'c\n'),
1124
1124
def test_plan_lca_merge(self):
1125
1125
self.setup_plan_merge()
1126
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1126
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1127
1127
self.assertEqual([
1129
('unchanged', b'a\n'),
1130
('killed-b', b'c\n'),
1133
('killed-a', b'b\n'),
1134
('unchanged', b'g\n')],
1129
('unchanged', 'a\n'),
1130
('killed-b', 'c\n'),
1133
('killed-a', 'b\n'),
1134
('unchanged', 'g\n')],
1137
1137
def test_plan_lca_merge_uncommitted_files(self):
1138
1138
self.setup_plan_merge_uncommitted()
1139
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1139
plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1140
1140
self.assertEqual([
1142
('unchanged', b'a\n'),
1143
('killed-b', b'c\n'),
1146
('killed-a', b'b\n'),
1147
('unchanged', b'g\n')],
1142
('unchanged', 'a\n'),
1143
('killed-b', 'c\n'),
1146
('killed-a', 'b\n'),
1147
('unchanged', 'g\n')],
1150
1150
def test_plan_lca_merge_with_base(self):
1151
1151
self.setup_merge_with_base()
1152
plan = self.plan_merge_vf.plan_lca_merge(b'THIS', b'OTHER', b'BASE')
1153
self.assertEqual([('unchanged', b'a\n'),
1155
('unchanged', b'b\n'),
1156
('killed-b', b'c\n'),
1152
plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
1153
self.assertEqual([('unchanged', 'a\n'),
1155
('unchanged', 'b\n'),
1156
('killed-b', 'c\n'),
1160
1160
def test_plan_lca_merge_with_criss_cross(self):
1161
self.add_version((b'root', b'ROOT'), [], b'abc')
1161
self.add_version(('root', 'ROOT'), [], 'abc')
1162
1162
# each side makes a change
1163
self.add_version((b'root', b'REV1'), [(b'root', b'ROOT')], b'abcd')
1164
self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
1163
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1164
self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
1165
1165
# both sides merge, discarding others' changes
1166
self.add_version((b'root', b'LCA1'),
1167
[(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1168
self.add_version((b'root', b'LCA2'),
1169
[(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1170
plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1171
self.assertEqual([('new-b', b'f\n'),
1172
('unchanged', b'a\n'),
1173
('unchanged', b'b\n'),
1174
('unchanged', b'c\n'),
1175
('conflicted-a', b'd\n'),
1176
('conflicted-b', b'e\n'),
1166
self.add_version(('root', 'LCA1'),
1167
[('root', 'REV1'), ('root', 'REV2')], 'abcd')
1168
self.add_version(('root', 'LCA2'),
1169
[('root', 'REV1'), ('root', 'REV2')], 'fabce')
1170
plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
1171
self.assertEqual([('new-b', 'f\n'),
1172
('unchanged', 'a\n'),
1173
('unchanged', 'b\n'),
1174
('unchanged', 'c\n'),
1175
('conflicted-a', 'd\n'),
1176
('conflicted-b', 'e\n'),
1179
1179
def test_plan_lca_merge_with_null(self):
1180
self.add_version((b'root', b'A'), [], b'ab')
1181
self.add_version((b'root', b'B'), [], b'bc')
1182
plan = self.plan_merge_vf.plan_lca_merge(b'A', b'B')
1183
self.assertEqual([('new-a', b'a\n'),
1184
('unchanged', b'b\n'),
1180
self.add_version(('root', 'A'), [], 'ab')
1181
self.add_version(('root', 'B'), [], 'bc')
1182
plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
1183
self.assertEqual([('new-a', 'a\n'),
1184
('unchanged', 'b\n'),
1188
1188
def test_plan_merge_with_delete_and_change(self):
1189
self.add_rev(b'root', b'C', [], b'a')
1190
self.add_rev(b'root', b'A', [b'C'], b'b')
1191
self.add_rev(b'root', b'B', [b'C'], b'')
1192
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1193
self.assertEqual([('killed-both', b'a\n'),
1189
self.add_rev('root', 'C', [], 'a')
1190
self.add_rev('root', 'A', ['C'], 'b')
1191
self.add_rev('root', 'B', ['C'], '')
1192
plan = self.plan_merge_vf.plan_merge('A', 'B')
1193
self.assertEqual([('killed-both', 'a\n'),
1197
1197
def test_plan_merge_with_move_and_change(self):
1198
self.add_rev(b'root', b'C', [], b'abcd')
1199
self.add_rev(b'root', b'A', [b'C'], b'acbd')
1200
self.add_rev(b'root', b'B', [b'C'], b'aBcd')
1201
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1202
self.assertEqual([('unchanged', b'a\n'),
1204
('killed-b', b'b\n'),
1206
('killed-a', b'c\n'),
1207
('unchanged', b'd\n'),
1198
self.add_rev('root', 'C', [], 'abcd')
1199
self.add_rev('root', 'A', ['C'], 'acbd')
1200
self.add_rev('root', 'B', ['C'], 'aBcd')
1201
plan = self.plan_merge_vf.plan_merge('A', 'B')
1202
self.assertEqual([('unchanged', 'a\n'),
1204
('killed-b', 'b\n'),
1206
('killed-a', 'c\n'),
1207
('unchanged', 'd\n'),
1211
1211
class LoggingMerger(object):
1395
1396
class TestMergerEntriesLCA(TestMergerBase):
1397
1398
def make_merge_obj(self, builder, other_revision_id,
1398
interesting_files=None):
1399
interesting_files=None, interesting_ids=None):
1399
1400
merger = self.make_Merger(builder, other_revision_id,
1400
interesting_files=interesting_files)
1401
interesting_files=interesting_files,
1402
interesting_ids=interesting_ids)
1401
1403
return merger.make_merger()
1403
1405
def test_simple(self):
1404
1406
builder = self.get_builder()
1405
builder.build_snapshot(None,
1406
[('add', (u'', b'a-root-id', 'directory', None)),
1407
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1408
revision_id=b'A-id')
1409
builder.build_snapshot([b'A-id'],
1410
[('modify', ('a', b'a\nb\nC\nc\n'))],
1411
revision_id=b'C-id')
1412
builder.build_snapshot([b'A-id'],
1413
[('modify', ('a', b'a\nB\nb\nc\n'))],
1414
revision_id=b'B-id')
1415
builder.build_snapshot([b'C-id', b'B-id'],
1416
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1417
revision_id=b'E-id')
1418
builder.build_snapshot([b'B-id', b'C-id'],
1419
[('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1420
revision_id=b'D-id', )
1421
merge_obj = self.make_merge_obj(builder, b'E-id')
1407
builder.build_snapshot('A-id', None,
1408
[('add', (u'', 'a-root-id', 'directory', None)),
1409
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1410
builder.build_snapshot('C-id', ['A-id'],
1411
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1412
builder.build_snapshot('B-id', ['A-id'],
1413
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1414
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1415
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1416
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1417
[('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
1418
merge_obj = self.make_merge_obj(builder, 'E-id')
1423
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1424
for t in merge_obj._lca_trees])
1425
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1420
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1421
for t in merge_obj._lca_trees])
1422
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1426
1423
entries = list(merge_obj._entries_lca())
1428
1425
# (file_id, changed, parents, names, executable)
1429
1426
# BASE, lca1, lca2, OTHER, THIS
1430
root_id = b'a-root-id'
1431
self.assertEqual([(b'a-id', True,
1432
((u'a', [u'a', u'a']), u'a', u'a'),
1427
root_id = 'a-root-id'
1428
self.assertEqual([('a-id', True,
1433
1429
((root_id, [root_id, root_id]), root_id, root_id),
1434
1430
((u'a', [u'a', u'a']), u'a', u'a'),
1435
1431
((False, [False, False]), False, False)),
1438
1434
def test_not_in_base(self):
1439
1435
# LCAs all have the same last-modified revision for the file, as do
1448
1444
# G modifies 'bar'
1450
1446
builder = self.get_builder()
1451
builder.build_snapshot(None,
1452
[('add', (u'', b'a-root-id', 'directory', None))],
1453
revision_id=b'A-id')
1454
builder.build_snapshot([b'A-id'],
1455
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1456
revision_id=b'B-id')
1457
builder.build_snapshot([b'A-id'],
1458
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1459
revision_id=b'C-id')
1460
builder.build_snapshot([b'B-id', b'C-id'],
1461
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1462
revision_id=b'D-id')
1463
builder.build_snapshot([b'C-id', b'B-id'],
1464
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1465
revision_id=b'E-id')
1466
builder.build_snapshot([b'E-id', b'D-id'],
1467
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1468
revision_id=b'G-id')
1469
builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1470
merge_obj = self.make_merge_obj(builder, b'G-id')
1447
builder.build_snapshot('A-id', None,
1448
[('add', (u'', 'a-root-id', 'directory', None))])
1449
builder.build_snapshot('B-id', ['A-id'],
1450
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1451
builder.build_snapshot('C-id', ['A-id'],
1452
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1453
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1454
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1455
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1456
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1457
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1458
[('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1459
builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
1460
merge_obj = self.make_merge_obj(builder, 'G-id')
1472
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1473
for t in merge_obj._lca_trees])
1474
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1462
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1463
for t in merge_obj._lca_trees])
1464
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1475
1465
entries = list(merge_obj._entries_lca())
1476
root_id = b'a-root-id'
1477
self.assertEqual([(b'bar-id', True,
1478
((None, [u'bar', u'bar']), u'bar', u'bar'),
1466
root_id = 'a-root-id'
1467
self.assertEqual([('bar-id', True,
1479
1468
((None, [root_id, root_id]), root_id, root_id),
1480
1469
((None, [u'bar', u'bar']), u'bar', u'bar'),
1481
1470
((None, [False, False]), False, False)),
1484
1473
def test_not_in_this(self):
1485
1474
builder = self.get_builder()
1486
builder.build_snapshot(None,
1487
[('add', (u'', b'a-root-id', 'directory', None)),
1488
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1489
revision_id=b'A-id')
1490
builder.build_snapshot([b'A-id'],
1491
[('modify', ('a', b'a\nB\nb\nc\n'))],
1492
revision_id=b'B-id')
1493
builder.build_snapshot([b'A-id'],
1494
[('modify', ('a', b'a\nb\nC\nc\n'))],
1495
revision_id=b'C-id')
1496
builder.build_snapshot([b'C-id', b'B-id'],
1497
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1498
revision_id=b'E-id')
1499
builder.build_snapshot([b'B-id', b'C-id'],
1500
[('unversion', 'a')],
1501
revision_id=b'D-id')
1502
merge_obj = self.make_merge_obj(builder, b'E-id')
1475
builder.build_snapshot('A-id', None,
1476
[('add', (u'', 'a-root-id', 'directory', None)),
1477
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1478
builder.build_snapshot('B-id', ['A-id'],
1479
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1480
builder.build_snapshot('C-id', ['A-id'],
1481
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1482
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1483
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1484
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1485
[('unversion', 'a-id')])
1486
merge_obj = self.make_merge_obj(builder, 'E-id')
1504
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1505
for t in merge_obj._lca_trees])
1506
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1488
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1489
for t in merge_obj._lca_trees])
1490
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1508
1492
entries = list(merge_obj._entries_lca())
1509
root_id = b'a-root-id'
1510
self.assertEqual([(b'a-id', True,
1511
((u'a', [u'a', u'a']), u'a', None),
1493
root_id = 'a-root-id'
1494
self.assertEqual([('a-id', True,
1512
1495
((root_id, [root_id, root_id]), root_id, None),
1513
1496
((u'a', [u'a', u'a']), u'a', None),
1514
1497
((False, [False, False]), False, None)),
1517
1500
def test_file_not_in_one_lca(self):
1518
1501
# A # just root
1522
1505
# D E # D and E both have the file, unchanged from C
1523
1506
builder = self.get_builder()
1524
builder.build_snapshot(None,
1525
[('add', (u'', b'a-root-id', 'directory', None))],
1526
revision_id=b'A-id')
1527
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1528
builder.build_snapshot([b'A-id'],
1529
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1530
revision_id=b'C-id')
1531
builder.build_snapshot([b'C-id', b'B-id'],
1532
[], revision_id=b'E-id') # Inherited from C
1533
builder.build_snapshot([b'B-id', b'C-id'], # Merged from C
1534
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1535
revision_id=b'D-id')
1536
merge_obj = self.make_merge_obj(builder, b'E-id')
1507
builder.build_snapshot('A-id', None,
1508
[('add', (u'', 'a-root-id', 'directory', None))])
1509
builder.build_snapshot('B-id', ['A-id'], [])
1510
builder.build_snapshot('C-id', ['A-id'],
1511
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1512
builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1513
builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
1514
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1515
merge_obj = self.make_merge_obj(builder, 'E-id')
1538
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1539
for t in merge_obj._lca_trees])
1540
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1517
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1518
for t in merge_obj._lca_trees])
1519
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1542
1521
entries = list(merge_obj._entries_lca())
1543
1522
self.assertEqual([], entries)
1545
1524
def test_not_in_other(self):
1546
1525
builder = self.get_builder()
1547
builder.build_snapshot(None,
1548
[('add', (u'', b'a-root-id', 'directory', None)),
1549
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1550
revision_id=b'A-id')
1551
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1552
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1553
builder.build_snapshot(
1555
[('unversion', 'a')], revision_id=b'E-id')
1556
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1557
merge_obj = self.make_merge_obj(builder, b'E-id')
1526
builder.build_snapshot('A-id', None,
1527
[('add', (u'', 'a-root-id', 'directory', None)),
1528
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1529
builder.build_snapshot('B-id', ['A-id'], [])
1530
builder.build_snapshot('C-id', ['A-id'], [])
1531
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1532
[('unversion', 'a-id')])
1533
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1534
merge_obj = self.make_merge_obj(builder, 'E-id')
1559
1536
entries = list(merge_obj._entries_lca())
1560
root_id = b'a-root-id'
1561
self.assertEqual([(b'a-id', True,
1562
((u'a', [u'a', u'a']), None, u'a'),
1537
root_id = 'a-root-id'
1538
self.assertEqual([('a-id', True,
1563
1539
((root_id, [root_id, root_id]), None, root_id),
1564
1540
((u'a', [u'a', u'a']), None, u'a'),
1565
1541
((False, [False, False]), None, False)),
1568
1544
def test_not_in_other_or_lca(self):
1569
1545
# A base, introduces 'foo'
1648
1619
# A => C, add file, thus C supersedes B
1649
1620
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1650
1621
builder = self.get_builder()
1651
builder.build_snapshot(None,
1652
[('add', (u'', b'a-root-id', 'directory', None))],
1653
revision_id=b'A-id')
1654
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1655
builder.build_snapshot([b'A-id'],
1656
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1657
revision_id=b'C-id')
1658
builder.build_snapshot([b'C-id', b'B-id'],
1659
[('unversion', 'a')],
1660
revision_id=b'E-id')
1661
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1662
merge_obj = self.make_merge_obj(builder, b'E-id')
1622
builder.build_snapshot('A-id', None,
1623
[('add', (u'', 'a-root-id', 'directory', None))])
1624
builder.build_snapshot('B-id', ['A-id'], [])
1625
builder.build_snapshot('C-id', ['A-id'],
1626
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1627
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1628
[('unversion', 'a-id')])
1629
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1630
merge_obj = self.make_merge_obj(builder, 'E-id')
1664
1632
entries = list(merge_obj._entries_lca())
1665
1633
self.assertEqual([], entries)
1667
1635
def test_only_in_other(self):
1668
1636
builder = self.get_builder()
1669
builder.build_snapshot(None,
1670
[('add', (u'', b'a-root-id', 'directory', None))],
1671
revision_id=b'A-id')
1672
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1673
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1674
builder.build_snapshot([b'C-id', b'B-id'],
1675
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1676
revision_id=b'E-id')
1677
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1678
merge_obj = self.make_merge_obj(builder, b'E-id')
1637
builder.build_snapshot('A-id', None,
1638
[('add', (u'', 'a-root-id', 'directory', None))])
1639
builder.build_snapshot('B-id', ['A-id'], [])
1640
builder.build_snapshot('C-id', ['A-id'], [])
1641
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1642
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1643
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1644
merge_obj = self.make_merge_obj(builder, 'E-id')
1680
1646
entries = list(merge_obj._entries_lca())
1681
root_id = b'a-root-id'
1682
self.assertEqual([(b'a-id', True,
1683
((None, [None, None]), u'a', None),
1647
root_id = 'a-root-id'
1648
self.assertEqual([('a-id', True,
1684
1649
((None, [None, None]), root_id, None),
1685
1650
((None, [None, None]), u'a', None),
1686
1651
((None, [None, None]), False, None)),
1689
1654
def test_one_lca_supersedes(self):
1690
1655
# One LCA supersedes the other LCAs last modified value, but the
1789
1745
# be pruned from the LCAs, even though it was newly introduced by E
1790
1746
# (superseding B).
1791
1747
builder = self.get_builder()
1792
builder.build_snapshot(None,
1793
[('add', (u'', b'a-root-id', 'directory', None)),
1794
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1795
revision_id=b'A-id')
1796
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1797
builder.build_snapshot([b'A-id'],
1798
[('rename', ('foo', 'bar'))],
1799
revision_id=b'B-id')
1800
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1801
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1802
builder.build_snapshot([b'E-id', b'D-id'],
1803
[('rename', ('foo', 'bar'))],
1804
revision_id=b'G-id')
1805
builder.build_snapshot([b'D-id', b'E-id'],
1806
[('rename', ('bar', 'bing'))],
1807
revision_id=b'F-id') # should end up conflicting
1808
merge_obj = self.make_merge_obj(builder, b'G-id')
1748
builder.build_snapshot('A-id', None,
1749
[('add', (u'', 'a-root-id', 'directory', None)),
1750
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1751
builder.build_snapshot('C-id', ['A-id'], [])
1752
builder.build_snapshot('B-id', ['A-id'],
1753
[('rename', ('foo', 'bar'))])
1754
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1755
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1756
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1757
[('rename', ('foo', 'bar'))])
1758
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1759
[('rename', ('bar', 'bing'))]) # should end up conflicting
1760
merge_obj = self.make_merge_obj(builder, 'G-id')
1810
1762
entries = list(merge_obj._entries_lca())
1811
root_id = b'a-root-id'
1763
root_id = 'a-root-id'
1812
1764
self.expectFailure("We prune values from BASE even when relevant.",
1815
((root_id, [root_id, root_id]), root_id, root_id),
1816
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1817
((False, [False, False]), False, False)),
1767
((root_id, [root_id, root_id]), root_id, root_id),
1768
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1769
((False, [False, False]), False, False)),
1820
1772
def test_both_sides_revert(self):
1821
1773
# Both sides of a criss-cross revert the text to the lca
1902
1845
# We need to conflict.
1904
1847
builder = self.get_builder()
1905
builder.build_snapshot(None,
1906
[('add', (u'', b'a-root-id', 'directory', None)),
1907
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1908
revision_id=b'A-id')
1909
builder.build_snapshot([b'A-id'],
1910
[('modify', ('foo', b'B content\n'))],
1911
revision_id=b'B-id')
1912
builder.build_snapshot([b'A-id'],
1913
[('modify', ('foo', b'C content\n'))],
1914
revision_id=b'C-id')
1915
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1916
builder.build_snapshot([b'B-id', b'C-id'],
1917
[('modify', ('foo', b'C content\n'))],
1918
revision_id=b'D-id') # Same as E
1919
builder.build_snapshot([b'D-id'],
1920
[('modify', ('foo', b'F content\n'))],
1921
revision_id=b'F-id')
1922
merge_obj = self.make_merge_obj(builder, b'E-id')
1848
builder.build_snapshot('A-id', None,
1849
[('add', (u'', 'a-root-id', 'directory', None)),
1850
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1851
builder.build_snapshot('B-id', ['A-id'],
1852
[('modify', ('foo-id', 'B content\n'))])
1853
builder.build_snapshot('C-id', ['A-id'],
1854
[('modify', ('foo-id', 'C content\n'))])
1855
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1856
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1857
[('modify', ('foo-id', 'C content\n'))]) # Same as E
1858
builder.build_snapshot('F-id', ['D-id'],
1859
[('modify', ('foo-id', 'F content\n'))])
1860
merge_obj = self.make_merge_obj(builder, 'E-id')
1924
1862
entries = list(merge_obj._entries_lca())
1925
1863
self.expectFailure("We don't detect that LCA resolution was the"
1926
1864
" same on both sides",
1927
self.assertEqual, [], entries)
1865
self.assertEqual, [], entries)
1929
1867
def test_only_path_changed(self):
1930
1868
builder = self.get_builder()
1931
builder.build_snapshot(None,
1932
[('add', (u'', b'a-root-id', 'directory', None)),
1933
('add', (u'a', b'a-id', 'file', b'content\n'))],
1934
revision_id=b'A-id')
1935
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1936
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1937
builder.build_snapshot([b'C-id', b'B-id'],
1938
[('rename', (u'a', u'b'))],
1939
revision_id=b'E-id')
1940
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1941
merge_obj = self.make_merge_obj(builder, b'E-id')
1869
builder.build_snapshot('A-id', None,
1870
[('add', (u'', 'a-root-id', 'directory', None)),
1871
('add', (u'a', 'a-id', 'file', 'content\n'))])
1872
builder.build_snapshot('B-id', ['A-id'], [])
1873
builder.build_snapshot('C-id', ['A-id'], [])
1874
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1875
[('rename', (u'a', u'b'))])
1876
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1877
merge_obj = self.make_merge_obj(builder, 'E-id')
1942
1878
entries = list(merge_obj._entries_lca())
1943
root_id = b'a-root-id'
1879
root_id = 'a-root-id'
1944
1880
# The content was not changed, only the path
1945
self.assertEqual([(b'a-id', False,
1946
((u'a', [u'a', u'a']), u'b', u'a'),
1881
self.assertEqual([('a-id', False,
1947
1882
((root_id, [root_id, root_id]), root_id, root_id),
1948
1883
((u'a', [u'a', u'a']), u'b', u'a'),
1949
1884
((False, [False, False]), False, False)),
1952
1887
def test_kind_changed(self):
1953
1888
# Identical content, except 'D' changes a-id into a directory
1954
1889
builder = self.get_builder()
1955
builder.build_snapshot(None,
1956
[('add', (u'', b'a-root-id', 'directory', None)),
1957
('add', (u'a', b'a-id', 'file', b'content\n'))],
1958
revision_id=b'A-id')
1959
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1960
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1961
builder.build_snapshot([b'C-id', b'B-id'],
1962
[('unversion', 'a'),
1964
('add', (u'a', b'a-id', 'directory', None))],
1965
revision_id=b'E-id')
1966
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1967
merge_obj = self.make_merge_obj(builder, b'E-id')
1890
builder.build_snapshot('A-id', None,
1891
[('add', (u'', 'a-root-id', 'directory', None)),
1892
('add', (u'a', 'a-id', 'file', 'content\n'))])
1893
builder.build_snapshot('B-id', ['A-id'], [])
1894
builder.build_snapshot('C-id', ['A-id'], [])
1895
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1896
[('unversion', 'a-id'),
1898
('add', (u'a', 'a-id', 'directory', None))])
1899
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1900
merge_obj = self.make_merge_obj(builder, 'E-id')
1968
1901
entries = list(merge_obj._entries_lca())
1969
root_id = b'a-root-id'
1902
root_id = 'a-root-id'
1970
1903
# Only the kind was changed (content)
1971
self.assertEqual([(b'a-id', True,
1972
((u'a', [u'a', u'a']), u'a', u'a'),
1904
self.assertEqual([('a-id', True,
1973
1905
((root_id, [root_id, root_id]), root_id, root_id),
1974
1906
((u'a', [u'a', u'a']), u'a', u'a'),
1975
1907
((False, [False, False]), False, False)),
1978
1910
def test_this_changed_kind(self):
1979
1911
# Identical content, but THIS changes a file to a directory
1980
1912
builder = self.get_builder()
1981
builder.build_snapshot(None,
1982
[('add', (u'', b'a-root-id', 'directory', None)),
1983
('add', (u'a', b'a-id', 'file', b'content\n'))],
1984
revision_id=b'A-id')
1985
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1986
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1987
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1988
builder.build_snapshot([b'B-id', b'C-id'],
1989
[('unversion', 'a'),
1991
('add', (u'a', b'a-id', 'directory', None))],
1992
revision_id=b'D-id')
1993
merge_obj = self.make_merge_obj(builder, b'E-id')
1913
builder.build_snapshot('A-id', None,
1914
[('add', (u'', 'a-root-id', 'directory', None)),
1915
('add', (u'a', 'a-id', 'file', 'content\n'))])
1916
builder.build_snapshot('B-id', ['A-id'], [])
1917
builder.build_snapshot('C-id', ['A-id'], [])
1918
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1919
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1920
[('unversion', 'a-id'),
1922
('add', (u'a', 'a-id', 'directory', None))])
1923
merge_obj = self.make_merge_obj(builder, 'E-id')
1994
1924
entries = list(merge_obj._entries_lca())
1995
1925
# Only the kind was changed (content)
1996
1926
self.assertEqual([], entries)
1998
1928
def test_interesting_files(self):
1999
1929
# Two files modified, but we should filter one of them
2000
1930
builder = self.get_builder()
2001
builder.build_snapshot(None,
2002
[('add', (u'', b'a-root-id', 'directory', None)),
2003
('add', (u'a', b'a-id', 'file', b'content\n')),
2004
('add', (u'b', b'b-id', 'file', b'content\n'))],
2005
revision_id=b'A-id')
2006
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2007
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2008
builder.build_snapshot([b'C-id', b'B-id'],
2009
[('modify', ('a', b'new-content\n')),
2010
('modify', ('b', b'new-content\n'))],
2011
revision_id=b'E-id')
2012
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2013
merge_obj = self.make_merge_obj(builder, b'E-id',
1931
builder.build_snapshot('A-id', None,
1932
[('add', (u'', 'a-root-id', 'directory', None)),
1933
('add', (u'a', 'a-id', 'file', 'content\n')),
1934
('add', (u'b', 'b-id', 'file', 'content\n'))])
1935
builder.build_snapshot('B-id', ['A-id'], [])
1936
builder.build_snapshot('C-id', ['A-id'], [])
1937
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1938
[('modify', ('a-id', 'new-content\n')),
1939
('modify', ('b-id', 'new-content\n'))])
1940
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1941
merge_obj = self.make_merge_obj(builder, 'E-id',
2014
1942
interesting_files=['b'])
2015
1943
entries = list(merge_obj._entries_lca())
2016
root_id = b'a-root-id'
2017
self.assertEqual([(b'b-id', True,
2018
((u'b', [u'b', u'b']), u'b', u'b'),
1944
root_id = 'a-root-id'
1945
self.assertEqual([('b-id', True,
2019
1946
((root_id, [root_id, root_id]), root_id, root_id),
2020
1947
((u'b', [u'b', u'b']), u'b', u'b'),
2021
1948
((False, [False, False]), False, False)),
2024
1951
def test_interesting_file_in_this(self):
2025
1952
# This renamed the file, but it should still match the entry in other
2026
1953
builder = self.get_builder()
2027
builder.build_snapshot(None,
2028
[('add', (u'', b'a-root-id', 'directory', None)),
2029
('add', (u'a', b'a-id', 'file', b'content\n')),
2030
('add', (u'b', b'b-id', 'file', b'content\n'))],
2031
revision_id=b'A-id')
2032
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2033
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2034
builder.build_snapshot([b'C-id', b'B-id'],
2035
[('modify', ('a', b'new-content\n')),
2036
('modify', ('b', b'new-content\n'))],
2037
revision_id=b'E-id')
2038
builder.build_snapshot([b'B-id', b'C-id'],
2039
[('rename', ('b', 'c'))],
2040
revision_id=b'D-id')
2041
merge_obj = self.make_merge_obj(builder, b'E-id',
1954
builder.build_snapshot('A-id', None,
1955
[('add', (u'', 'a-root-id', 'directory', None)),
1956
('add', (u'a', 'a-id', 'file', 'content\n')),
1957
('add', (u'b', 'b-id', 'file', 'content\n'))])
1958
builder.build_snapshot('B-id', ['A-id'], [])
1959
builder.build_snapshot('C-id', ['A-id'], [])
1960
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1961
[('modify', ('a-id', 'new-content\n')),
1962
('modify', ('b-id', 'new-content\n'))])
1963
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1964
[('rename', ('b', 'c'))])
1965
merge_obj = self.make_merge_obj(builder, 'E-id',
2042
1966
interesting_files=['c'])
2043
1967
entries = list(merge_obj._entries_lca())
2044
root_id = b'a-root-id'
2045
self.assertEqual([(b'b-id', True,
2046
((u'b', [u'b', u'b']), u'b', u'c'),
1968
root_id = 'a-root-id'
1969
self.assertEqual([('b-id', True,
2047
1970
((root_id, [root_id, root_id]), root_id, root_id),
2048
1971
((u'b', [u'b', u'b']), u'b', u'c'),
2049
1972
((False, [False, False]), False, False)),
2052
1975
def test_interesting_file_in_base(self):
2053
1976
# This renamed the file, but it should still match the entry in BASE
2054
1977
builder = self.get_builder()
2055
builder.build_snapshot(None,
2056
[('add', (u'', b'a-root-id', 'directory', None)),
2057
('add', (u'a', b'a-id', 'file', b'content\n')),
2058
('add', (u'c', b'c-id', 'file', b'content\n'))],
2059
revision_id=b'A-id')
2060
builder.build_snapshot([b'A-id'],
2061
[('rename', ('c', 'b'))],
2062
revision_id=b'B-id')
2063
builder.build_snapshot([b'A-id'],
2064
[('rename', ('c', 'b'))],
2065
revision_id=b'C-id')
2066
builder.build_snapshot([b'C-id', b'B-id'],
2067
[('modify', ('a', b'new-content\n')),
2068
('modify', ('b', b'new-content\n'))],
2069
revision_id=b'E-id')
2070
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2071
merge_obj = self.make_merge_obj(builder, b'E-id',
1978
builder.build_snapshot('A-id', None,
1979
[('add', (u'', 'a-root-id', 'directory', None)),
1980
('add', (u'a', 'a-id', 'file', 'content\n')),
1981
('add', (u'c', 'c-id', 'file', 'content\n'))])
1982
builder.build_snapshot('B-id', ['A-id'],
1983
[('rename', ('c', 'b'))])
1984
builder.build_snapshot('C-id', ['A-id'],
1985
[('rename', ('c', 'b'))])
1986
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1987
[('modify', ('a-id', 'new-content\n')),
1988
('modify', ('c-id', 'new-content\n'))])
1989
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1990
merge_obj = self.make_merge_obj(builder, 'E-id',
2072
1991
interesting_files=['c'])
2073
1992
entries = list(merge_obj._entries_lca())
2074
root_id = b'a-root-id'
2075
self.assertEqual([(b'c-id', True,
2076
((u'c', [u'b', u'b']), u'b', u'b'),
1993
root_id = 'a-root-id'
1994
self.assertEqual([('c-id', True,
2077
1995
((root_id, [root_id, root_id]), root_id, root_id),
2078
1996
((u'c', [u'b', u'b']), u'b', u'b'),
2079
1997
((False, [False, False]), False, False)),
2082
2000
def test_interesting_file_in_lca(self):
2083
2001
# This renamed the file, but it should still match the entry in LCA
2084
2002
builder = self.get_builder()
2085
builder.build_snapshot(None,
2086
[('add', (u'', b'a-root-id', 'directory', None)),
2087
('add', (u'a', b'a-id', 'file', b'content\n')),
2088
('add', (u'b', b'b-id', 'file', b'content\n'))],
2089
revision_id=b'A-id')
2090
builder.build_snapshot([b'A-id'],
2091
[('rename', ('b', 'c'))], revision_id=b'B-id')
2092
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2093
builder.build_snapshot([b'C-id', b'B-id'],
2094
[('modify', ('a', b'new-content\n')),
2095
('modify', ('b', b'new-content\n'))],
2096
revision_id=b'E-id')
2097
builder.build_snapshot([b'B-id', b'C-id'],
2098
[('rename', ('c', 'b'))], revision_id=b'D-id')
2099
merge_obj = self.make_merge_obj(builder, b'E-id',
2003
builder.build_snapshot('A-id', None,
2004
[('add', (u'', 'a-root-id', 'directory', None)),
2005
('add', (u'a', 'a-id', 'file', 'content\n')),
2006
('add', (u'b', 'b-id', 'file', 'content\n'))])
2007
builder.build_snapshot('B-id', ['A-id'],
2008
[('rename', ('b', 'c'))])
2009
builder.build_snapshot('C-id', ['A-id'], [])
2010
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2011
[('modify', ('a-id', 'new-content\n')),
2012
('modify', ('b-id', 'new-content\n'))])
2013
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2014
[('rename', ('c', 'b'))])
2015
merge_obj = self.make_merge_obj(builder, 'E-id',
2100
2016
interesting_files=['c'])
2101
2017
entries = list(merge_obj._entries_lca())
2102
root_id = b'a-root-id'
2103
self.assertEqual([(b'b-id', True,
2104
((u'b', [u'c', u'b']), u'b', u'b'),
2018
root_id = 'a-root-id'
2019
self.assertEqual([('b-id', True,
2105
2020
((root_id, [root_id, root_id]), root_id, root_id),
2106
2021
((u'b', [u'c', u'b']), u'b', u'b'),
2107
2022
((False, [False, False]), False, False)),
2110
def test_interesting_files(self):
2025
def test_interesting_ids(self):
2111
2026
# Two files modified, but we should filter one of them
2112
2027
builder = self.get_builder()
2113
builder.build_snapshot(None,
2114
[('add', (u'', b'a-root-id', 'directory', None)),
2115
('add', (u'a', b'a-id', 'file', b'content\n')),
2116
('add', (u'b', b'b-id', 'file', b'content\n'))],
2117
revision_id=b'A-id')
2118
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2119
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2120
builder.build_snapshot([b'C-id', b'B-id'],
2121
[('modify', ('a', b'new-content\n')),
2122
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2123
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2124
merge_obj = self.make_merge_obj(builder, b'E-id',
2125
interesting_files=['b'])
2028
builder.build_snapshot('A-id', None,
2029
[('add', (u'', 'a-root-id', 'directory', None)),
2030
('add', (u'a', 'a-id', 'file', 'content\n')),
2031
('add', (u'b', 'b-id', 'file', 'content\n'))])
2032
builder.build_snapshot('B-id', ['A-id'], [])
2033
builder.build_snapshot('C-id', ['A-id'], [])
2034
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2035
[('modify', ('a-id', 'new-content\n')),
2036
('modify', ('b-id', 'new-content\n'))])
2037
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2038
merge_obj = self.make_merge_obj(builder, 'E-id',
2039
interesting_ids=['b-id'])
2126
2040
entries = list(merge_obj._entries_lca())
2127
root_id = b'a-root-id'
2128
self.assertEqual([(b'b-id', True,
2129
((u'b', [u'b', u'b']), u'b', u'b'),
2041
root_id = 'a-root-id'
2042
self.assertEqual([('b-id', True,
2130
2043
((root_id, [root_id, root_id]), root_id, root_id),
2131
2044
((u'b', [u'b', u'b']), u'b', u'b'),
2132
2045
((False, [False, False]), False, False)),
2136
2050
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2408
2315
wt.lock_write()
2409
2316
self.addCleanup(wt.unlock)
2410
2317
os.symlink('bar', 'path/foo')
2411
wt.add(['foo'], [b'foo-id'])
2412
wt.commit('A add symlink', rev_id=b'A-id')
2318
wt.add(['foo'], ['foo-id'])
2319
wt.commit('A add symlink', rev_id='A-id')
2413
2320
wt.rename_one('foo', 'barry')
2414
wt.commit('B foo => barry', rev_id=b'B-id')
2415
wt.set_last_revision(b'A-id')
2416
wt.branch.set_last_revision_info(1, b'A-id')
2321
wt.commit('B foo => barry', rev_id='B-id')
2322
wt.set_last_revision('A-id')
2323
wt.branch.set_last_revision_info(1, 'A-id')
2418
wt.commit('C', rev_id=b'C-id')
2419
wt.merge_from_branch(wt.branch, b'B-id')
2420
self.assertEqual('barry', wt.id2path(b'foo-id'))
2421
self.assertEqual('bar', wt.get_symlink_target('barry'))
2422
wt.commit('E merges C & B', rev_id=b'E-id')
2325
wt.commit('C', rev_id='C-id')
2326
wt.merge_from_branch(wt.branch, 'B-id')
2327
self.assertEqual('barry', wt.id2path('foo-id'))
2328
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2329
wt.commit('E merges C & B', rev_id='E-id')
2423
2330
wt.rename_one('barry', 'blah')
2424
wt.commit('F barry => blah', rev_id=b'F-id')
2425
wt.set_last_revision(b'B-id')
2426
wt.branch.set_last_revision_info(2, b'B-id')
2331
wt.commit('F barry => blah', rev_id='F-id')
2332
wt.set_last_revision('B-id')
2333
wt.branch.set_last_revision_info(2, 'B-id')
2428
wt.merge_from_branch(wt.branch, b'C-id')
2429
wt.commit('D merges B & C', rev_id=b'D-id')
2430
self.assertEqual('barry', wt.id2path(b'foo-id'))
2335
wt.merge_from_branch(wt.branch, 'C-id')
2336
wt.commit('D merges B & C', rev_id='D-id')
2337
self.assertEqual('barry', wt.id2path('foo-id'))
2431
2338
# Check the output of the Merger object directly
2432
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2339
merger = _mod_merge.Merger.from_revision_ids(None,
2433
2341
merger.merge_type = _mod_merge.Merge3Merger
2434
2342
merge_obj = merger.make_merger()
2435
2343
root_id = wt.path2id('')
2436
2344
entries = list(merge_obj._entries_lca())
2437
2345
# No content change, just a path change
2438
self.assertEqual([(b'foo-id', False,
2439
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2346
self.assertEqual([('foo-id', False,
2440
2347
((root_id, [root_id, root_id]), root_id, root_id),
2441
2348
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2442
2349
((False, [False, False]), False, False)),
2444
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2351
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2445
2352
self.assertEqual(0, conflicts)
2446
self.assertEqual('blah', wt.id2path(b'foo-id'))
2353
self.assertEqual('blah', wt.id2path('foo-id'))
2448
2355
def test_symlink_no_content_change(self):
2449
2356
self.requireFeature(features.SymlinkFeature)
2566
2474
wt.lock_write()
2567
2475
self.addCleanup(wt.unlock)
2568
2476
os.symlink('bar', 'path/foo')
2569
wt.add(['foo'], [b'foo-id'])
2570
wt.commit('add symlink', rev_id=b'A-id')
2477
wt.add(['foo'], ['foo-id'])
2478
wt.commit('add symlink', rev_id='A-id')
2571
2479
os.remove('path/foo')
2572
2480
os.symlink('baz', 'path/foo')
2573
wt.commit('foo => baz', rev_id=b'B-id')
2574
wt.set_last_revision(b'A-id')
2575
wt.branch.set_last_revision_info(1, b'A-id')
2481
wt.commit('foo => baz', rev_id='B-id')
2482
wt.set_last_revision('A-id')
2483
wt.branch.set_last_revision_info(1, 'A-id')
2577
wt.commit('C', rev_id=b'C-id')
2578
wt.merge_from_branch(wt.branch, b'B-id')
2579
self.assertEqual('baz', wt.get_symlink_target('foo'))
2580
wt.commit('E merges C & B', rev_id=b'E-id')
2485
wt.commit('C', rev_id='C-id')
2486
wt.merge_from_branch(wt.branch, 'B-id')
2487
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2488
wt.commit('E merges C & B', rev_id='E-id')
2581
2489
os.remove('path/foo')
2582
2490
os.symlink('bing', 'path/foo')
2583
wt.commit('F foo => bing', rev_id=b'F-id')
2584
wt.set_last_revision(b'B-id')
2585
wt.branch.set_last_revision_info(2, b'B-id')
2491
wt.commit('F foo => bing', rev_id='F-id')
2492
wt.set_last_revision('B-id')
2493
wt.branch.set_last_revision_info(2, 'B-id')
2587
wt.merge_from_branch(wt.branch, b'C-id')
2588
wt.commit('D merges B & C', rev_id=b'D-id')
2589
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2495
wt.merge_from_branch(wt.branch, 'C-id')
2496
wt.commit('D merges B & C', rev_id='D-id')
2497
wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2590
2498
wt_base.lock_read()
2591
2499
self.addCleanup(wt_base.unlock)
2592
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2500
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2593
2501
wt_lca1.lock_read()
2594
2502
self.addCleanup(wt_lca1.unlock)
2595
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2503
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2596
2504
wt_lca2.lock_read()
2597
2505
self.addCleanup(wt_lca2.unlock)
2598
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2506
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2599
2507
wt_other.lock_read()
2600
2508
self.addCleanup(wt_other.unlock)
2601
2509
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2602
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2510
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2603
2511
entries = list(merge_obj._entries_lca())
2604
2512
root_id = wt.path2id('')
2605
self.assertEqual([(b'foo-id', True,
2606
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2513
self.assertEqual([('foo-id', True,
2607
2514
((root_id, [root_id, root_id]), root_id, root_id),
2608
2515
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2609
2516
((False, [False, False]), False, False)),
2612
2519
def test_other_reverted_path_to_base(self):
2613
2520
# A Path at 'foo'
2621
2528
# F Path at 'foo'
2622
2529
builder = self.get_builder()
2623
builder.build_snapshot(None,
2624
[('add', (u'', b'a-root-id', 'directory', None)),
2625
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2626
revision_id=b'A-id')
2627
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2628
builder.build_snapshot([b'A-id'],
2629
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2630
builder.build_snapshot([b'C-id', b'B-id'],
2631
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2632
builder.build_snapshot([b'E-id'],
2633
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2634
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2635
wt, conflicts = self.do_merge(builder, b'F-id')
2530
builder.build_snapshot('A-id', None,
2531
[('add', (u'', 'a-root-id', 'directory', None)),
2532
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2533
builder.build_snapshot('C-id', ['A-id'], [])
2534
builder.build_snapshot('B-id', ['A-id'],
2535
[('rename', ('foo', 'bar'))])
2536
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2537
[('rename', ('foo', 'bar'))]) # merge the rename
2538
builder.build_snapshot('F-id', ['E-id'],
2539
[('rename', ('bar', 'foo'))]) # Rename back to BASE
2540
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2541
wt, conflicts = self.do_merge(builder, 'F-id')
2636
2542
self.assertEqual(0, conflicts)
2637
self.assertEqual('foo', wt.id2path(b'foo-id'))
2543
self.assertEqual('foo', wt.id2path('foo-id'))
2639
2545
def test_other_reverted_content_to_base(self):
2640
2546
builder = self.get_builder()
2641
builder.build_snapshot(None,
2642
[('add', (u'', b'a-root-id', 'directory', None)),
2643
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2644
revision_id=b'A-id')
2645
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2646
builder.build_snapshot([b'A-id'],
2647
[('modify', ('foo', b'B content\n'))],
2648
revision_id=b'B-id')
2649
builder.build_snapshot([b'C-id', b'B-id'],
2650
[('modify', ('foo', b'B content\n'))],
2651
revision_id=b'E-id') # merge the content
2652
builder.build_snapshot([b'E-id'],
2653
[('modify', ('foo', b'base content\n'))],
2654
revision_id=b'F-id') # Revert back to BASE
2655
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2656
wt, conflicts = self.do_merge(builder, b'F-id')
2547
builder.build_snapshot('A-id', None,
2548
[('add', (u'', 'a-root-id', 'directory', None)),
2549
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2550
builder.build_snapshot('C-id', ['A-id'], [])
2551
builder.build_snapshot('B-id', ['A-id'],
2552
[('modify', ('foo-id', 'B content\n'))])
2553
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2554
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2555
builder.build_snapshot('F-id', ['E-id'],
2556
[('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2557
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2558
wt, conflicts = self.do_merge(builder, 'F-id')
2657
2559
self.assertEqual(0, conflicts)
2658
2560
# TODO: We need to use the per-file graph to properly select a BASE
2659
2561
# before this will work. Or at least use the LCA trees to find
2660
2562
# the appropriate content base. (which is B, not A).
2661
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2563
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2663
2565
def test_other_modified_content(self):
2664
2566
builder = self.get_builder()
2665
builder.build_snapshot(None,
2666
[('add', (u'', b'a-root-id', 'directory', None)),
2667
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2668
revision_id=b'A-id')
2669
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2670
builder.build_snapshot([b'A-id'],
2671
[('modify', ('foo', b'B content\n'))],
2672
revision_id=b'B-id')
2673
builder.build_snapshot([b'C-id', b'B-id'],
2674
[('modify', ('foo', b'B content\n'))],
2675
revision_id=b'E-id') # merge the content
2676
builder.build_snapshot([b'E-id'],
2677
[('modify', ('foo', b'F content\n'))],
2678
revision_id=b'F-id') # Override B content
2679
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2680
wt, conflicts = self.do_merge(builder, b'F-id')
2567
builder.build_snapshot('A-id', None,
2568
[('add', (u'', 'a-root-id', 'directory', None)),
2569
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2570
builder.build_snapshot('C-id', ['A-id'], [])
2571
builder.build_snapshot('B-id', ['A-id'],
2572
[('modify', ('foo-id', 'B content\n'))])
2573
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2574
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2575
builder.build_snapshot('F-id', ['E-id'],
2576
[('modify', ('foo-id', 'F content\n'))]) # Override B content
2577
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2578
wt, conflicts = self.do_merge(builder, 'F-id')
2681
2579
self.assertEqual(0, conflicts)
2682
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2580
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2684
2582
def test_all_wt(self):
2685
2583
"""Check behavior if all trees are Working Trees."""
2693
2591
# D E E updates content, renames 'b' => 'c'
2694
2592
builder = self.get_builder()
2695
builder.build_snapshot(None,
2696
[('add', (u'', b'a-root-id', 'directory', None)),
2697
('add', (u'a', b'a-id', 'file', b'base content\n')),
2698
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2699
revision_id=b'A-id')
2700
builder.build_snapshot([b'A-id'],
2701
[('modify', ('foo', b'B content\n'))],
2702
revision_id=b'B-id')
2703
builder.build_snapshot([b'A-id'],
2704
[('rename', ('a', 'b'))],
2705
revision_id=b'C-id')
2706
builder.build_snapshot([b'C-id', b'B-id'],
2707
[('rename', ('b', 'c')),
2708
('modify', ('foo', b'E content\n'))],
2709
revision_id=b'E-id')
2710
builder.build_snapshot([b'B-id', b'C-id'],
2711
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2593
builder.build_snapshot('A-id', None,
2594
[('add', (u'', 'a-root-id', 'directory', None)),
2595
('add', (u'a', 'a-id', 'file', 'base content\n')),
2596
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2597
builder.build_snapshot('B-id', ['A-id'],
2598
[('modify', ('foo-id', 'B content\n'))])
2599
builder.build_snapshot('C-id', ['A-id'],
2600
[('rename', ('a', 'b'))])
2601
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2602
[('rename', ('b', 'c')),
2603
('modify', ('foo-id', 'E content\n'))])
2604
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2605
[('rename', ('a', 'b'))]) # merged change
2712
2606
wt_this = self.get_wt_from_builder(builder)
2713
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2607
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2714
2608
wt_base.lock_read()
2715
2609
self.addCleanup(wt_base.unlock)
2716
wt_lca1 = wt_this.controldir.sprout(
2717
'b-tree', b'B-id').open_workingtree()
2610
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2718
2611
wt_lca1.lock_read()
2719
2612
self.addCleanup(wt_lca1.unlock)
2720
wt_lca2 = wt_this.controldir.sprout(
2721
'c-tree', b'C-id').open_workingtree()
2613
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2722
2614
wt_lca2.lock_read()
2723
2615
self.addCleanup(wt_lca2.unlock)
2724
wt_other = wt_this.controldir.sprout(
2725
'other', b'E-id').open_workingtree()
2616
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2726
2617
wt_other.lock_read()
2727
2618
self.addCleanup(wt_other.unlock)
2728
2619
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2729
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2620
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2730
2621
entries = list(merge_obj._entries_lca())
2731
root_id = b'a-root-id'
2732
self.assertEqual([(b'a-id', False,
2733
((u'a', [u'a', u'b']), u'c', u'b'),
2734
((root_id, [root_id, root_id]), root_id, root_id),
2735
((u'a', [u'a', u'b']), u'c', u'b'),
2736
((False, [False, False]), False, False)),
2738
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2739
((root_id, [root_id, root_id]), root_id, root_id),
2740
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2741
((False, [False, False]), False, False)),
2622
root_id = 'a-root-id'
2623
self.assertEqual([('a-id', False,
2624
((root_id, [root_id, root_id]), root_id, root_id),
2625
((u'a', [u'a', u'b']), u'c', u'b'),
2626
((False, [False, False]), False, False)),
2628
((root_id, [root_id, root_id]), root_id, root_id),
2629
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2630
((False, [False, False]), False, False)),
2744
2633
def test_nested_tree_unmodified(self):
2745
2634
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2746
2635
# 'tree-reference'
2747
2636
wt = self.make_branch_and_tree('tree',
2748
format='development-subtree')
2637
format='development-subtree')
2749
2638
wt.lock_write()
2750
2639
self.addCleanup(wt.unlock)
2751
2640
sub_tree = self.make_branch_and_tree('tree/sub-tree',
2752
format='development-subtree')
2753
wt.set_root_id(b'a-root-id')
2754
sub_tree.set_root_id(b'sub-tree-root')
2755
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2641
format='development-subtree')
2642
wt.set_root_id('a-root-id')
2643
sub_tree.set_root_id('sub-tree-root')
2644
self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2756
2645
sub_tree.add('file')
2757
sub_tree.commit('foo', rev_id=b'sub-A-id')
2646
sub_tree.commit('foo', rev_id='sub-A-id')
2758
2647
wt.add_reference(sub_tree)
2759
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2648
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2760
2649
# Now create a criss-cross merge in the parent, without modifying the
2762
wt.commit('B', rev_id=b'B-id', recursive=None)
2763
wt.set_last_revision(b'A-id')
2764
wt.branch.set_last_revision_info(1, b'A-id')
2765
wt.commit('C', rev_id=b'C-id', recursive=None)
2766
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2767
wt.commit('E', rev_id=b'E-id', recursive=None)
2768
wt.set_parent_ids([b'B-id', b'C-id'])
2769
wt.branch.set_last_revision_info(2, b'B-id')
2770
wt.commit('D', rev_id=b'D-id', recursive=None)
2651
wt.commit('B', rev_id='B-id', recursive=None)
2652
wt.set_last_revision('A-id')
2653
wt.branch.set_last_revision_info(1, 'A-id')
2654
wt.commit('C', rev_id='C-id', recursive=None)
2655
wt.merge_from_branch(wt.branch, to_revision='B-id')
2656
wt.commit('E', rev_id='E-id', recursive=None)
2657
wt.set_parent_ids(['B-id', 'C-id'])
2658
wt.branch.set_last_revision_info(2, 'B-id')
2659
wt.commit('D', rev_id='D-id', recursive=None)
2772
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2661
merger = _mod_merge.Merger.from_revision_ids(None,
2773
2663
merger.merge_type = _mod_merge.Merge3Merger
2774
2664
merge_obj = merger.make_merger()
2775
2665
entries = list(merge_obj._entries_lca())
2817
2708
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2818
2709
# 'tree-reference'
2819
2710
wt = self.make_branch_and_tree('tree',
2820
format='development-subtree')
2711
format='development-subtree')
2821
2712
wt.lock_write()
2822
2713
self.addCleanup(wt.unlock)
2823
2714
sub_tree = self.make_branch_and_tree('tree/sub',
2824
format='development-subtree')
2825
wt.set_root_id(b'a-root-id')
2826
sub_tree.set_root_id(b'sub-tree-root')
2827
self.build_tree_contents([('tree/sub/file', b'text1')])
2715
format='development-subtree')
2716
wt.set_root_id('a-root-id')
2717
sub_tree.set_root_id('sub-tree-root')
2718
self.build_tree_contents([('tree/sub/file', 'text1')])
2828
2719
sub_tree.add('file')
2829
sub_tree.commit('foo', rev_id=b'sub-A-id')
2720
sub_tree.commit('foo', rev_id='sub-A-id')
2830
2721
wt.add_reference(sub_tree)
2831
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2722
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2832
2723
# Now create a criss-cross merge in the parent, without modifying the
2834
wt.commit('B', rev_id=b'B-id', recursive=None)
2835
wt.set_last_revision(b'A-id')
2836
wt.branch.set_last_revision_info(1, b'A-id')
2837
wt.commit('C', rev_id=b'C-id', recursive=None)
2838
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2725
wt.commit('B', rev_id='B-id', recursive=None)
2726
wt.set_last_revision('A-id')
2727
wt.branch.set_last_revision_info(1, 'A-id')
2728
wt.commit('C', rev_id='C-id', recursive=None)
2729
wt.merge_from_branch(wt.branch, to_revision='B-id')
2839
2730
wt.rename_one('sub', 'alt_sub')
2840
wt.commit('E', rev_id=b'E-id', recursive=None)
2841
wt.set_last_revision(b'B-id')
2731
wt.commit('E', rev_id='E-id', recursive=None)
2732
wt.set_last_revision('B-id')
2843
wt.set_parent_ids([b'B-id', b'C-id'])
2844
wt.branch.set_last_revision_info(2, b'B-id')
2845
wt.commit('D', rev_id=b'D-id', recursive=None)
2734
wt.set_parent_ids(['B-id', 'C-id'])
2735
wt.branch.set_last_revision_info(2, 'B-id')
2736
wt.commit('D', rev_id='D-id', recursive=None)
2847
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2738
merger = _mod_merge.Merger.from_revision_ids(None,
2848
2740
merger.merge_type = _mod_merge.Merge3Merger
2849
2741
merge_obj = merger.make_merger()
2850
2742
entries = list(merge_obj._entries_lca())
2851
root_id = b'a-root-id'
2852
self.assertEqual([(b'sub-tree-root', False,
2853
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2743
root_id = 'a-root-id'
2744
self.assertEqual([('sub-tree-root', False,
2854
2745
((root_id, [root_id, root_id]), root_id, root_id),
2855
2746
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2856
2747
((False, [False, False]), False, False)),
2859
2750
def test_nested_tree_subtree_renamed_and_modified(self):
2860
2751
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2861
2752
# 'tree-reference'
2862
2753
wt = self.make_branch_and_tree('tree',
2863
format='development-subtree')
2754
format='development-subtree')
2864
2755
wt.lock_write()
2865
2756
self.addCleanup(wt.unlock)
2866
2757
sub_tree = self.make_branch_and_tree('tree/sub',
2867
format='development-subtree')
2868
wt.set_root_id(b'a-root-id')
2869
sub_tree.set_root_id(b'sub-tree-root')
2870
self.build_tree_contents([('tree/sub/file', b'text1')])
2758
format='development-subtree')
2759
wt.set_root_id('a-root-id')
2760
sub_tree.set_root_id('sub-tree-root')
2761
self.build_tree_contents([('tree/sub/file', 'text1')])
2871
2762
sub_tree.add('file')
2872
sub_tree.commit('foo', rev_id=b'sub-A-id')
2763
sub_tree.commit('foo', rev_id='sub-A-id')
2873
2764
wt.add_reference(sub_tree)
2874
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2765
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2875
2766
# Now create a criss-cross merge in the parent, without modifying the
2877
wt.commit('B', rev_id=b'B-id', recursive=None)
2878
wt.set_last_revision(b'A-id')
2879
wt.branch.set_last_revision_info(1, b'A-id')
2880
wt.commit('C', rev_id=b'C-id', recursive=None)
2881
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2882
self.build_tree_contents([('tree/sub/file', b'text2')])
2883
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2768
wt.commit('B', rev_id='B-id', recursive=None)
2769
wt.set_last_revision('A-id')
2770
wt.branch.set_last_revision_info(1, 'A-id')
2771
wt.commit('C', rev_id='C-id', recursive=None)
2772
wt.merge_from_branch(wt.branch, to_revision='B-id')
2773
self.build_tree_contents([('tree/sub/file', 'text2')])
2774
sub_tree.commit('modify contents', rev_id='sub-B-id')
2884
2775
wt.rename_one('sub', 'alt_sub')
2885
wt.commit('E', rev_id=b'E-id', recursive=None)
2886
wt.set_last_revision(b'B-id')
2776
wt.commit('E', rev_id='E-id', recursive=None)
2777
wt.set_last_revision('B-id')
2888
wt.set_parent_ids([b'B-id', b'C-id'])
2889
wt.branch.set_last_revision_info(2, b'B-id')
2890
wt.commit('D', rev_id=b'D-id', recursive=None)
2779
wt.set_parent_ids(['B-id', 'C-id'])
2780
wt.branch.set_last_revision_info(2, 'B-id')
2781
wt.commit('D', rev_id='D-id', recursive=None)
2892
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2783
merger = _mod_merge.Merger.from_revision_ids(None,
2893
2785
merger.merge_type = _mod_merge.Merge3Merger
2894
2786
merge_obj = merger.make_merger()
2895
2787
entries = list(merge_obj._entries_lca())
2896
root_id = b'a-root-id'
2897
self.assertEqual([(b'sub-tree-root', False,
2898
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2788
root_id = 'a-root-id'
2789
self.assertEqual([('sub-tree-root', False,
2899
2790
((root_id, [root_id, root_id]), root_id, root_id),
2900
2791
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2901
2792
((False, [False, False]), False, False)),
2905
2796
class TestLCAMultiWay(tests.TestCase):
2907
2798
def assertLCAMultiWay(self, expected, base, lcas, other, this,
2908
2799
allow_overriding_lca=True):
2909
2800
self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2910
(base, lcas), other, this,
2911
allow_overriding_lca=allow_overriding_lca))
2801
(base, lcas), other, this,
2802
allow_overriding_lca=allow_overriding_lca))
2913
2804
def test_other_equal_equal_lcas(self):
2914
2805
"""Test when OTHER=LCA and all LCAs are identical."""
2915
2806
self.assertLCAMultiWay('this',
2916
'bval', ['bval', 'bval'], 'bval', 'bval')
2917
self.assertLCAMultiWay('this',
2918
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2919
self.assertLCAMultiWay('this',
2920
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2921
self.assertLCAMultiWay('this',
2922
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2923
self.assertLCAMultiWay('this',
2924
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2807
'bval', ['bval', 'bval'], 'bval', 'bval')
2808
self.assertLCAMultiWay('this',
2809
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2810
self.assertLCAMultiWay('this',
2811
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2812
self.assertLCAMultiWay('this',
2813
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2814
self.assertLCAMultiWay('this',
2815
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2926
2817
def test_other_equal_this(self):
2927
2818
"""Test when other and this are identical."""
2928
2819
self.assertLCAMultiWay('this',
2929
'bval', ['bval', 'bval'], 'oval', 'oval')
2930
self.assertLCAMultiWay('this',
2931
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2932
self.assertLCAMultiWay('this',
2933
'bval', ['cval', 'dval'], 'oval', 'oval')
2934
self.assertLCAMultiWay('this',
2935
'bval', [None, 'lcaval'], 'oval', 'oval')
2936
self.assertLCAMultiWay('this',
2937
None, [None, 'lcaval'], 'oval', 'oval')
2938
self.assertLCAMultiWay('this',
2939
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2940
self.assertLCAMultiWay('this',
2941
None, ['cval', 'dval'], 'oval', 'oval')
2942
self.assertLCAMultiWay('this',
2943
None, ['cval', 'dval'], None, None)
2944
self.assertLCAMultiWay('this',
2945
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2820
'bval', ['bval', 'bval'], 'oval', 'oval')
2821
self.assertLCAMultiWay('this',
2822
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2823
self.assertLCAMultiWay('this',
2824
'bval', ['cval', 'dval'], 'oval', 'oval')
2825
self.assertLCAMultiWay('this',
2826
'bval', [None, 'lcaval'], 'oval', 'oval')
2827
self.assertLCAMultiWay('this',
2828
None, [None, 'lcaval'], 'oval', 'oval')
2829
self.assertLCAMultiWay('this',
2830
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2831
self.assertLCAMultiWay('this',
2832
None, ['cval', 'dval'], 'oval', 'oval')
2833
self.assertLCAMultiWay('this',
2834
None, ['cval', 'dval'], None, None)
2835
self.assertLCAMultiWay('this',
2836
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2947
2838
def test_no_lcas(self):
2948
2839
self.assertLCAMultiWay('this',
2949
'bval', [], 'bval', 'tval')
2840
'bval', [], 'bval', 'tval')
2950
2841
self.assertLCAMultiWay('other',
2951
'bval', [], 'oval', 'bval')
2842
'bval', [], 'oval', 'bval')
2952
2843
self.assertLCAMultiWay('conflict',
2953
'bval', [], 'oval', 'tval')
2844
'bval', [], 'oval', 'tval')
2954
2845
self.assertLCAMultiWay('this',
2955
'bval', [], 'oval', 'oval')
2846
'bval', [], 'oval', 'oval')
2957
2848
def test_lca_supersedes_other_lca(self):
2958
2849
"""If one lca == base, the other lca takes precedence"""
2959
2850
self.assertLCAMultiWay('this',
2960
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2851
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2961
2852
self.assertLCAMultiWay('this',
2962
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2853
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2963
2854
# This is actually considered a 'revert' because the 'lcaval' in LCAS
2964
2855
# supersedes the BASE val (in the other LCA) but then OTHER reverts it
2965
2856
# back to bval.
2966
2857
self.assertLCAMultiWay('other',
2967
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2858
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2968
2859
self.assertLCAMultiWay('conflict',
2969
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2860
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2971
2862
def test_other_and_this_pick_different_lca(self):
2972
2863
# OTHER and THIS resolve the lca conflict in different ways
2973
2864
self.assertLCAMultiWay('conflict',
2974
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2975
self.assertLCAMultiWay('conflict',
2976
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2977
self.assertLCAMultiWay('conflict',
2978
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2865
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2866
self.assertLCAMultiWay('conflict',
2867
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2868
self.assertLCAMultiWay('conflict',
2869
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2980
2871
def test_other_in_lca(self):
2981
2872
# OTHER takes a value of one of the LCAs, THIS takes a new value, which
2982
2873
# theoretically supersedes both LCA values and 'wins'
2983
self.assertLCAMultiWay(
2984
'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2985
self.assertLCAMultiWay(
2986
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
2988
self.assertLCAMultiWay('conflict',
2990
'lca2val'], 'lca1val', 'newval',
2991
allow_overriding_lca=False)
2992
self.assertLCAMultiWay('conflict',
2993
'bval', ['lca1val', 'lca2val',
2994
'lca3val'], 'lca1val', 'newval',
2995
allow_overriding_lca=False)
2874
self.assertLCAMultiWay('this',
2875
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2876
self.assertLCAMultiWay('this',
2877
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
2878
self.assertLCAMultiWay('conflict',
2879
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2880
allow_overriding_lca=False)
2881
self.assertLCAMultiWay('conflict',
2882
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2883
allow_overriding_lca=False)
2996
2884
# THIS reverted back to BASE, but that is an explicit supersede of all
2998
self.assertLCAMultiWay(
2999
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3001
self.assertLCAMultiWay(
3002
'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3003
self.assertLCAMultiWay('conflict',
3004
'bval', ['lca1val', 'lca2val',
3005
'lca3val'], 'lca1val', 'bval',
3006
allow_overriding_lca=False)
3007
self.assertLCAMultiWay('conflict',
3008
'bval', ['lca1val', 'lca2val',
3009
'bval'], 'lca1val', 'bval',
3010
allow_overriding_lca=False)
2886
self.assertLCAMultiWay('this',
2887
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2888
self.assertLCAMultiWay('this',
2889
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
2890
self.assertLCAMultiWay('conflict',
2891
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2892
allow_overriding_lca=False)
2893
self.assertLCAMultiWay('conflict',
2894
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2895
allow_overriding_lca=False)
3012
2897
def test_this_in_lca(self):
3013
2898
# THIS takes a value of one of the LCAs, OTHER takes a new value, which
3014
2899
# theoretically supersedes both LCA values and 'wins'
3015
self.assertLCAMultiWay(
3016
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3017
self.assertLCAMultiWay(
3018
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3019
self.assertLCAMultiWay('conflict',
3021
'lca2val'], 'oval', 'lca1val',
3022
allow_overriding_lca=False)
3023
self.assertLCAMultiWay('conflict',
3025
'lca2val'], 'oval', 'lca2val',
3026
allow_overriding_lca=False)
2900
self.assertLCAMultiWay('other',
2901
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2902
self.assertLCAMultiWay('other',
2903
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
2904
self.assertLCAMultiWay('conflict',
2905
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2906
allow_overriding_lca=False)
2907
self.assertLCAMultiWay('conflict',
2908
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2909
allow_overriding_lca=False)
3027
2910
# OTHER reverted back to BASE, but that is an explicit supersede of all
3029
self.assertLCAMultiWay(
3030
'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
3032
self.assertLCAMultiWay(
3033
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
3034
'bval', 'lca3val', allow_overriding_lca=False)
2912
self.assertLCAMultiWay('other',
2913
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
2914
self.assertLCAMultiWay('conflict',
2915
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2916
allow_overriding_lca=False)
3036
2918
def test_all_differ(self):
3037
self.assertLCAMultiWay(
3038
'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3039
self.assertLCAMultiWay(
3040
'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
3042
self.assertLCAMultiWay(
3043
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
2919
self.assertLCAMultiWay('conflict',
2920
'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2921
self.assertLCAMultiWay('conflict',
2922
'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2923
self.assertLCAMultiWay('conflict',
2924
'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
3047
2927
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3257
3135
project_wt.lock_read()
3258
3136
self.addCleanup(project_wt.unlock)
3259
3137
# The r1-lib1 revision should be merged into this one
3260
self.assertEqual([b'r1-project', b'r1-lib1'],
3261
project_wt.get_parent_ids())
3138
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3262
3139
new_lib1_id = project_wt.path2id('lib1')
3263
3140
self.assertNotEqual(None, new_lib1_id)
3264
3141
self.assertTreeEntriesEqual(
3265
3142
[('', root_id),
3266
('README', b'project-README-id'),
3267
('dir', b'project-dir-id'),
3143
('README', 'project-README-id'),
3144
('dir', 'project-dir-id'),
3268
3145
('lib1', new_lib1_id),
3269
('dir/file.c', b'project-file.c-id'),
3270
('lib1/Makefile', b'lib1-Makefile-id'),
3271
('lib1/README', b'lib1-README-id'),
3272
('lib1/foo.c', b'lib1-foo.c-id'),
3146
('dir/file.c', 'project-file.c-id'),
3147
('lib1/Makefile', 'lib1-Makefile-id'),
3148
('lib1/README', 'lib1-README-id'),
3149
('lib1/foo.c', 'lib1-foo.c-id'),
3275
3152
def test_name_conflict(self):
3276
3153
"""When the target directory name already exists a conflict is
3277
3154
generated and the original directory is renamed to foo.moved.
3279
3156
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3280
self.setup_simple_branch('src', ['README'])
3157
src_wt = self.setup_simple_branch('src', ['README'])
3281
3158
conflicts = self.do_merge_into('src', 'dest/dir')
3282
3159
self.assertEqual(1, conflicts)
3283
3160
dest_wt.lock_read()
3284
3161
self.addCleanup(dest_wt.unlock)
3285
3162
# The r1-lib1 revision should be merged into this one
3286
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3163
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3287
3164
self.assertTreeEntriesEqual(
3288
[('', b'dest-root-id'),
3289
('dir', b'src-root-id'),
3290
('dir.moved', b'dest-dir-id'),
3291
('dir/README', b'src-README-id'),
3292
('dir.moved/file.txt', b'dest-file.txt-id'),
3165
[('', 'dest-root-id'),
3166
('dir', 'src-root-id'),
3167
('dir.moved', 'dest-dir-id'),
3168
('dir/README', 'src-README-id'),
3169
('dir.moved/file.txt', 'dest-file.txt-id'),
3295
3172
def test_file_id_conflict(self):
3296
3173
"""A conflict is generated if the merge-into adds a file (or other
3297
3174
inventory entry) with a file-id that already exists in the target tree.
3299
self.setup_simple_branch('dest', ['file.txt'])
3176
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3300
3177
# Make a second tree with a file-id that will clash with file.txt in
3302
3179
src_wt = self.make_branch_and_tree('src')
3303
3180
self.build_tree(['src/README'])
3304
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3305
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3181
src_wt.add(['README'], ids=['dest-file.txt-id'])
3182
src_wt.commit("Rev 1 of src.", rev_id='r1-src')
3306
3183
conflicts = self.do_merge_into('src', 'dest/dir')
3307
3184
# This is an edge case that shouldn't happen to users very often. So
3308
3185
# we don't care really about the exact presentation of the conflict,