357
364
'c', but not 'b'.
359
366
this_tree = self.make_branch_and_tree('this')
360
self.build_tree_contents([('this/file', "a\n")])
367
self.build_tree_contents([('this/file', b"a\n")])
361
368
this_tree.add('file')
362
369
this_tree.commit('rev1')
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')
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')
368
375
this_tree.lock_write()
369
376
self.addCleanup(this_tree.unlock)
370
377
return this_tree, other_tree
372
379
def test_weave_cherrypick(self):
373
380
this_tree, other_tree = self.prepare_cherrypick()
374
merger = _mod_merge.Merger.from_revision_ids(None,
375
this_tree, 'rev3b', 'rev2b', other_tree.branch)
381
merger = _mod_merge.Merger.from_revision_ids(
382
this_tree, b'rev3b', b'rev2b', other_tree.branch)
376
383
merger.merge_type = _mod_merge.WeaveMerger
377
384
merger.do_merge()
378
self.assertFileEqual('c\na\n', 'this/file')
385
self.assertFileEqual(b'c\na\n', 'this/file')
380
387
def test_weave_cannot_reverse_cherrypick(self):
381
388
this_tree, other_tree = self.prepare_cherrypick()
382
merger = _mod_merge.Merger.from_revision_ids(None,
383
this_tree, 'rev2b', 'rev3b', other_tree.branch)
389
merger = _mod_merge.Merger.from_revision_ids(
390
this_tree, b'rev2b', b'rev3b', other_tree.branch)
384
391
merger.merge_type = _mod_merge.WeaveMerger
385
392
self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
387
394
def test_merge3_can_reverse_cherrypick(self):
388
395
this_tree, other_tree = self.prepare_cherrypick()
389
merger = _mod_merge.Merger.from_revision_ids(None,
390
this_tree, 'rev2b', 'rev3b', other_tree.branch)
396
merger = _mod_merge.Merger.from_revision_ids(
397
this_tree, b'rev2b', b'rev3b', other_tree.branch)
391
398
merger.merge_type = _mod_merge.Merge3Merger
392
399
merger.do_merge()
394
401
def test_merge3_will_detect_cherrypick(self):
395
402
this_tree = self.make_branch_and_tree('this')
396
self.build_tree_contents([('this/file', "a\n")])
403
self.build_tree_contents([('this/file', b"a\n")])
397
404
this_tree.add('file')
398
405
this_tree.commit('rev1')
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')
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')
404
411
this_tree.lock_write()
405
412
self.addCleanup(this_tree.unlock)
407
merger = _mod_merge.Merger.from_revision_ids(None,
408
this_tree, 'rev3b', 'rev2b', other_tree.branch)
414
merger = _mod_merge.Merger.from_revision_ids(
415
this_tree, b'rev3b', b'rev2b', other_tree.branch)
409
416
merger.merge_type = _mod_merge.Merge3Merger
410
417
merger.do_merge()
411
self.assertFileEqual('a\n'
415
'>>>>>>> MERGE-SOURCE\n',
418
self.assertFileEqual(b'a\n'
422
b'>>>>>>> MERGE-SOURCE\n',
418
425
def test_merge_reverse_revision_range(self):
437
444
def test_make_merger(self):
438
445
this_tree = self.make_branch_and_tree('this')
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')
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')
443
450
this_tree.lock_write()
444
451
self.addCleanup(this_tree.unlock)
445
merger = _mod_merge.Merger.from_revision_ids(None,
446
this_tree, 'rev2b', other_branch=other_tree.branch)
452
merger = _mod_merge.Merger.from_revision_ids(
453
this_tree, b'rev2b', other_branch=other_tree.branch)
447
454
merger.merge_type = _mod_merge.Merge3Merger
448
455
tree_merger = merger.make_merger()
449
456
self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
450
self.assertEqual('rev2b',
451
tree_merger.other_tree.get_revision_id())
452
self.assertEqual('rev1',
453
tree_merger.base_tree.get_revision_id())
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())
454
461
self.assertEqual(other_tree.branch, tree_merger.other_branch)
456
463
def test_make_preview_transform(self):
457
464
this_tree = self.make_branch_and_tree('this')
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')
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')
466
473
this_tree.lock_write()
467
474
self.addCleanup(this_tree.unlock)
468
merger = _mod_merge.Merger.from_revision_ids(None,
469
this_tree, 'rev2b', other_branch=other_tree.branch)
475
merger = _mod_merge.Merger.from_revision_ids(
476
this_tree, b'rev2b', other_branch=other_tree.branch)
470
477
merger.merge_type = _mod_merge.Merge3Merger
471
478
tree_merger = merger.make_merger()
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())
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())
486
486
def test_do_merge(self):
487
487
this_tree = self.make_branch_and_tree('this')
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')
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')
496
496
this_tree.lock_write()
497
497
self.addCleanup(this_tree.unlock)
498
merger = _mod_merge.Merger.from_revision_ids(None,
499
this_tree, 'rev2b', other_branch=other_tree.branch)
498
merger = _mod_merge.Merger.from_revision_ids(
499
this_tree, b'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
tree_file = this_tree.get_file('file-id')
505
self.assertEqual('2b\n1\n2a\n', tree_file.read())
503
with this_tree.get_file('file') as tree_file:
504
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
509
506
def test_merge_require_tree_root(self):
510
507
tree = self.make_branch_and_tree(".")
576
574
def add_uncommitted_version(self, key, parents, text):
577
575
self.plan_merge_vf.add_lines(key, parents,
578
[c+'\n' for c in text])
576
[bytes([c]) + b'\n' for c in bytearray(text)])
580
578
def setup_plan_merge(self):
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',))
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',))
586
584
def setup_plan_merge_uncommitted(self):
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',))
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',))
592
592
def test_base_from_plan(self):
593
593
self.setup_plan_merge()
594
plan = self.plan_merge_vf.plan_merge('B', 'C')
594
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
595
595
pwm = versionedfile.PlanWeaveMerge(plan)
596
self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
596
self.assertEqual([b'a\n', b'b\n', b'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', 'C')),
601
plan._get_matching_blocks(b'B', 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', 'C')
606
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
607
607
self.assertEqual([
609
('unchanged', 'a\n'),
609
('unchanged', b'a\n'),
610
('killed-a', b'b\n'),
611
('killed-b', b'c\n'),
618
618
def test_plan_merge_cherrypick(self):
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',))
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',))
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('root', 'A', [], 'abc')
638
self.add_rev('root', 'B', [], 'xyz')
639
my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
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',))
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('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'))
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'))
940
940
self.assertEqual([
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'),
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'),
951
951
pwm = versionedfile.PlanWeaveMerge(plan)
952
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
953
''.join(pwm.base_from_plan()))
952
self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
953
b''.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('E', 'D'))
956
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
957
957
self.assertEqual([
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'),
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'),
968
968
pwm = versionedfile.PlanWeaveMerge(plan)
969
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
970
''.join(pwm.base_from_plan()))
969
self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
970
b''.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('D', 'E'))
974
plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
975
975
self.assertEqual([
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'),
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'),
986
986
pwm = versionedfile.PlanWeaveMerge(plan)
987
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
988
''.join(pwm.base_from_plan()))
987
self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
988
b''.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('E', 'D'))
991
plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
992
992
self.assertEqual([
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'),
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'),
1003
1003
pwm = versionedfile.PlanWeaveMerge(plan)
1004
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
1005
''.join(pwm.base_from_plan()))
1004
self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
1005
b''.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', 'a\n'),
1082
('killed-a', 'c\n'),
1085
('killed-b', 'f\n'),
1086
('killed-b', 'g\n'),
1080
('unchanged', b'a\n'),
1082
('killed-a', b'c\n'),
1085
('killed-b', b'f\n'),
1086
('killed-b', b'g\n'),
1089
('unchanged', 'a\n'),
1091
('killed-a', 'c\n'),
1094
('killed-b', 'f\n'),
1095
('killed-b', 'i\n'),
1089
('unchanged', b'a\n'),
1091
('killed-a', b'c\n'),
1094
('killed-b', b'f\n'),
1095
('killed-b', b'i\n'),
1097
1097
subtracted_plan = [
1098
('unchanged', 'a\n'),
1100
('killed-a', 'c\n'),
1102
('unchanged', 'f\n'),
1103
('killed-b', 'i\n'),
1098
('unchanged', b'a\n'),
1100
('killed-a', b'c\n'),
1102
('unchanged', b'f\n'),
1103
('killed-b', 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('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')
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')
1114
1114
def test_plan_merge_with_base(self):
1115
1115
self.setup_merge_with_base()
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'),
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'),
1124
1124
def test_plan_lca_merge(self):
1125
1125
self.setup_plan_merge()
1126
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1126
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1127
1127
self.assertEqual([
1129
('unchanged', 'a\n'),
1130
('killed-b', 'c\n'),
1133
('killed-a', 'b\n'),
1134
('unchanged', 'g\n')],
1129
('unchanged', b'a\n'),
1130
('killed-b', b'c\n'),
1133
('killed-a', b'b\n'),
1134
('unchanged', b'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:', 'C:')
1139
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1140
1140
self.assertEqual([
1142
('unchanged', 'a\n'),
1143
('killed-b', 'c\n'),
1146
('killed-a', 'b\n'),
1147
('unchanged', 'g\n')],
1142
('unchanged', b'a\n'),
1143
('killed-b', b'c\n'),
1146
('killed-a', b'b\n'),
1147
('unchanged', b'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('THIS', 'OTHER', 'BASE')
1153
self.assertEqual([('unchanged', 'a\n'),
1155
('unchanged', 'b\n'),
1156
('killed-b', 'c\n'),
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'),
1160
1160
def test_plan_lca_merge_with_criss_cross(self):
1161
self.add_version(('root', 'ROOT'), [], 'abc')
1161
self.add_version((b'root', b'ROOT'), [], b'abc')
1162
1162
# each side makes a change
1163
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1164
self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
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')
1165
1165
# both sides merge, discarding others' changes
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'),
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'),
1179
1179
def test_plan_lca_merge_with_null(self):
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'),
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'),
1188
1188
def test_plan_merge_with_delete_and_change(self):
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'),
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'),
1197
1197
def test_plan_merge_with_move_and_change(self):
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'),
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'),
1211
1211
class LoggingMerger(object):
1396
1395
class TestMergerEntriesLCA(TestMergerBase):
1398
1397
def make_merge_obj(self, builder, other_revision_id,
1399
interesting_files=None, interesting_ids=None):
1398
interesting_files=None):
1400
1399
merger = self.make_Merger(builder, other_revision_id,
1401
interesting_files=interesting_files,
1402
interesting_ids=interesting_ids)
1400
interesting_files=interesting_files)
1403
1401
return merger.make_merger()
1405
1403
def test_simple(self):
1406
1404
builder = self.get_builder()
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')
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')
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())
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())
1423
1426
entries = list(merge_obj._entries_lca())
1425
1428
# (file_id, changed, parents, names, executable)
1426
1429
# BASE, lca1, lca2, OTHER, THIS
1427
root_id = 'a-root-id'
1428
self.assertEqual([('a-id', True,
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'),
1429
1433
((root_id, [root_id, root_id]), root_id, root_id),
1430
1434
((u'a', [u'a', u'a']), u'a', u'a'),
1431
((False, [False, False]), False, False)),
1435
((False, [False, False]), False, False),
1434
1439
def test_not_in_base(self):
1435
1440
# LCAs all have the same last-modified revision for the file, as do
1444
1449
# G modifies 'bar'
1446
1451
builder = self.get_builder()
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')
1452
builder.build_snapshot(None,
1453
[('add', (u'', b'a-root-id', 'directory', None))],
1454
revision_id=b'A-id')
1455
builder.build_snapshot([b'A-id'],
1456
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1457
revision_id=b'B-id')
1458
builder.build_snapshot([b'A-id'],
1459
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1460
revision_id=b'C-id')
1461
builder.build_snapshot([b'B-id', b'C-id'],
1462
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1463
revision_id=b'D-id')
1464
builder.build_snapshot([b'C-id', b'B-id'],
1465
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1466
revision_id=b'E-id')
1467
builder.build_snapshot([b'E-id', b'D-id'],
1468
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1469
revision_id=b'G-id')
1470
builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1471
merge_obj = self.make_merge_obj(builder, b'G-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())
1473
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1474
for t in merge_obj._lca_trees])
1475
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1465
1476
entries = list(merge_obj._entries_lca())
1466
root_id = 'a-root-id'
1467
self.assertEqual([('bar-id', True,
1477
root_id = b'a-root-id'
1478
self.assertEqual([(b'bar-id', True,
1479
((None, [u'bar', u'bar']), u'bar', u'bar'),
1468
1480
((None, [root_id, root_id]), root_id, root_id),
1469
1481
((None, [u'bar', u'bar']), u'bar', u'bar'),
1470
((None, [False, False]), False, False)),
1482
((None, [False, False]), False, False),
1473
1486
def test_not_in_this(self):
1474
1487
builder = self.get_builder()
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')
1488
builder.build_snapshot(None,
1489
[('add', (u'', b'a-root-id', 'directory', None)),
1490
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1491
revision_id=b'A-id')
1492
builder.build_snapshot([b'A-id'],
1493
[('modify', ('a', b'a\nB\nb\nc\n'))],
1494
revision_id=b'B-id')
1495
builder.build_snapshot([b'A-id'],
1496
[('modify', ('a', b'a\nb\nC\nc\n'))],
1497
revision_id=b'C-id')
1498
builder.build_snapshot([b'C-id', b'B-id'],
1499
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1500
revision_id=b'E-id')
1501
builder.build_snapshot([b'B-id', b'C-id'],
1502
[('unversion', 'a')],
1503
revision_id=b'D-id')
1504
merge_obj = self.make_merge_obj(builder, b'E-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())
1506
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1507
for t in merge_obj._lca_trees])
1508
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1492
1510
entries = list(merge_obj._entries_lca())
1493
root_id = 'a-root-id'
1494
self.assertEqual([('a-id', True,
1511
root_id = b'a-root-id'
1512
self.assertEqual([(b'a-id', True,
1513
((u'a', [u'a', u'a']), u'a', None),
1495
1514
((root_id, [root_id, root_id]), root_id, None),
1496
1515
((u'a', [u'a', u'a']), u'a', None),
1497
((False, [False, False]), False, None)),
1516
((False, [False, False]), False, None),
1500
1520
def test_file_not_in_one_lca(self):
1501
1521
# A # just root
1505
1525
# D E # D and E both have the file, unchanged from C
1506
1526
builder = self.get_builder()
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')
1527
builder.build_snapshot(None,
1528
[('add', (u'', b'a-root-id', 'directory', None))],
1529
revision_id=b'A-id')
1530
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1531
builder.build_snapshot([b'A-id'],
1532
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1533
revision_id=b'C-id')
1534
builder.build_snapshot([b'C-id', b'B-id'],
1535
[], revision_id=b'E-id') # Inherited from C
1536
builder.build_snapshot([b'B-id', b'C-id'], # Merged from C
1537
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1538
revision_id=b'D-id')
1539
merge_obj = self.make_merge_obj(builder, b'E-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())
1541
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1542
for t in merge_obj._lca_trees])
1543
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1521
1545
entries = list(merge_obj._entries_lca())
1522
1546
self.assertEqual([], entries)
1524
1548
def test_not_in_other(self):
1525
1549
builder = self.get_builder()
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')
1550
builder.build_snapshot(None,
1551
[('add', (u'', b'a-root-id', 'directory', None)),
1552
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1553
revision_id=b'A-id')
1554
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1555
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1556
builder.build_snapshot(
1558
[('unversion', 'a')], revision_id=b'E-id')
1559
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1560
merge_obj = self.make_merge_obj(builder, b'E-id')
1536
1562
entries = list(merge_obj._entries_lca())
1537
root_id = 'a-root-id'
1538
self.assertEqual([('a-id', True,
1563
root_id = b'a-root-id'
1564
self.assertEqual([(b'a-id', True,
1565
((u'a', [u'a', u'a']), None, u'a'),
1539
1566
((root_id, [root_id, root_id]), None, root_id),
1540
1567
((u'a', [u'a', u'a']), None, u'a'),
1541
((False, [False, False]), None, False)),
1568
((False, [False, False]), None, False),
1544
1572
def test_not_in_other_or_lca(self):
1545
1573
# A base, introduces 'foo'
1619
1653
# A => C, add file, thus C supersedes B
1620
1654
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1621
1655
builder = self.get_builder()
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')
1656
builder.build_snapshot(None,
1657
[('add', (u'', b'a-root-id', 'directory', None))],
1658
revision_id=b'A-id')
1659
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1660
builder.build_snapshot([b'A-id'],
1661
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1662
revision_id=b'C-id')
1663
builder.build_snapshot([b'C-id', b'B-id'],
1664
[('unversion', 'a')],
1665
revision_id=b'E-id')
1666
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1667
merge_obj = self.make_merge_obj(builder, b'E-id')
1632
1669
entries = list(merge_obj._entries_lca())
1633
1670
self.assertEqual([], entries)
1635
1672
def test_only_in_other(self):
1636
1673
builder = self.get_builder()
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')
1674
builder.build_snapshot(None,
1675
[('add', (u'', b'a-root-id', 'directory', None))],
1676
revision_id=b'A-id')
1677
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1678
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1679
builder.build_snapshot([b'C-id', b'B-id'],
1680
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1681
revision_id=b'E-id')
1682
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1683
merge_obj = self.make_merge_obj(builder, b'E-id')
1646
1685
entries = list(merge_obj._entries_lca())
1647
root_id = 'a-root-id'
1648
self.assertEqual([('a-id', True,
1686
root_id = b'a-root-id'
1687
self.assertEqual([(b'a-id', True,
1688
((None, [None, None]), u'a', None),
1649
1689
((None, [None, None]), root_id, None),
1650
1690
((None, [None, None]), u'a', None),
1651
((None, [None, None]), False, None)),
1691
((None, [None, None]), False, None),
1654
1695
def test_one_lca_supersedes(self):
1655
1696
# One LCA supersedes the other LCAs last modified value, but the
1745
1795
# be pruned from the LCAs, even though it was newly introduced by E
1746
1796
# (superseding B).
1747
1797
builder = self.get_builder()
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')
1798
builder.build_snapshot(None,
1799
[('add', (u'', b'a-root-id', 'directory', None)),
1800
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1801
revision_id=b'A-id')
1802
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1803
builder.build_snapshot([b'A-id'],
1804
[('rename', ('foo', 'bar'))],
1805
revision_id=b'B-id')
1806
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1807
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1808
builder.build_snapshot([b'E-id', b'D-id'],
1809
[('rename', ('foo', 'bar'))],
1810
revision_id=b'G-id')
1811
builder.build_snapshot([b'D-id', b'E-id'],
1812
[('rename', ('bar', 'bing'))],
1813
revision_id=b'F-id') # should end up conflicting
1814
merge_obj = self.make_merge_obj(builder, b'G-id')
1762
1816
entries = list(merge_obj._entries_lca())
1763
root_id = 'a-root-id'
1817
root_id = b'a-root-id'
1764
1818
self.expectFailure("We prune values from BASE even when relevant.",
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)),
1821
((root_id, [root_id, root_id]), root_id, root_id),
1822
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1823
((False, [False, False]), False, False),
1772
1827
def test_both_sides_revert(self):
1773
1828
# Both sides of a criss-cross revert the text to the lca
1809
1869
# We need to emit an entry for 'foo', because D & E differed on the
1810
1870
# merge resolution
1811
1871
builder = self.get_builder()
1812
builder.build_snapshot('A-id', None,
1813
[('add', (u'', 'a-root-id', 'directory', None)),
1814
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1815
builder.build_snapshot('B-id', ['A-id'],
1816
[('modify', ('foo-id', 'B content\n'))])
1817
builder.build_snapshot('C-id', ['A-id'],
1818
[('modify', ('foo-id', 'C content\n'))])
1819
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1820
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1821
builder.build_snapshot('F-id', ['D-id'],
1822
[('modify', ('foo-id', 'F content\n'))])
1823
merge_obj = self.make_merge_obj(builder, 'E-id')
1872
builder.build_snapshot(None,
1873
[('add', (u'', b'a-root-id', 'directory', None)),
1874
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1875
revision_id=b'A-id')
1876
builder.build_snapshot([b'A-id'],
1877
[('modify', ('foo', b'B content\n'))],
1878
revision_id=b'B-id')
1879
builder.build_snapshot([b'A-id'],
1880
[('modify', ('foo', b'C content\n'))],
1881
revision_id=b'C-id', )
1882
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1883
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1884
builder.build_snapshot([b'D-id'],
1885
[('modify', ('foo', b'F content\n'))],
1886
revision_id=b'F-id')
1887
merge_obj = self.make_merge_obj(builder, b'E-id')
1825
1889
entries = list(merge_obj._entries_lca())
1826
root_id = 'a-root-id'
1827
self.assertEqual([('foo-id', True,
1890
root_id = b'a-root-id'
1891
self.assertEqual([(b'foo-id', True,
1892
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1828
1893
((root_id, [root_id, root_id]), root_id, root_id),
1829
1894
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1830
((False, [False, False]), False, False)),
1895
((False, [False, False]), False, False),
1833
1899
def test_same_lca_resolution_one_side_updates_content(self):
1834
1900
# Both sides converge, but then one side updates the text.
1845
1911
# We need to conflict.
1847
1913
builder = self.get_builder()
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')
1914
builder.build_snapshot(None,
1915
[('add', (u'', b'a-root-id', 'directory', None)),
1916
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1917
revision_id=b'A-id')
1918
builder.build_snapshot([b'A-id'],
1919
[('modify', ('foo', b'B content\n'))],
1920
revision_id=b'B-id')
1921
builder.build_snapshot([b'A-id'],
1922
[('modify', ('foo', b'C content\n'))],
1923
revision_id=b'C-id')
1924
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1925
builder.build_snapshot([b'B-id', b'C-id'],
1926
[('modify', ('foo', b'C content\n'))],
1927
revision_id=b'D-id') # Same as E
1928
builder.build_snapshot([b'D-id'],
1929
[('modify', ('foo', b'F content\n'))],
1930
revision_id=b'F-id')
1931
merge_obj = self.make_merge_obj(builder, b'E-id')
1862
1933
entries = list(merge_obj._entries_lca())
1863
1934
self.expectFailure("We don't detect that LCA resolution was the"
1864
1935
" same on both sides",
1865
self.assertEqual, [], entries)
1936
self.assertEqual, [], entries)
1867
1938
def test_only_path_changed(self):
1868
1939
builder = self.get_builder()
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')
1940
builder.build_snapshot(None,
1941
[('add', (u'', b'a-root-id', 'directory', None)),
1942
('add', (u'a', b'a-id', 'file', b'content\n'))],
1943
revision_id=b'A-id')
1944
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1945
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1946
builder.build_snapshot([b'C-id', b'B-id'],
1947
[('rename', (u'a', u'b'))],
1948
revision_id=b'E-id')
1949
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1950
merge_obj = self.make_merge_obj(builder, b'E-id')
1878
1951
entries = list(merge_obj._entries_lca())
1879
root_id = 'a-root-id'
1952
root_id = b'a-root-id'
1880
1953
# The content was not changed, only the path
1881
self.assertEqual([('a-id', False,
1954
self.assertEqual([(b'a-id', False,
1955
((u'a', [u'a', u'a']), u'b', u'a'),
1882
1956
((root_id, [root_id, root_id]), root_id, root_id),
1883
1957
((u'a', [u'a', u'a']), u'b', u'a'),
1884
((False, [False, False]), False, False)),
1958
((False, [False, False]), False, False),
1887
1962
def test_kind_changed(self):
1888
1963
# Identical content, except 'D' changes a-id into a directory
1889
1964
builder = self.get_builder()
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')
1965
builder.build_snapshot(None,
1966
[('add', (u'', b'a-root-id', 'directory', None)),
1967
('add', (u'a', b'a-id', 'file', b'content\n'))],
1968
revision_id=b'A-id')
1969
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1970
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1971
builder.build_snapshot([b'C-id', b'B-id'],
1972
[('unversion', 'a'),
1974
('add', (u'a', b'a-id', 'directory', None))],
1975
revision_id=b'E-id')
1976
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1977
merge_obj = self.make_merge_obj(builder, b'E-id')
1901
1978
entries = list(merge_obj._entries_lca())
1902
root_id = 'a-root-id'
1979
root_id = b'a-root-id'
1903
1980
# Only the kind was changed (content)
1904
self.assertEqual([('a-id', True,
1981
self.assertEqual([(b'a-id', True,
1982
((u'a', [u'a', u'a']), u'a', u'a'),
1905
1983
((root_id, [root_id, root_id]), root_id, root_id),
1906
1984
((u'a', [u'a', u'a']), u'a', u'a'),
1907
((False, [False, False]), False, False)),
1985
((False, [False, False]), False, False),
1910
1989
def test_this_changed_kind(self):
1911
1990
# Identical content, but THIS changes a file to a directory
1912
1991
builder = self.get_builder()
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')
1992
builder.build_snapshot(None,
1993
[('add', (u'', b'a-root-id', 'directory', None)),
1994
('add', (u'a', b'a-id', 'file', b'content\n'))],
1995
revision_id=b'A-id')
1996
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1997
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1998
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1999
builder.build_snapshot([b'B-id', b'C-id'],
2000
[('unversion', 'a'),
2002
('add', (u'a', b'a-id', 'directory', None))],
2003
revision_id=b'D-id')
2004
merge_obj = self.make_merge_obj(builder, b'E-id')
1924
2005
entries = list(merge_obj._entries_lca())
1925
2006
# Only the kind was changed (content)
1926
2007
self.assertEqual([], entries)
1928
2009
def test_interesting_files(self):
1929
2010
# Two files modified, but we should filter one of them
1930
2011
builder = self.get_builder()
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',
2012
builder.build_snapshot(None,
2013
[('add', (u'', b'a-root-id', 'directory', None)),
2014
('add', (u'a', b'a-id', 'file', b'content\n')),
2015
('add', (u'b', b'b-id', 'file', b'content\n'))],
2016
revision_id=b'A-id')
2017
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2018
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2019
builder.build_snapshot([b'C-id', b'B-id'],
2020
[('modify', ('a', b'new-content\n')),
2021
('modify', ('b', b'new-content\n'))],
2022
revision_id=b'E-id')
2023
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2024
merge_obj = self.make_merge_obj(builder, b'E-id',
1942
2025
interesting_files=['b'])
1943
2026
entries = list(merge_obj._entries_lca())
1944
root_id = 'a-root-id'
1945
self.assertEqual([('b-id', True,
2027
root_id = b'a-root-id'
2028
self.assertEqual([(b'b-id', True,
2029
((u'b', [u'b', u'b']), u'b', u'b'),
1946
2030
((root_id, [root_id, root_id]), root_id, root_id),
1947
2031
((u'b', [u'b', u'b']), u'b', u'b'),
1948
((False, [False, False]), False, False)),
2032
((False, [False, False]), False, False),
1951
2036
def test_interesting_file_in_this(self):
1952
2037
# This renamed the file, but it should still match the entry in other
1953
2038
builder = self.get_builder()
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',
2039
builder.build_snapshot(None,
2040
[('add', (u'', b'a-root-id', 'directory', None)),
2041
('add', (u'a', b'a-id', 'file', b'content\n')),
2042
('add', (u'b', b'b-id', 'file', b'content\n'))],
2043
revision_id=b'A-id')
2044
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2045
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2046
builder.build_snapshot([b'C-id', b'B-id'],
2047
[('modify', ('a', b'new-content\n')),
2048
('modify', ('b', b'new-content\n'))],
2049
revision_id=b'E-id')
2050
builder.build_snapshot([b'B-id', b'C-id'],
2051
[('rename', ('b', 'c'))],
2052
revision_id=b'D-id')
2053
merge_obj = self.make_merge_obj(builder, b'E-id',
1966
2054
interesting_files=['c'])
1967
2055
entries = list(merge_obj._entries_lca())
1968
root_id = 'a-root-id'
1969
self.assertEqual([('b-id', True,
2056
root_id = b'a-root-id'
2057
self.assertEqual([(b'b-id', True,
2058
((u'b', [u'b', u'b']), u'b', u'c'),
1970
2059
((root_id, [root_id, root_id]), root_id, root_id),
1971
2060
((u'b', [u'b', u'b']), u'b', u'c'),
1972
((False, [False, False]), False, False)),
2061
((False, [False, False]), False, False),
1975
2065
def test_interesting_file_in_base(self):
1976
2066
# This renamed the file, but it should still match the entry in BASE
1977
2067
builder = self.get_builder()
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',
2068
builder.build_snapshot(None,
2069
[('add', (u'', b'a-root-id', 'directory', None)),
2070
('add', (u'a', b'a-id', 'file', b'content\n')),
2071
('add', (u'c', b'c-id', 'file', b'content\n'))],
2072
revision_id=b'A-id')
2073
builder.build_snapshot([b'A-id'],
2074
[('rename', ('c', 'b'))],
2075
revision_id=b'B-id')
2076
builder.build_snapshot([b'A-id'],
2077
[('rename', ('c', 'b'))],
2078
revision_id=b'C-id')
2079
builder.build_snapshot([b'C-id', b'B-id'],
2080
[('modify', ('a', b'new-content\n')),
2081
('modify', ('b', b'new-content\n'))],
2082
revision_id=b'E-id')
2083
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2084
merge_obj = self.make_merge_obj(builder, b'E-id',
1991
2085
interesting_files=['c'])
1992
2086
entries = list(merge_obj._entries_lca())
1993
root_id = 'a-root-id'
1994
self.assertEqual([('c-id', True,
2087
root_id = b'a-root-id'
2088
self.assertEqual([(b'c-id', True,
2089
((u'c', [u'b', u'b']), u'b', u'b'),
1995
2090
((root_id, [root_id, root_id]), root_id, root_id),
1996
2091
((u'c', [u'b', u'b']), u'b', u'b'),
1997
((False, [False, False]), False, False)),
2092
((False, [False, False]), False, False),
2000
2096
def test_interesting_file_in_lca(self):
2001
2097
# This renamed the file, but it should still match the entry in LCA
2002
2098
builder = self.get_builder()
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',
2099
builder.build_snapshot(None,
2100
[('add', (u'', b'a-root-id', 'directory', None)),
2101
('add', (u'a', b'a-id', 'file', b'content\n')),
2102
('add', (u'b', b'b-id', 'file', b'content\n'))],
2103
revision_id=b'A-id')
2104
builder.build_snapshot([b'A-id'],
2105
[('rename', ('b', 'c'))], revision_id=b'B-id')
2106
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2107
builder.build_snapshot([b'C-id', b'B-id'],
2108
[('modify', ('a', b'new-content\n')),
2109
('modify', ('b', b'new-content\n'))],
2110
revision_id=b'E-id')
2111
builder.build_snapshot([b'B-id', b'C-id'],
2112
[('rename', ('c', 'b'))], revision_id=b'D-id')
2113
merge_obj = self.make_merge_obj(builder, b'E-id',
2016
2114
interesting_files=['c'])
2017
2115
entries = list(merge_obj._entries_lca())
2018
root_id = 'a-root-id'
2019
self.assertEqual([('b-id', True,
2116
root_id = b'a-root-id'
2117
self.assertEqual([(b'b-id', True,
2118
((u'b', [u'c', u'b']), u'b', u'b'),
2020
2119
((root_id, [root_id, root_id]), root_id, root_id),
2021
2120
((u'b', [u'c', u'b']), u'b', u'b'),
2022
((False, [False, False]), False, False)),
2121
((False, [False, False]), False, False),
2025
def test_interesting_ids(self):
2125
def test_interesting_files(self):
2026
2126
# Two files modified, but we should filter one of them
2027
2127
builder = self.get_builder()
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'])
2128
builder.build_snapshot(None,
2129
[('add', (u'', b'a-root-id', 'directory', None)),
2130
('add', (u'a', b'a-id', 'file', b'content\n')),
2131
('add', (u'b', b'b-id', 'file', b'content\n'))],
2132
revision_id=b'A-id')
2133
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2134
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2135
builder.build_snapshot([b'C-id', b'B-id'],
2136
[('modify', ('a', b'new-content\n')),
2137
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2138
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2139
merge_obj = self.make_merge_obj(builder, b'E-id',
2140
interesting_files=['b'])
2040
2141
entries = list(merge_obj._entries_lca())
2041
root_id = 'a-root-id'
2042
self.assertEqual([('b-id', True,
2142
root_id = b'a-root-id'
2143
self.assertEqual([(b'b-id', True,
2144
((u'b', [u'b', u'b']), u'b', u'b'),
2043
2145
((root_id, [root_id, root_id]), root_id, root_id),
2044
2146
((u'b', [u'b', u'b']), u'b', u'b'),
2045
((False, [False, False]), False, False)),
2147
((False, [False, False]), False, False),
2050
2152
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2315
2424
wt.lock_write()
2316
2425
self.addCleanup(wt.unlock)
2317
2426
os.symlink('bar', 'path/foo')
2318
wt.add(['foo'], ['foo-id'])
2319
wt.commit('A add symlink', rev_id='A-id')
2427
wt.add(['foo'], [b'foo-id'])
2428
wt.commit('A add symlink', rev_id=b'A-id')
2320
2429
wt.rename_one('foo', 'barry')
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')
2430
wt.commit('B foo => barry', rev_id=b'B-id')
2431
wt.set_last_revision(b'A-id')
2432
wt.branch.set_last_revision_info(1, b'A-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')
2434
wt.commit('C', rev_id=b'C-id')
2435
wt.merge_from_branch(wt.branch, b'B-id')
2436
self.assertEqual('barry', wt.id2path(b'foo-id'))
2437
self.assertEqual('bar', wt.get_symlink_target('barry'))
2438
wt.commit('E merges C & B', rev_id=b'E-id')
2330
2439
wt.rename_one('barry', 'blah')
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')
2440
wt.commit('F barry => blah', rev_id=b'F-id')
2441
wt.set_last_revision(b'B-id')
2442
wt.branch.set_last_revision_info(2, b'B-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'))
2444
wt.merge_from_branch(wt.branch, b'C-id')
2445
wt.commit('D merges B & C', rev_id=b'D-id')
2446
self.assertEqual('barry', wt.id2path(b'foo-id'))
2338
2447
# Check the output of the Merger object directly
2339
merger = _mod_merge.Merger.from_revision_ids(None,
2448
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2341
2449
merger.merge_type = _mod_merge.Merge3Merger
2342
2450
merge_obj = merger.make_merger()
2343
2451
root_id = wt.path2id('')
2344
2452
entries = list(merge_obj._entries_lca())
2345
2453
# No content change, just a path change
2346
self.assertEqual([('foo-id', False,
2454
self.assertEqual([(b'foo-id', False,
2455
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2347
2456
((root_id, [root_id, root_id]), root_id, root_id),
2348
2457
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2349
((False, [False, False]), False, False)),
2351
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2458
((False, [False, False]), False, False),
2461
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2352
2462
self.assertEqual(0, conflicts)
2353
self.assertEqual('blah', wt.id2path('foo-id'))
2463
self.assertEqual('blah', wt.id2path(b'foo-id'))
2355
2465
def test_symlink_no_content_change(self):
2356
2466
self.requireFeature(features.SymlinkFeature)
2418
2527
wt = self.make_branch_and_tree('path')
2419
2528
wt.lock_write()
2420
2529
self.addCleanup(wt.unlock)
2421
wt.commit('base', rev_id='A-id')
2530
wt.commit('base', rev_id=b'A-id')
2422
2531
os.symlink('bar', 'path/foo')
2423
wt.add(['foo'], ['foo-id'])
2424
wt.commit('add symlink foo => bar', rev_id='B-id')
2425
wt.set_last_revision('A-id')
2426
wt.branch.set_last_revision_info(1, 'A-id')
2532
wt.add(['foo'], [b'foo-id'])
2533
wt.commit('add symlink foo => bar', rev_id=b'B-id')
2534
wt.set_last_revision(b'A-id')
2535
wt.branch.set_last_revision_info(1, b'A-id')
2428
wt.commit('C', rev_id='C-id')
2429
wt.merge_from_branch(wt.branch, 'B-id')
2430
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2537
wt.commit('C', rev_id=b'C-id')
2538
wt.merge_from_branch(wt.branch, b'B-id')
2539
self.assertEqual('bar', wt.get_symlink_target('foo'))
2431
2540
os.remove('path/foo')
2432
2541
# We have to change the link in E, or it won't try to do a comparison
2433
2542
os.symlink('bing', 'path/foo')
2434
wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2435
wt.set_last_revision('B-id')
2436
wt.branch.set_last_revision_info(2, 'B-id')
2543
wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2544
wt.set_last_revision(b'B-id')
2545
wt.branch.set_last_revision_info(2, b'B-id')
2438
wt.merge_from_branch(wt.branch, 'C-id')
2547
wt.merge_from_branch(wt.branch, b'C-id')
2439
2548
os.remove('path/foo')
2440
self.build_tree_contents([('path/foo', 'file content\n')])
2549
self.build_tree_contents([('path/foo', b'file content\n')])
2441
2550
# XXX: workaround, WT doesn't detect kind changes unless you do
2442
2551
# iter_changes()
2443
2552
list(wt.iter_changes(wt.basis_tree()))
2444
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2553
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2446
merger = _mod_merge.Merger.from_revision_ids(None,
2555
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2448
2556
merger.merge_type = _mod_merge.Merge3Merger
2449
2557
merge_obj = merger.make_merger()
2450
2558
entries = list(merge_obj._entries_lca())
2451
2559
root_id = wt.path2id('')
2452
self.assertEqual([('foo-id', True,
2560
self.assertEqual([(b'foo-id', True,
2561
((None, [u'foo', None]), u'foo', u'foo'),
2453
2562
((None, [root_id, None]), root_id, root_id),
2454
2563
((None, [u'foo', None]), u'foo', u'foo'),
2455
((None, [False, None]), False, False)),
2564
((None, [False, None]), False, False),
2458
2568
def test_symlink_all_wt(self):
2459
2569
"""Check behavior if all trees are Working Trees."""
2474
2584
wt.lock_write()
2475
2585
self.addCleanup(wt.unlock)
2476
2586
os.symlink('bar', 'path/foo')
2477
wt.add(['foo'], ['foo-id'])
2478
wt.commit('add symlink', rev_id='A-id')
2587
wt.add(['foo'], [b'foo-id'])
2588
wt.commit('add symlink', rev_id=b'A-id')
2479
2589
os.remove('path/foo')
2480
2590
os.symlink('baz', 'path/foo')
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')
2591
wt.commit('foo => baz', rev_id=b'B-id')
2592
wt.set_last_revision(b'A-id')
2593
wt.branch.set_last_revision_info(1, b'A-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')
2595
wt.commit('C', rev_id=b'C-id')
2596
wt.merge_from_branch(wt.branch, b'B-id')
2597
self.assertEqual('baz', wt.get_symlink_target('foo'))
2598
wt.commit('E merges C & B', rev_id=b'E-id')
2489
2599
os.remove('path/foo')
2490
2600
os.symlink('bing', 'path/foo')
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')
2601
wt.commit('F foo => bing', rev_id=b'F-id')
2602
wt.set_last_revision(b'B-id')
2603
wt.branch.set_last_revision_info(2, b'B-id')
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()
2605
wt.merge_from_branch(wt.branch, b'C-id')
2606
wt.commit('D merges B & C', rev_id=b'D-id')
2607
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2498
2608
wt_base.lock_read()
2499
2609
self.addCleanup(wt_base.unlock)
2500
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2610
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2501
2611
wt_lca1.lock_read()
2502
2612
self.addCleanup(wt_lca1.unlock)
2503
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2613
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2504
2614
wt_lca2.lock_read()
2505
2615
self.addCleanup(wt_lca2.unlock)
2506
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2616
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2507
2617
wt_other.lock_read()
2508
2618
self.addCleanup(wt_other.unlock)
2509
2619
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2510
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2620
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2511
2621
entries = list(merge_obj._entries_lca())
2512
2622
root_id = wt.path2id('')
2513
self.assertEqual([('foo-id', True,
2623
self.assertEqual([(b'foo-id', True,
2624
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2514
2625
((root_id, [root_id, root_id]), root_id, root_id),
2515
2626
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2516
((False, [False, False]), False, False)),
2627
((False, [False, False]), False, False),
2519
2631
def test_other_reverted_path_to_base(self):
2520
2632
# A Path at 'foo'
2528
2640
# F Path at 'foo'
2529
2641
builder = self.get_builder()
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')
2642
builder.build_snapshot(None,
2643
[('add', (u'', b'a-root-id', 'directory', None)),
2644
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2645
revision_id=b'A-id')
2646
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2647
builder.build_snapshot([b'A-id'],
2648
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2649
builder.build_snapshot([b'C-id', b'B-id'],
2650
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2651
builder.build_snapshot([b'E-id'],
2652
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2653
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2654
wt, conflicts = self.do_merge(builder, b'F-id')
2542
2655
self.assertEqual(0, conflicts)
2543
self.assertEqual('foo', wt.id2path('foo-id'))
2656
self.assertEqual('foo', wt.id2path(b'foo-id'))
2545
2658
def test_other_reverted_content_to_base(self):
2546
2659
builder = self.get_builder()
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')
2660
builder.build_snapshot(None,
2661
[('add', (u'', b'a-root-id', 'directory', None)),
2662
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2663
revision_id=b'A-id')
2664
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2665
builder.build_snapshot([b'A-id'],
2666
[('modify', ('foo', b'B content\n'))],
2667
revision_id=b'B-id')
2668
builder.build_snapshot([b'C-id', b'B-id'],
2669
[('modify', ('foo', b'B content\n'))],
2670
revision_id=b'E-id') # merge the content
2671
builder.build_snapshot([b'E-id'],
2672
[('modify', ('foo', b'base content\n'))],
2673
revision_id=b'F-id') # Revert back to BASE
2674
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2675
wt, conflicts = self.do_merge(builder, b'F-id')
2559
2676
self.assertEqual(0, conflicts)
2560
2677
# TODO: We need to use the per-file graph to properly select a BASE
2561
2678
# before this will work. Or at least use the LCA trees to find
2562
2679
# the appropriate content base. (which is B, not A).
2563
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2680
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2565
2682
def test_other_modified_content(self):
2566
2683
builder = self.get_builder()
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')
2684
builder.build_snapshot(None,
2685
[('add', (u'', b'a-root-id', 'directory', None)),
2686
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2687
revision_id=b'A-id')
2688
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2689
builder.build_snapshot([b'A-id'],
2690
[('modify', ('foo', b'B content\n'))],
2691
revision_id=b'B-id')
2692
builder.build_snapshot([b'C-id', b'B-id'],
2693
[('modify', ('foo', b'B content\n'))],
2694
revision_id=b'E-id') # merge the content
2695
builder.build_snapshot([b'E-id'],
2696
[('modify', ('foo', b'F content\n'))],
2697
revision_id=b'F-id') # Override B content
2698
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2699
wt, conflicts = self.do_merge(builder, b'F-id')
2579
2700
self.assertEqual(0, conflicts)
2580
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2701
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2582
2703
def test_all_wt(self):
2583
2704
"""Check behavior if all trees are Working Trees."""
2591
2712
# D E E updates content, renames 'b' => 'c'
2592
2713
builder = self.get_builder()
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
2714
builder.build_snapshot(None,
2715
[('add', (u'', b'a-root-id', 'directory', None)),
2716
('add', (u'a', b'a-id', 'file', b'base content\n')),
2717
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2718
revision_id=b'A-id')
2719
builder.build_snapshot([b'A-id'],
2720
[('modify', ('foo', b'B content\n'))],
2721
revision_id=b'B-id')
2722
builder.build_snapshot([b'A-id'],
2723
[('rename', ('a', 'b'))],
2724
revision_id=b'C-id')
2725
builder.build_snapshot([b'C-id', b'B-id'],
2726
[('rename', ('b', 'c')),
2727
('modify', ('foo', b'E content\n'))],
2728
revision_id=b'E-id')
2729
builder.build_snapshot([b'B-id', b'C-id'],
2730
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2606
2731
wt_this = self.get_wt_from_builder(builder)
2607
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2732
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2608
2733
wt_base.lock_read()
2609
2734
self.addCleanup(wt_base.unlock)
2610
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2735
wt_lca1 = wt_this.controldir.sprout(
2736
'b-tree', b'B-id').open_workingtree()
2611
2737
wt_lca1.lock_read()
2612
2738
self.addCleanup(wt_lca1.unlock)
2613
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2739
wt_lca2 = wt_this.controldir.sprout(
2740
'c-tree', b'C-id').open_workingtree()
2614
2741
wt_lca2.lock_read()
2615
2742
self.addCleanup(wt_lca2.unlock)
2616
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2743
wt_other = wt_this.controldir.sprout(
2744
'other', b'E-id').open_workingtree()
2617
2745
wt_other.lock_read()
2618
2746
self.addCleanup(wt_other.unlock)
2619
2747
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2620
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2748
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2621
2749
entries = list(merge_obj._entries_lca())
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)),
2750
root_id = b'a-root-id'
2751
self.assertEqual([(b'a-id', False,
2752
((u'a', [u'a', u'b']), u'c', u'b'),
2753
((root_id, [root_id, root_id]), root_id, root_id),
2754
((u'a', [u'a', u'b']), u'c', u'b'),
2755
((False, [False, False]), False, False),
2758
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2759
((root_id, [root_id, root_id]), root_id, root_id),
2760
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2761
((False, [False, False]), False, False),
2633
2765
def test_nested_tree_unmodified(self):
2634
2766
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2635
2767
# 'tree-reference'
2636
2768
wt = self.make_branch_and_tree('tree',
2637
format='development-subtree')
2769
format='development-subtree')
2638
2770
wt.lock_write()
2639
2771
self.addCleanup(wt.unlock)
2640
2772
sub_tree = self.make_branch_and_tree('tree/sub-tree',
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')])
2773
format='development-subtree')
2774
wt.set_root_id(b'a-root-id')
2775
sub_tree.set_root_id(b'sub-tree-root')
2776
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2645
2777
sub_tree.add('file')
2646
sub_tree.commit('foo', rev_id='sub-A-id')
2778
sub_tree.commit('foo', rev_id=b'sub-A-id')
2647
2779
wt.add_reference(sub_tree)
2648
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2780
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2649
2781
# Now create a criss-cross merge in the parent, without modifying the
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)
2783
wt.commit('B', rev_id=b'B-id', recursive=None)
2784
wt.set_last_revision(b'A-id')
2785
wt.branch.set_last_revision_info(1, b'A-id')
2786
wt.commit('C', rev_id=b'C-id', recursive=None)
2787
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2788
wt.commit('E', rev_id=b'E-id', recursive=None)
2789
wt.set_parent_ids([b'B-id', b'C-id'])
2790
wt.branch.set_last_revision_info(2, b'B-id')
2791
wt.commit('D', rev_id=b'D-id', recursive=None)
2661
merger = _mod_merge.Merger.from_revision_ids(None,
2793
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2663
2794
merger.merge_type = _mod_merge.Merge3Merger
2664
2795
merge_obj = merger.make_merger()
2665
2796
entries = list(merge_obj._entries_lca())
2708
2838
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2709
2839
# 'tree-reference'
2710
2840
wt = self.make_branch_and_tree('tree',
2711
format='development-subtree')
2841
format='development-subtree')
2712
2842
wt.lock_write()
2713
2843
self.addCleanup(wt.unlock)
2714
2844
sub_tree = self.make_branch_and_tree('tree/sub',
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')])
2845
format='development-subtree')
2846
wt.set_root_id(b'a-root-id')
2847
sub_tree.set_root_id(b'sub-tree-root')
2848
self.build_tree_contents([('tree/sub/file', b'text1')])
2719
2849
sub_tree.add('file')
2720
sub_tree.commit('foo', rev_id='sub-A-id')
2850
sub_tree.commit('foo', rev_id=b'sub-A-id')
2721
2851
wt.add_reference(sub_tree)
2722
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2852
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2723
2853
# Now create a criss-cross merge in the parent, without modifying the
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')
2855
wt.commit('B', rev_id=b'B-id', recursive=None)
2856
wt.set_last_revision(b'A-id')
2857
wt.branch.set_last_revision_info(1, b'A-id')
2858
wt.commit('C', rev_id=b'C-id', recursive=None)
2859
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2730
2860
wt.rename_one('sub', 'alt_sub')
2731
wt.commit('E', rev_id='E-id', recursive=None)
2732
wt.set_last_revision('B-id')
2861
wt.commit('E', rev_id=b'E-id', recursive=None)
2862
wt.set_last_revision(b'B-id')
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)
2864
wt.set_parent_ids([b'B-id', b'C-id'])
2865
wt.branch.set_last_revision_info(2, b'B-id')
2866
wt.commit('D', rev_id=b'D-id', recursive=None)
2738
merger = _mod_merge.Merger.from_revision_ids(None,
2868
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2740
2869
merger.merge_type = _mod_merge.Merge3Merger
2741
2870
merge_obj = merger.make_merger()
2742
2871
entries = list(merge_obj._entries_lca())
2743
root_id = 'a-root-id'
2744
self.assertEqual([('sub-tree-root', False,
2872
root_id = b'a-root-id'
2873
self.assertEqual([(b'sub-tree-root', False,
2874
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2745
2875
((root_id, [root_id, root_id]), root_id, root_id),
2746
2876
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2747
((False, [False, False]), False, False)),
2877
((False, [False, False]), False, False),
2750
2881
def test_nested_tree_subtree_renamed_and_modified(self):
2751
2882
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2752
2883
# 'tree-reference'
2753
2884
wt = self.make_branch_and_tree('tree',
2754
format='development-subtree')
2885
format='development-subtree')
2755
2886
wt.lock_write()
2756
2887
self.addCleanup(wt.unlock)
2757
2888
sub_tree = self.make_branch_and_tree('tree/sub',
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')])
2889
format='development-subtree')
2890
wt.set_root_id(b'a-root-id')
2891
sub_tree.set_root_id(b'sub-tree-root')
2892
self.build_tree_contents([('tree/sub/file', b'text1')])
2762
2893
sub_tree.add('file')
2763
sub_tree.commit('foo', rev_id='sub-A-id')
2894
sub_tree.commit('foo', rev_id=b'sub-A-id')
2764
2895
wt.add_reference(sub_tree)
2765
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2896
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2766
2897
# Now create a criss-cross merge in the parent, without modifying the
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')
2899
wt.commit('B', rev_id=b'B-id', recursive=None)
2900
wt.set_last_revision(b'A-id')
2901
wt.branch.set_last_revision_info(1, b'A-id')
2902
wt.commit('C', rev_id=b'C-id', recursive=None)
2903
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2904
self.build_tree_contents([('tree/sub/file', b'text2')])
2905
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2775
2906
wt.rename_one('sub', 'alt_sub')
2776
wt.commit('E', rev_id='E-id', recursive=None)
2777
wt.set_last_revision('B-id')
2907
wt.commit('E', rev_id=b'E-id', recursive=None)
2908
wt.set_last_revision(b'B-id')
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)
2910
wt.set_parent_ids([b'B-id', b'C-id'])
2911
wt.branch.set_last_revision_info(2, b'B-id')
2912
wt.commit('D', rev_id=b'D-id', recursive=None)
2783
merger = _mod_merge.Merger.from_revision_ids(None,
2914
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2785
2915
merger.merge_type = _mod_merge.Merge3Merger
2786
2916
merge_obj = merger.make_merger()
2787
2917
entries = list(merge_obj._entries_lca())
2788
root_id = 'a-root-id'
2789
self.assertEqual([('sub-tree-root', False,
2918
root_id = b'a-root-id'
2919
self.assertEqual([(b'sub-tree-root', False,
2920
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2790
2921
((root_id, [root_id, root_id]), root_id, root_id),
2791
2922
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2792
((False, [False, False]), False, False)),
2923
((False, [False, False]), False, False),
2796
2928
class TestLCAMultiWay(tests.TestCase):
2798
2930
def assertLCAMultiWay(self, expected, base, lcas, other, this,
2799
2931
allow_overriding_lca=True):
2800
2932
self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2801
(base, lcas), other, this,
2802
allow_overriding_lca=allow_overriding_lca))
2933
(base, lcas), other, this,
2934
allow_overriding_lca=allow_overriding_lca))
2804
2936
def test_other_equal_equal_lcas(self):
2805
2937
"""Test when OTHER=LCA and all LCAs are identical."""
2806
2938
self.assertLCAMultiWay('this',
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)
2939
'bval', ['bval', 'bval'], 'bval', 'bval')
2940
self.assertLCAMultiWay('this',
2941
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2942
self.assertLCAMultiWay('this',
2943
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2944
self.assertLCAMultiWay('this',
2945
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2946
self.assertLCAMultiWay('this',
2947
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2817
2949
def test_other_equal_this(self):
2818
2950
"""Test when other and this are identical."""
2819
2951
self.assertLCAMultiWay('this',
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')
2952
'bval', ['bval', 'bval'], 'oval', 'oval')
2953
self.assertLCAMultiWay('this',
2954
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2955
self.assertLCAMultiWay('this',
2956
'bval', ['cval', 'dval'], 'oval', 'oval')
2957
self.assertLCAMultiWay('this',
2958
'bval', [None, 'lcaval'], 'oval', 'oval')
2959
self.assertLCAMultiWay('this',
2960
None, [None, 'lcaval'], 'oval', 'oval')
2961
self.assertLCAMultiWay('this',
2962
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2963
self.assertLCAMultiWay('this',
2964
None, ['cval', 'dval'], 'oval', 'oval')
2965
self.assertLCAMultiWay('this',
2966
None, ['cval', 'dval'], None, None)
2967
self.assertLCAMultiWay('this',
2968
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2838
2970
def test_no_lcas(self):
2839
2971
self.assertLCAMultiWay('this',
2840
'bval', [], 'bval', 'tval')
2972
'bval', [], 'bval', 'tval')
2841
2973
self.assertLCAMultiWay('other',
2842
'bval', [], 'oval', 'bval')
2974
'bval', [], 'oval', 'bval')
2843
2975
self.assertLCAMultiWay('conflict',
2844
'bval', [], 'oval', 'tval')
2976
'bval', [], 'oval', 'tval')
2845
2977
self.assertLCAMultiWay('this',
2846
'bval', [], 'oval', 'oval')
2978
'bval', [], 'oval', 'oval')
2848
2980
def test_lca_supersedes_other_lca(self):
2849
2981
"""If one lca == base, the other lca takes precedence"""
2850
2982
self.assertLCAMultiWay('this',
2851
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2983
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2852
2984
self.assertLCAMultiWay('this',
2853
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2985
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2854
2986
# This is actually considered a 'revert' because the 'lcaval' in LCAS
2855
2987
# supersedes the BASE val (in the other LCA) but then OTHER reverts it
2856
2988
# back to bval.
2857
2989
self.assertLCAMultiWay('other',
2858
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2990
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2859
2991
self.assertLCAMultiWay('conflict',
2860
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2992
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2862
2994
def test_other_and_this_pick_different_lca(self):
2863
2995
# OTHER and THIS resolve the lca conflict in different ways
2864
2996
self.assertLCAMultiWay('conflict',
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')
2997
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2998
self.assertLCAMultiWay('conflict',
2999
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
3000
self.assertLCAMultiWay('conflict',
3001
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2871
3003
def test_other_in_lca(self):
2872
3004
# OTHER takes a value of one of the LCAs, THIS takes a new value, which
2873
3005
# theoretically supersedes both LCA values and 'wins'
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)
3006
self.assertLCAMultiWay(
3007
'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
3008
self.assertLCAMultiWay(
3009
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3011
self.assertLCAMultiWay('conflict',
3013
'lca2val'], 'lca1val', 'newval',
3014
allow_overriding_lca=False)
3015
self.assertLCAMultiWay('conflict',
3016
'bval', ['lca1val', 'lca2val',
3017
'lca3val'], 'lca1val', 'newval',
3018
allow_overriding_lca=False)
2884
3019
# THIS reverted back to BASE, but that is an explicit supersede of all
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)
3021
self.assertLCAMultiWay(
3022
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3024
self.assertLCAMultiWay(
3025
'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3026
self.assertLCAMultiWay('conflict',
3027
'bval', ['lca1val', 'lca2val',
3028
'lca3val'], 'lca1val', 'bval',
3029
allow_overriding_lca=False)
3030
self.assertLCAMultiWay('conflict',
3031
'bval', ['lca1val', 'lca2val',
3032
'bval'], 'lca1val', 'bval',
3033
allow_overriding_lca=False)
2897
3035
def test_this_in_lca(self):
2898
3036
# THIS takes a value of one of the LCAs, OTHER takes a new value, which
2899
3037
# theoretically supersedes both LCA values and 'wins'
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)
3038
self.assertLCAMultiWay(
3039
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3040
self.assertLCAMultiWay(
3041
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3042
self.assertLCAMultiWay('conflict',
3044
'lca2val'], 'oval', 'lca1val',
3045
allow_overriding_lca=False)
3046
self.assertLCAMultiWay('conflict',
3048
'lca2val'], 'oval', 'lca2val',
3049
allow_overriding_lca=False)
2910
3050
# OTHER reverted back to BASE, but that is an explicit supersede of all
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)
3052
self.assertLCAMultiWay(
3053
'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
3055
self.assertLCAMultiWay(
3056
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
3057
'bval', 'lca3val', allow_overriding_lca=False)
2918
3059
def test_all_differ(self):
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')
3060
self.assertLCAMultiWay(
3061
'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3062
self.assertLCAMultiWay(
3063
'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
3065
self.assertLCAMultiWay(
3066
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
2927
3070
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3135
3280
project_wt.lock_read()
3136
3281
self.addCleanup(project_wt.unlock)
3137
3282
# The r1-lib1 revision should be merged into this one
3138
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3283
self.assertEqual([b'r1-project', b'r1-lib1'],
3284
project_wt.get_parent_ids())
3139
3285
new_lib1_id = project_wt.path2id('lib1')
3140
3286
self.assertNotEqual(None, new_lib1_id)
3141
3287
self.assertTreeEntriesEqual(
3142
3288
[('', root_id),
3143
('README', 'project-README-id'),
3144
('dir', 'project-dir-id'),
3289
('README', b'project-README-id'),
3290
('dir', b'project-dir-id'),
3145
3291
('lib1', new_lib1_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'),
3292
('dir/file.c', b'project-file.c-id'),
3293
('lib1/Makefile', b'lib1-Makefile-id'),
3294
('lib1/README', b'lib1-README-id'),
3295
('lib1/foo.c', b'lib1-foo.c-id'),
3152
3298
def test_name_conflict(self):
3153
3299
"""When the target directory name already exists a conflict is
3154
3300
generated and the original directory is renamed to foo.moved.
3156
3302
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3157
src_wt = self.setup_simple_branch('src', ['README'])
3303
self.setup_simple_branch('src', ['README'])
3158
3304
conflicts = self.do_merge_into('src', 'dest/dir')
3159
3305
self.assertEqual(1, conflicts)
3160
3306
dest_wt.lock_read()
3161
3307
self.addCleanup(dest_wt.unlock)
3162
3308
# The r1-lib1 revision should be merged into this one
3163
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3309
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3164
3310
self.assertTreeEntriesEqual(
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'),
3311
[('', b'dest-root-id'),
3312
('dir', b'src-root-id'),
3313
('dir.moved', b'dest-dir-id'),
3314
('dir/README', b'src-README-id'),
3315
('dir.moved/file.txt', b'dest-file.txt-id'),
3172
3318
def test_file_id_conflict(self):
3173
3319
"""A conflict is generated if the merge-into adds a file (or other
3174
3320
inventory entry) with a file-id that already exists in the target tree.
3176
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3322
self.setup_simple_branch('dest', ['file.txt'])
3177
3323
# Make a second tree with a file-id that will clash with file.txt in
3179
3325
src_wt = self.make_branch_and_tree('src')
3180
3326
self.build_tree(['src/README'])
3181
src_wt.add(['README'], ids=['dest-file.txt-id'])
3182
src_wt.commit("Rev 1 of src.", rev_id='r1-src')
3327
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3328
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3183
3329
conflicts = self.do_merge_into('src', 'dest/dir')
3184
3330
# This is an edge case that shouldn't happen to users very often. So
3185
3331
# we don't care really about the exact presentation of the conflict,