444
439
def test_make_merger(self):
445
440
this_tree = self.make_branch_and_tree('this')
446
this_tree.commit('rev1', rev_id=b'rev1')
441
this_tree.commit('rev1', rev_id='rev1')
447
442
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
this_tree.commit('rev2', rev_id='rev2a')
444
other_tree.commit('rev2', rev_id='rev2b')
450
445
this_tree.lock_write()
451
446
self.addCleanup(this_tree.unlock)
452
447
merger = _mod_merge.Merger.from_revision_ids(
453
this_tree, b'rev2b', other_branch=other_tree.branch)
448
this_tree, 'rev2b', other_branch=other_tree.branch)
454
449
merger.merge_type = _mod_merge.Merge3Merger
455
450
tree_merger = merger.make_merger()
456
451
self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
457
self.assertEqual(b'rev2b',
458
tree_merger.other_tree.get_revision_id())
459
self.assertEqual(b'rev1',
460
tree_merger.base_tree.get_revision_id())
452
self.assertEqual('rev2b',
453
tree_merger.other_tree.get_revision_id())
454
self.assertEqual('rev1',
455
tree_merger.base_tree.get_revision_id())
461
456
self.assertEqual(other_tree.branch, tree_merger.other_branch)
463
458
def test_make_preview_transform(self):
464
459
this_tree = self.make_branch_and_tree('this')
465
self.build_tree_contents([('this/file', b'1\n')])
466
this_tree.add('file', b'file-id')
467
this_tree.commit('rev1', rev_id=b'rev1')
460
self.build_tree_contents([('this/file', '1\n')])
461
this_tree.add('file', 'file-id')
462
this_tree.commit('rev1', rev_id='rev1')
468
463
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')
464
self.build_tree_contents([('this/file', '1\n2a\n')])
465
this_tree.commit('rev2', rev_id='rev2a')
466
self.build_tree_contents([('other/file', '2b\n1\n')])
467
other_tree.commit('rev2', rev_id='rev2b')
473
468
this_tree.lock_write()
474
469
self.addCleanup(this_tree.unlock)
475
470
merger = _mod_merge.Merger.from_revision_ids(
476
this_tree, b'rev2b', other_branch=other_tree.branch)
471
this_tree, 'rev2b', other_branch=other_tree.branch)
477
472
merger.merge_type = _mod_merge.Merge3Merger
478
473
tree_merger = merger.make_merger()
479
with tree_merger.make_preview_transform() as tt:
480
preview_tree = tt.get_preview_tree()
481
with this_tree.get_file('file') as tree_file:
482
self.assertEqual(b'1\n2a\n', tree_file.read())
483
with preview_tree.get_file('file') as preview_file:
484
self.assertEqual(b'2b\n1\n2a\n', preview_file.read())
474
tt = tree_merger.make_preview_transform()
475
self.addCleanup(tt.finalize)
476
preview_tree = tt.get_preview_tree()
477
tree_file = this_tree.get_file('file')
479
self.assertEqual('1\n2a\n', tree_file.read())
482
preview_file = preview_tree.get_file('file')
484
self.assertEqual('2b\n1\n2a\n', preview_file.read())
486
488
def test_do_merge(self):
487
489
this_tree = self.make_branch_and_tree('this')
488
self.build_tree_contents([('this/file', b'1\n')])
489
this_tree.add('file', b'file-id')
490
this_tree.commit('rev1', rev_id=b'rev1')
490
self.build_tree_contents([('this/file', '1\n')])
491
this_tree.add('file', 'file-id')
492
this_tree.commit('rev1', rev_id='rev1')
491
493
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')
494
self.build_tree_contents([('this/file', '1\n2a\n')])
495
this_tree.commit('rev2', rev_id='rev2a')
496
self.build_tree_contents([('other/file', '2b\n1\n')])
497
other_tree.commit('rev2', rev_id='rev2b')
496
498
this_tree.lock_write()
497
499
self.addCleanup(this_tree.unlock)
498
500
merger = _mod_merge.Merger.from_revision_ids(
499
this_tree, b'rev2b', other_branch=other_tree.branch)
501
this_tree, 'rev2b', other_branch=other_tree.branch)
500
502
merger.merge_type = _mod_merge.Merge3Merger
501
503
tree_merger = merger.make_merger()
502
504
tt = tree_merger.do_merge()
503
with this_tree.get_file('file') as tree_file:
504
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
505
tree_file = this_tree.get_file('file')
507
self.assertEqual('2b\n1\n2a\n', tree_file.read())
506
511
def test_merge_require_tree_root(self):
507
512
tree = self.make_branch_and_tree(".")
574
578
def add_uncommitted_version(self, key, parents, text):
575
579
self.plan_merge_vf.add_lines(key, parents,
576
[bytes([c]) + b'\n' for c in bytearray(text)])
580
[c+'\n' for c in text])
578
582
def setup_plan_merge(self):
579
self.add_rev(b'root', b'A', [], b'abc')
580
self.add_rev(b'root', b'B', [b'A'], b'acehg')
581
self.add_rev(b'root', b'C', [b'A'], b'fabg')
582
return _PlanMerge(b'B', b'C', self.plan_merge_vf, (b'root',))
583
self.add_rev('root', 'A', [], 'abc')
584
self.add_rev('root', 'B', ['A'], 'acehg')
585
self.add_rev('root', 'C', ['A'], 'fabg')
586
return _PlanMerge('B', 'C', self.plan_merge_vf, ('root',))
584
588
def setup_plan_merge_uncommitted(self):
585
self.add_version((b'root', b'A'), [], b'abc')
586
self.add_uncommitted_version(
587
(b'root', b'B:'), [(b'root', b'A')], b'acehg')
588
self.add_uncommitted_version(
589
(b'root', b'C:'), [(b'root', b'A')], b'fabg')
590
return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
589
self.add_version(('root', 'A'), [], 'abc')
590
self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
591
self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
592
return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
592
594
def test_base_from_plan(self):
593
595
self.setup_plan_merge()
594
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
596
plan = self.plan_merge_vf.plan_merge('B', 'C')
595
597
pwm = versionedfile.PlanWeaveMerge(plan)
596
self.assertEqual([b'a\n', b'b\n', b'c\n'], pwm.base_from_plan())
598
self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
598
600
def test_unique_lines(self):
599
601
plan = self.setup_plan_merge()
600
602
self.assertEqual(plan._unique_lines(
601
plan._get_matching_blocks(b'B', b'C')),
603
plan._get_matching_blocks('B', 'C')),
602
604
([1, 2, 3], [0, 2]))
604
606
def test_plan_merge(self):
605
607
self.setup_plan_merge()
606
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
608
plan = self.plan_merge_vf.plan_merge('B', 'C')
607
609
self.assertEqual([
609
('unchanged', b'a\n'),
610
('killed-a', b'b\n'),
611
('killed-b', b'c\n'),
611
('unchanged', 'a\n'),
618
620
def test_plan_merge_cherrypick(self):
619
self.add_rev(b'root', b'A', [], b'abc')
620
self.add_rev(b'root', b'B', [b'A'], b'abcde')
621
self.add_rev(b'root', b'C', [b'A'], b'abcefg')
622
self.add_rev(b'root', b'D', [b'A', b'B', b'C'], b'abcdegh')
623
my_plan = _PlanMerge(b'B', b'D', self.plan_merge_vf, (b'root',))
621
self.add_rev('root', 'A', [], 'abc')
622
self.add_rev('root', 'B', ['A'], 'abcde')
623
self.add_rev('root', 'C', ['A'], 'abcefg')
624
self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
625
my_plan = _PlanMerge('B', 'D', self.plan_merge_vf, ('root',))
624
626
# We shortcut when one text supersedes the other in the per-file graph.
625
627
# We don't actually need to compare the texts at this point.
626
628
self.assertEqual([
634
list(my_plan.plan_merge()))
636
list(my_plan.plan_merge()))
636
638
def test_plan_merge_no_common_ancestor(self):
637
self.add_rev(b'root', b'A', [], b'abc')
638
self.add_rev(b'root', b'B', [], b'xyz')
639
my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
639
self.add_rev('root', 'A', [], 'abc')
640
self.add_rev('root', 'B', [], 'xyz')
641
my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
640
642
self.assertEqual([
647
list(my_plan.plan_merge()))
649
list(my_plan.plan_merge()))
649
651
def test_plan_merge_tail_ancestors(self):
650
652
# The graph looks like this:
921
923
# XX unused ancestor, should not show up in the weave
925
927
# B C B & C both introduce a new line
929
931
# D E B & C are both merged, so both are common ancestors
930
932
# In the process of merging, both sides order the new
931
933
# lines differently
933
self.add_rev(b'root', b'XX', [], b'qrs')
934
self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
935
self.add_rev(b'root', b'B', [b'A'], b'abcdgef')
936
self.add_rev(b'root', b'C', [b'A'], b'abcdhef')
937
self.add_rev(b'root', b'D', [b'B', b'C'], b'abcdghef')
938
self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
939
plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
935
self.add_rev('root', 'XX', [], 'qrs')
936
self.add_rev('root', 'A', ['XX'], 'abcdef')
937
self.add_rev('root', 'B', ['A'], 'abcdgef')
938
self.add_rev('root', 'C', ['A'], 'abcdhef')
939
self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
940
self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
941
plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
940
942
self.assertEqual([
941
('unchanged', b'a\n'),
942
('unchanged', b'b\n'),
943
('unchanged', b'c\n'),
944
('unchanged', b'd\n'),
946
('unchanged', b'g\n'),
947
('killed-b', b'h\n'),
948
('unchanged', b'e\n'),
949
('unchanged', b'f\n'),
943
('unchanged', 'a\n'),
944
('unchanged', 'b\n'),
945
('unchanged', 'c\n'),
946
('unchanged', 'd\n'),
948
('unchanged', 'g\n'),
950
('unchanged', 'e\n'),
951
('unchanged', 'f\n'),
951
953
pwm = versionedfile.PlanWeaveMerge(plan)
952
self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
953
b''.join(pwm.base_from_plan()))
954
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
955
''.join(pwm.base_from_plan()))
954
956
# Reversing the order reverses the merge plan, and final order of 'hg'
956
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
958
plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
957
959
self.assertEqual([
958
('unchanged', b'a\n'),
959
('unchanged', b'b\n'),
960
('unchanged', b'c\n'),
961
('unchanged', b'd\n'),
963
('unchanged', b'h\n'),
964
('killed-b', b'g\n'),
965
('unchanged', b'e\n'),
966
('unchanged', b'f\n'),
960
('unchanged', 'a\n'),
961
('unchanged', 'b\n'),
962
('unchanged', 'c\n'),
963
('unchanged', 'd\n'),
965
('unchanged', 'h\n'),
967
('unchanged', 'e\n'),
968
('unchanged', 'f\n'),
968
970
pwm = versionedfile.PlanWeaveMerge(plan)
969
self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
970
b''.join(pwm.base_from_plan()))
971
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
972
''.join(pwm.base_from_plan()))
971
973
# This is where lca differs, in that it (fairly correctly) determines
972
974
# that there is a conflict because both sides resolved the merge
974
plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
976
plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
975
977
self.assertEqual([
976
('unchanged', b'a\n'),
977
('unchanged', b'b\n'),
978
('unchanged', b'c\n'),
979
('unchanged', b'd\n'),
980
('conflicted-b', b'h\n'),
981
('unchanged', b'g\n'),
982
('conflicted-a', b'h\n'),
983
('unchanged', b'e\n'),
984
('unchanged', b'f\n'),
978
('unchanged', 'a\n'),
979
('unchanged', 'b\n'),
980
('unchanged', 'c\n'),
981
('unchanged', 'd\n'),
982
('conflicted-b', 'h\n'),
983
('unchanged', 'g\n'),
984
('conflicted-a', 'h\n'),
985
('unchanged', 'e\n'),
986
('unchanged', 'f\n'),
986
988
pwm = versionedfile.PlanWeaveMerge(plan)
987
self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
988
b''.join(pwm.base_from_plan()))
989
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
990
''.join(pwm.base_from_plan()))
989
991
# Reversing it changes what line is doubled, but still gives a
990
992
# double-conflict
991
plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
993
plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
992
994
self.assertEqual([
993
('unchanged', b'a\n'),
994
('unchanged', b'b\n'),
995
('unchanged', b'c\n'),
996
('unchanged', b'd\n'),
997
('conflicted-b', b'g\n'),
998
('unchanged', b'h\n'),
999
('conflicted-a', b'g\n'),
1000
('unchanged', b'e\n'),
1001
('unchanged', b'f\n'),
995
('unchanged', 'a\n'),
996
('unchanged', 'b\n'),
997
('unchanged', 'c\n'),
998
('unchanged', 'd\n'),
999
('conflicted-b', 'g\n'),
1000
('unchanged', 'h\n'),
1001
('conflicted-a', 'g\n'),
1002
('unchanged', 'e\n'),
1003
('unchanged', 'f\n'),
1003
1005
pwm = versionedfile.PlanWeaveMerge(plan)
1004
self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
1005
b''.join(pwm.base_from_plan()))
1006
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
1007
''.join(pwm.base_from_plan()))
1007
1009
def assertRemoveExternalReferences(self, filtered_parent_map,
1008
1010
child_map, tails, parent_map):
1066
1068
self.assertPruneTails({1: []}, [5],
1067
1069
{1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
1068
1070
# Prune a partial chain
1069
self.assertPruneTails({1: [6], 6: []}, [5],
1071
self.assertPruneTails({1: [6], 6:[]}, [5],
1070
1072
{1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
1072
1074
# 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: []})
1075
self.assertPruneTails({1:[3], 3:[]}, [4, 5],
1076
{1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1077
self.assertPruneTails({1:[3], 3:[]}, [5, 4],
1078
{1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1078
1080
def test_subtract_plans(self):
1080
('unchanged', b'a\n'),
1082
('killed-a', b'c\n'),
1085
('killed-b', b'f\n'),
1086
('killed-b', b'g\n'),
1082
('unchanged', 'a\n'),
1084
('killed-a', 'c\n'),
1087
('killed-b', 'f\n'),
1088
('killed-b', 'g\n'),
1089
('unchanged', b'a\n'),
1091
('killed-a', b'c\n'),
1094
('killed-b', b'f\n'),
1095
('killed-b', b'i\n'),
1091
('unchanged', 'a\n'),
1093
('killed-a', 'c\n'),
1096
('killed-b', 'f\n'),
1097
('killed-b', 'i\n'),
1097
1099
subtracted_plan = [
1098
('unchanged', b'a\n'),
1100
('killed-a', b'c\n'),
1102
('unchanged', b'f\n'),
1103
('killed-b', b'i\n'),
1100
('unchanged', 'a\n'),
1102
('killed-a', 'c\n'),
1104
('unchanged', 'f\n'),
1105
('killed-b', 'i\n'),
1105
1107
self.assertEqual(subtracted_plan,
1106
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1108
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1108
1110
def setup_merge_with_base(self):
1109
self.add_rev(b'root', b'COMMON', [], b'abc')
1110
self.add_rev(b'root', b'THIS', [b'COMMON'], b'abcd')
1111
self.add_rev(b'root', b'BASE', [b'COMMON'], b'eabc')
1112
self.add_rev(b'root', b'OTHER', [b'BASE'], b'eafb')
1111
self.add_rev('root', 'COMMON', [], 'abc')
1112
self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
1113
self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
1114
self.add_rev('root', 'OTHER', ['BASE'], 'eafb')
1114
1116
def test_plan_merge_with_base(self):
1115
1117
self.setup_merge_with_base()
1116
plan = self.plan_merge_vf.plan_merge(b'THIS', b'OTHER', b'BASE')
1117
self.assertEqual([('unchanged', b'a\n'),
1119
('unchanged', b'b\n'),
1120
('killed-b', b'c\n'),
1118
plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
1119
self.assertEqual([('unchanged', 'a\n'),
1121
('unchanged', 'b\n'),
1122
('killed-b', 'c\n'),
1124
1126
def test_plan_lca_merge(self):
1125
1127
self.setup_plan_merge()
1126
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1128
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1127
1129
self.assertEqual([
1129
('unchanged', b'a\n'),
1130
('killed-b', b'c\n'),
1133
('killed-a', b'b\n'),
1134
('unchanged', b'g\n')],
1131
('unchanged', 'a\n'),
1132
('killed-b', 'c\n'),
1135
('killed-a', 'b\n'),
1136
('unchanged', 'g\n')],
1137
1139
def test_plan_lca_merge_uncommitted_files(self):
1138
1140
self.setup_plan_merge_uncommitted()
1139
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1141
plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1140
1142
self.assertEqual([
1142
('unchanged', b'a\n'),
1143
('killed-b', b'c\n'),
1146
('killed-a', b'b\n'),
1147
('unchanged', b'g\n')],
1144
('unchanged', 'a\n'),
1145
('killed-b', 'c\n'),
1148
('killed-a', 'b\n'),
1149
('unchanged', 'g\n')],
1150
1152
def test_plan_lca_merge_with_base(self):
1151
1153
self.setup_merge_with_base()
1152
plan = self.plan_merge_vf.plan_lca_merge(b'THIS', b'OTHER', b'BASE')
1153
self.assertEqual([('unchanged', b'a\n'),
1155
('unchanged', b'b\n'),
1156
('killed-b', b'c\n'),
1154
plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
1155
self.assertEqual([('unchanged', 'a\n'),
1157
('unchanged', 'b\n'),
1158
('killed-b', 'c\n'),
1160
1162
def test_plan_lca_merge_with_criss_cross(self):
1161
self.add_version((b'root', b'ROOT'), [], b'abc')
1163
self.add_version(('root', 'ROOT'), [], 'abc')
1162
1164
# each side makes a change
1163
self.add_version((b'root', b'REV1'), [(b'root', b'ROOT')], b'abcd')
1164
self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
1165
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1166
self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
1165
1167
# both sides merge, discarding others' changes
1166
self.add_version((b'root', b'LCA1'),
1167
[(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1168
self.add_version((b'root', b'LCA2'),
1169
[(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1170
plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1171
self.assertEqual([('new-b', b'f\n'),
1172
('unchanged', b'a\n'),
1173
('unchanged', b'b\n'),
1174
('unchanged', b'c\n'),
1175
('conflicted-a', b'd\n'),
1176
('conflicted-b', b'e\n'),
1168
self.add_version(('root', 'LCA1'),
1169
[('root', 'REV1'), ('root', 'REV2')], 'abcd')
1170
self.add_version(('root', 'LCA2'),
1171
[('root', 'REV1'), ('root', 'REV2')], 'fabce')
1172
plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
1173
self.assertEqual([('new-b', 'f\n'),
1174
('unchanged', 'a\n'),
1175
('unchanged', 'b\n'),
1176
('unchanged', 'c\n'),
1177
('conflicted-a', 'd\n'),
1178
('conflicted-b', 'e\n'),
1179
1181
def test_plan_lca_merge_with_null(self):
1180
self.add_version((b'root', b'A'), [], b'ab')
1181
self.add_version((b'root', b'B'), [], b'bc')
1182
plan = self.plan_merge_vf.plan_lca_merge(b'A', b'B')
1183
self.assertEqual([('new-a', b'a\n'),
1184
('unchanged', b'b\n'),
1182
self.add_version(('root', 'A'), [], 'ab')
1183
self.add_version(('root', 'B'), [], 'bc')
1184
plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
1185
self.assertEqual([('new-a', 'a\n'),
1186
('unchanged', 'b\n'),
1188
1190
def test_plan_merge_with_delete_and_change(self):
1189
self.add_rev(b'root', b'C', [], b'a')
1190
self.add_rev(b'root', b'A', [b'C'], b'b')
1191
self.add_rev(b'root', b'B', [b'C'], b'')
1192
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1193
self.assertEqual([('killed-both', b'a\n'),
1191
self.add_rev('root', 'C', [], 'a')
1192
self.add_rev('root', 'A', ['C'], 'b')
1193
self.add_rev('root', 'B', ['C'], '')
1194
plan = self.plan_merge_vf.plan_merge('A', 'B')
1195
self.assertEqual([('killed-both', 'a\n'),
1197
1199
def test_plan_merge_with_move_and_change(self):
1198
self.add_rev(b'root', b'C', [], b'abcd')
1199
self.add_rev(b'root', b'A', [b'C'], b'acbd')
1200
self.add_rev(b'root', b'B', [b'C'], b'aBcd')
1201
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1202
self.assertEqual([('unchanged', b'a\n'),
1204
('killed-b', b'b\n'),
1206
('killed-a', b'c\n'),
1207
('unchanged', b'd\n'),
1200
self.add_rev('root', 'C', [], 'abcd')
1201
self.add_rev('root', 'A', ['C'], 'acbd')
1202
self.add_rev('root', 'B', ['C'], 'aBcd')
1203
plan = self.plan_merge_vf.plan_merge('A', 'B')
1204
self.assertEqual([('unchanged', 'a\n'),
1206
('killed-b', 'b\n'),
1208
('killed-a', 'c\n'),
1209
('unchanged', 'd\n'),
1211
1213
class LoggingMerger(object):
1395
1400
class TestMergerEntriesLCA(TestMergerBase):
1397
1402
def make_merge_obj(self, builder, other_revision_id,
1398
interesting_files=None):
1403
interesting_files=None, interesting_ids=None):
1399
1404
merger = self.make_Merger(builder, other_revision_id,
1400
interesting_files=interesting_files)
1405
interesting_files=interesting_files,
1406
interesting_ids=interesting_ids)
1401
1407
return merger.make_merger()
1403
1409
def test_simple(self):
1404
1410
builder = self.get_builder()
1405
1411
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')
1412
[('add', (u'', 'a-root-id', 'directory', None)),
1413
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))],
1415
builder.build_snapshot(['A-id'],
1416
[('modify', ('a-id', 'a\nb\nC\nc\n'))],
1418
builder.build_snapshot(['A-id'],
1419
[('modify', ('a-id', 'a\nB\nb\nc\n'))],
1421
builder.build_snapshot(['C-id', 'B-id'],
1422
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))],
1424
builder.build_snapshot(['B-id', 'C-id'],
1425
[('modify', ('a-id', 'a\nB\nb\nC\nc\n'))],
1426
revision_id='D-id', )
1427
merge_obj = self.make_merge_obj(builder, 'E-id')
1423
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1424
for t in merge_obj._lca_trees])
1425
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1429
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1430
for t in merge_obj._lca_trees])
1431
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1426
1432
entries = list(merge_obj._entries_lca())
1428
1434
# (file_id, changed, parents, names, executable)
1429
1435
# BASE, lca1, lca2, OTHER, THIS
1430
root_id = b'a-root-id'
1431
self.assertEqual([(b'a-id', True,
1432
((u'a', [u'a', u'a']), u'a', u'a'),
1436
root_id = 'a-root-id'
1437
self.assertEqual([('a-id', True,
1433
1438
((root_id, [root_id, root_id]), root_id, root_id),
1434
1439
((u'a', [u'a', u'a']), u'a', u'a'),
1435
1440
((False, [False, False]), False, False)),
1438
1443
def test_not_in_base(self):
1439
1444
# LCAs all have the same last-modified revision for the file, as do
1450
1455
builder = self.get_builder()
1451
1456
builder.build_snapshot(None,
1452
[('add', (u'', b'a-root-id', 'directory', None))],
1453
revision_id=b'A-id')
1454
builder.build_snapshot([b'A-id'],
1455
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1456
revision_id=b'B-id')
1457
builder.build_snapshot([b'A-id'],
1458
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1459
revision_id=b'C-id')
1460
builder.build_snapshot([b'B-id', b'C-id'],
1461
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1462
revision_id=b'D-id')
1463
builder.build_snapshot([b'C-id', b'B-id'],
1464
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1465
revision_id=b'E-id')
1466
builder.build_snapshot([b'E-id', b'D-id'],
1467
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1468
revision_id=b'G-id')
1469
builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1470
merge_obj = self.make_merge_obj(builder, b'G-id')
1457
[('add', (u'', 'a-root-id', 'directory', None))],
1459
builder.build_snapshot(['A-id'],
1460
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
1462
builder.build_snapshot(['A-id'],
1463
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))],
1465
builder.build_snapshot(['B-id', 'C-id'],
1466
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))],
1468
builder.build_snapshot(['C-id', 'B-id'],
1469
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
1471
builder.build_snapshot(['E-id', 'D-id'],
1472
[('modify', (u'bar-id', 'd\ne\nf\nG\n'))],
1474
builder.build_snapshot(['D-id', 'E-id'], [], revision_id='F-id')
1475
merge_obj = self.make_merge_obj(builder, 'G-id')
1472
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1473
for t in merge_obj._lca_trees])
1474
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1477
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1478
for t in merge_obj._lca_trees])
1479
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1475
1480
entries = list(merge_obj._entries_lca())
1476
root_id = b'a-root-id'
1477
self.assertEqual([(b'bar-id', True,
1478
((None, [u'bar', u'bar']), u'bar', u'bar'),
1481
root_id = 'a-root-id'
1482
self.assertEqual([('bar-id', True,
1479
1483
((None, [root_id, root_id]), root_id, root_id),
1480
1484
((None, [u'bar', u'bar']), u'bar', u'bar'),
1481
1485
((None, [False, False]), False, False)),
1484
1488
def test_not_in_this(self):
1485
1489
builder = self.get_builder()
1486
1490
builder.build_snapshot(None,
1487
[('add', (u'', b'a-root-id', 'directory', None)),
1488
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1489
revision_id=b'A-id')
1490
builder.build_snapshot([b'A-id'],
1491
[('modify', ('a', b'a\nB\nb\nc\n'))],
1492
revision_id=b'B-id')
1493
builder.build_snapshot([b'A-id'],
1494
[('modify', ('a', b'a\nb\nC\nc\n'))],
1495
revision_id=b'C-id')
1496
builder.build_snapshot([b'C-id', b'B-id'],
1497
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1498
revision_id=b'E-id')
1499
builder.build_snapshot([b'B-id', b'C-id'],
1500
[('unversion', 'a')],
1501
revision_id=b'D-id')
1502
merge_obj = self.make_merge_obj(builder, b'E-id')
1491
[('add', (u'', 'a-root-id', 'directory', None)),
1492
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))],
1494
builder.build_snapshot(['A-id'],
1495
[('modify', ('a-id', 'a\nB\nb\nc\n'))],
1497
builder.build_snapshot(['A-id'],
1498
[('modify', ('a-id', 'a\nb\nC\nc\n'))],
1500
builder.build_snapshot(['C-id', 'B-id'],
1501
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))],
1503
builder.build_snapshot(['B-id', 'C-id'],
1504
[('unversion', 'a-id')],
1506
merge_obj = self.make_merge_obj(builder, 'E-id')
1504
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1505
for t in merge_obj._lca_trees])
1506
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1508
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1509
for t in merge_obj._lca_trees])
1510
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1508
1512
entries = list(merge_obj._entries_lca())
1509
root_id = b'a-root-id'
1510
self.assertEqual([(b'a-id', True,
1511
((u'a', [u'a', u'a']), u'a', None),
1513
root_id = 'a-root-id'
1514
self.assertEqual([('a-id', True,
1512
1515
((root_id, [root_id, root_id]), root_id, None),
1513
1516
((u'a', [u'a', u'a']), u'a', None),
1514
1517
((False, [False, False]), False, None)),
1517
1520
def test_file_not_in_one_lca(self):
1518
1521
# A # just root
1753
1753
builder = self.get_builder()
1754
1754
builder.build_snapshot(None,
1755
[('add', (u'', b'a-root-id', 'directory', None)),
1756
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1757
revision_id=b'A-id')
1758
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1759
builder.build_snapshot([b'A-id'],
1760
[('rename', ('foo', 'bar'))],
1761
revision_id=b'B-id')
1762
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1763
builder.build_snapshot([b'C-id', b'B-id'],
1764
[('rename', ('foo', 'bing'))],
1765
revision_id=b'E-id') # override to bing
1766
builder.build_snapshot([b'E-id', b'D-id'],
1767
[('rename', ('bing', 'barry'))],
1768
revision_id=b'G-id') # override to barry
1769
builder.build_snapshot([b'D-id', b'E-id'],
1770
[('rename', ('bar', 'bing'))],
1771
revision_id=b'F-id') # Merge in E's change
1772
merge_obj = self.make_merge_obj(builder, b'G-id')
1755
[('add', (u'', 'a-root-id', 'directory', None)),
1756
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1758
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1759
builder.build_snapshot(['A-id'],
1760
[('rename', ('foo', 'bar'))],
1762
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1763
builder.build_snapshot(['C-id', 'B-id'],
1764
[('rename', ('foo', 'bing'))],
1765
revision_id='E-id') # override to bing
1766
builder.build_snapshot(['E-id', 'D-id'],
1767
[('rename', ('bing', 'barry'))],
1768
revision_id='G-id') # override to barry
1769
builder.build_snapshot(['D-id', 'E-id'],
1770
[('rename', ('bar', 'bing'))],
1771
revision_id='F-id') # Merge in E's change
1772
merge_obj = self.make_merge_obj(builder, 'G-id')
1774
1774
self.expectFailure("We don't do an actual heads() check on lca values,"
1775
" or use the per-attribute graph",
1776
self.assertEqual, [], list(merge_obj._entries_lca()))
1775
" or use the per-attribute graph",
1776
self.assertEqual, [], list(merge_obj._entries_lca()))
1778
1778
def test_one_lca_accidentally_pruned(self):
1779
1779
# Another incorrect resolution from the same basic flaw:
1790
1790
# (superseding B).
1791
1791
builder = self.get_builder()
1792
1792
builder.build_snapshot(None,
1793
[('add', (u'', b'a-root-id', 'directory', None)),
1794
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1795
revision_id=b'A-id')
1796
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1797
builder.build_snapshot([b'A-id'],
1798
[('rename', ('foo', 'bar'))],
1799
revision_id=b'B-id')
1800
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1801
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1802
builder.build_snapshot([b'E-id', b'D-id'],
1803
[('rename', ('foo', 'bar'))],
1804
revision_id=b'G-id')
1805
builder.build_snapshot([b'D-id', b'E-id'],
1806
[('rename', ('bar', 'bing'))],
1807
revision_id=b'F-id') # should end up conflicting
1808
merge_obj = self.make_merge_obj(builder, b'G-id')
1793
[('add', (u'', 'a-root-id', 'directory', None)),
1794
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1796
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1797
builder.build_snapshot(['A-id'],
1798
[('rename', ('foo', 'bar'))],
1800
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1801
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1802
builder.build_snapshot(['E-id', 'D-id'],
1803
[('rename', ('foo', 'bar'))],
1805
builder.build_snapshot(['D-id', 'E-id'],
1806
[('rename', ('bar', 'bing'))],
1807
revision_id='F-id') # should end up conflicting
1808
merge_obj = self.make_merge_obj(builder, 'G-id')
1810
1810
entries = list(merge_obj._entries_lca())
1811
root_id = b'a-root-id'
1811
root_id = 'a-root-id'
1812
1812
self.expectFailure("We prune values from BASE even when relevant.",
1815
((root_id, [root_id, root_id]), root_id, root_id),
1816
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1817
((False, [False, False]), False, False)),
1815
((root_id, [root_id, root_id]), root_id, root_id),
1816
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1817
((False, [False, False]), False, False)),
1820
1820
def test_both_sides_revert(self):
1821
1821
# Both sides of a criss-cross revert the text to the lca
1904
1902
builder = self.get_builder()
1905
1903
builder.build_snapshot(None,
1906
[('add', (u'', b'a-root-id', 'directory', None)),
1907
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1908
revision_id=b'A-id')
1909
builder.build_snapshot([b'A-id'],
1910
[('modify', ('foo', b'B content\n'))],
1911
revision_id=b'B-id')
1912
builder.build_snapshot([b'A-id'],
1913
[('modify', ('foo', b'C content\n'))],
1914
revision_id=b'C-id')
1915
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1916
builder.build_snapshot([b'B-id', b'C-id'],
1917
[('modify', ('foo', b'C content\n'))],
1918
revision_id=b'D-id') # Same as E
1919
builder.build_snapshot([b'D-id'],
1920
[('modify', ('foo', b'F content\n'))],
1921
revision_id=b'F-id')
1922
merge_obj = self.make_merge_obj(builder, b'E-id')
1904
[('add', (u'', 'a-root-id', 'directory', None)),
1905
('add', (u'foo', 'foo-id', 'file', 'A content\n'))],
1907
builder.build_snapshot(['A-id'],
1908
[('modify', ('foo-id', 'B content\n'))],
1910
builder.build_snapshot(['A-id'],
1911
[('modify', ('foo-id', 'C content\n'))],
1913
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1914
builder.build_snapshot(['B-id', 'C-id'],
1915
[('modify', ('foo-id', 'C content\n'))],
1916
revision_id='D-id') # Same as E
1917
builder.build_snapshot(['D-id'],
1918
[('modify', ('foo-id', 'F content\n'))],
1920
merge_obj = self.make_merge_obj(builder, 'E-id')
1924
1922
entries = list(merge_obj._entries_lca())
1925
1923
self.expectFailure("We don't detect that LCA resolution was the"
1926
1924
" same on both sides",
1927
self.assertEqual, [], entries)
1925
self.assertEqual, [], entries)
1929
1927
def test_only_path_changed(self):
1930
1928
builder = self.get_builder()
1931
1929
builder.build_snapshot(None,
1932
[('add', (u'', b'a-root-id', 'directory', None)),
1933
('add', (u'a', b'a-id', 'file', b'content\n'))],
1934
revision_id=b'A-id')
1935
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1936
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1937
builder.build_snapshot([b'C-id', b'B-id'],
1938
[('rename', (u'a', u'b'))],
1939
revision_id=b'E-id')
1940
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1941
merge_obj = self.make_merge_obj(builder, b'E-id')
1930
[('add', (u'', 'a-root-id', 'directory', None)),
1931
('add', (u'a', 'a-id', 'file', 'content\n'))],
1933
builder.build_snapshot(['A-id'], [], revision_id='B-id')
1934
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1935
builder.build_snapshot(['C-id', 'B-id'],
1936
[('rename', (u'a', u'b'))],
1938
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1939
merge_obj = self.make_merge_obj(builder, 'E-id')
1942
1940
entries = list(merge_obj._entries_lca())
1943
root_id = b'a-root-id'
1941
root_id = 'a-root-id'
1944
1942
# The content was not changed, only the path
1945
self.assertEqual([(b'a-id', False,
1946
((u'a', [u'a', u'a']), u'b', u'a'),
1943
self.assertEqual([('a-id', False,
1947
1944
((root_id, [root_id, root_id]), root_id, root_id),
1948
1945
((u'a', [u'a', u'a']), u'b', u'a'),
1949
1946
((False, [False, False]), False, False)),
1952
1949
def test_kind_changed(self):
1953
1950
# Identical content, except 'D' changes a-id into a directory
1954
1951
builder = self.get_builder()
1955
1952
builder.build_snapshot(None,
1956
[('add', (u'', b'a-root-id', 'directory', None)),
1957
('add', (u'a', b'a-id', 'file', b'content\n'))],
1958
revision_id=b'A-id')
1959
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1960
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1961
builder.build_snapshot([b'C-id', b'B-id'],
1962
[('unversion', 'a'),
1964
('add', (u'a', b'a-id', 'directory', None))],
1965
revision_id=b'E-id')
1966
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1967
merge_obj = self.make_merge_obj(builder, b'E-id')
1953
[('add', (u'', 'a-root-id', 'directory', None)),
1954
('add', (u'a', 'a-id', 'file', 'content\n'))],
1956
builder.build_snapshot(['A-id'], [], revision_id='B-id')
1957
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1958
builder.build_snapshot(['C-id', 'B-id'],
1959
[('unversion', 'a-id'),
1961
('add', (u'a', 'a-id', 'directory', None))],
1963
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
1964
merge_obj = self.make_merge_obj(builder, 'E-id')
1968
1965
entries = list(merge_obj._entries_lca())
1969
root_id = b'a-root-id'
1966
root_id = 'a-root-id'
1970
1967
# Only the kind was changed (content)
1971
self.assertEqual([(b'a-id', True,
1972
((u'a', [u'a', u'a']), u'a', u'a'),
1968
self.assertEqual([('a-id', True,
1973
1969
((root_id, [root_id, root_id]), root_id, root_id),
1974
1970
((u'a', [u'a', u'a']), u'a', u'a'),
1975
1971
((False, [False, False]), False, False)),
1978
1974
def test_this_changed_kind(self):
1979
1975
# Identical content, but THIS changes a file to a directory
1980
1976
builder = self.get_builder()
1981
1977
builder.build_snapshot(None,
1982
[('add', (u'', b'a-root-id', 'directory', None)),
1983
('add', (u'a', b'a-id', 'file', b'content\n'))],
1984
revision_id=b'A-id')
1985
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1986
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1987
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1988
builder.build_snapshot([b'B-id', b'C-id'],
1989
[('unversion', 'a'),
1991
('add', (u'a', b'a-id', 'directory', None))],
1992
revision_id=b'D-id')
1993
merge_obj = self.make_merge_obj(builder, b'E-id')
1978
[('add', (u'', 'a-root-id', 'directory', None)),
1979
('add', (u'a', 'a-id', 'file', 'content\n'))],
1981
builder.build_snapshot(['A-id'], [], revision_id='B-id')
1982
builder.build_snapshot(['A-id'], [], revision_id='C-id')
1983
builder.build_snapshot(['C-id', 'B-id'], [], revision_id='E-id')
1984
builder.build_snapshot(['B-id', 'C-id'],
1985
[('unversion', 'a-id'),
1987
('add', (u'a', 'a-id', 'directory', None))],
1989
merge_obj = self.make_merge_obj(builder, 'E-id')
1994
1990
entries = list(merge_obj._entries_lca())
1995
1991
# Only the kind was changed (content)
1996
1992
self.assertEqual([], entries)
1999
1995
# Two files modified, but we should filter one of them
2000
1996
builder = self.get_builder()
2001
1997
builder.build_snapshot(None,
2002
[('add', (u'', b'a-root-id', 'directory', None)),
2003
('add', (u'a', b'a-id', 'file', b'content\n')),
2004
('add', (u'b', b'b-id', 'file', b'content\n'))],
2005
revision_id=b'A-id')
2006
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2007
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2008
builder.build_snapshot([b'C-id', b'B-id'],
2009
[('modify', ('a', b'new-content\n')),
2010
('modify', ('b', b'new-content\n'))],
2011
revision_id=b'E-id')
2012
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2013
merge_obj = self.make_merge_obj(builder, b'E-id',
1998
[('add', (u'', 'a-root-id', 'directory', None)),
1999
('add', (u'a', 'a-id', 'file', 'content\n')),
2000
('add', (u'b', 'b-id', 'file', 'content\n'))],
2002
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2003
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2004
builder.build_snapshot(['C-id', 'B-id'],
2005
[('modify', ('a-id', 'new-content\n')),
2006
('modify', ('b-id', 'new-content\n'))],
2008
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2009
merge_obj = self.make_merge_obj(builder, 'E-id',
2014
2010
interesting_files=['b'])
2015
2011
entries = list(merge_obj._entries_lca())
2016
root_id = b'a-root-id'
2017
self.assertEqual([(b'b-id', True,
2018
((u'b', [u'b', u'b']), u'b', u'b'),
2012
root_id = 'a-root-id'
2013
self.assertEqual([('b-id', True,
2019
2014
((root_id, [root_id, root_id]), root_id, root_id),
2020
2015
((u'b', [u'b', u'b']), u'b', u'b'),
2021
2016
((False, [False, False]), False, False)),
2024
2019
def test_interesting_file_in_this(self):
2025
2020
# This renamed the file, but it should still match the entry in other
2026
2021
builder = self.get_builder()
2027
2022
builder.build_snapshot(None,
2028
[('add', (u'', b'a-root-id', 'directory', None)),
2029
('add', (u'a', b'a-id', 'file', b'content\n')),
2030
('add', (u'b', b'b-id', 'file', b'content\n'))],
2031
revision_id=b'A-id')
2032
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2033
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2034
builder.build_snapshot([b'C-id', b'B-id'],
2035
[('modify', ('a', b'new-content\n')),
2036
('modify', ('b', b'new-content\n'))],
2037
revision_id=b'E-id')
2038
builder.build_snapshot([b'B-id', b'C-id'],
2039
[('rename', ('b', 'c'))],
2040
revision_id=b'D-id')
2041
merge_obj = self.make_merge_obj(builder, b'E-id',
2023
[('add', (u'', 'a-root-id', 'directory', None)),
2024
('add', (u'a', 'a-id', 'file', 'content\n')),
2025
('add', (u'b', 'b-id', 'file', 'content\n'))],
2027
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2028
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2029
builder.build_snapshot(['C-id', 'B-id'],
2030
[('modify', ('a-id', 'new-content\n')),
2031
('modify', ('b-id', 'new-content\n'))],
2033
builder.build_snapshot(['B-id', 'C-id'],
2034
[('rename', ('b', 'c'))],
2036
merge_obj = self.make_merge_obj(builder, 'E-id',
2042
2037
interesting_files=['c'])
2043
2038
entries = list(merge_obj._entries_lca())
2044
root_id = b'a-root-id'
2045
self.assertEqual([(b'b-id', True,
2046
((u'b', [u'b', u'b']), u'b', u'c'),
2039
root_id = 'a-root-id'
2040
self.assertEqual([('b-id', True,
2047
2041
((root_id, [root_id, root_id]), root_id, root_id),
2048
2042
((u'b', [u'b', u'b']), u'b', u'c'),
2049
2043
((False, [False, False]), False, False)),
2052
2046
def test_interesting_file_in_base(self):
2053
2047
# This renamed the file, but it should still match the entry in BASE
2054
2048
builder = self.get_builder()
2055
2049
builder.build_snapshot(None,
2056
[('add', (u'', b'a-root-id', 'directory', None)),
2057
('add', (u'a', b'a-id', 'file', b'content\n')),
2058
('add', (u'c', b'c-id', 'file', b'content\n'))],
2059
revision_id=b'A-id')
2060
builder.build_snapshot([b'A-id'],
2061
[('rename', ('c', 'b'))],
2062
revision_id=b'B-id')
2063
builder.build_snapshot([b'A-id'],
2064
[('rename', ('c', 'b'))],
2065
revision_id=b'C-id')
2066
builder.build_snapshot([b'C-id', b'B-id'],
2067
[('modify', ('a', b'new-content\n')),
2068
('modify', ('b', b'new-content\n'))],
2069
revision_id=b'E-id')
2070
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2071
merge_obj = self.make_merge_obj(builder, b'E-id',
2050
[('add', (u'', 'a-root-id', 'directory', None)),
2051
('add', (u'a', 'a-id', 'file', 'content\n')),
2052
('add', (u'c', 'c-id', 'file', 'content\n'))],
2054
builder.build_snapshot(['A-id'],
2055
[('rename', ('c', 'b'))],
2057
builder.build_snapshot(['A-id'],
2058
[('rename', ('c', 'b'))],
2060
builder.build_snapshot(['C-id', 'B-id'],
2061
[('modify', ('a-id', 'new-content\n')),
2062
('modify', ('c-id', 'new-content\n'))],
2064
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2065
merge_obj = self.make_merge_obj(builder, 'E-id',
2072
2066
interesting_files=['c'])
2073
2067
entries = list(merge_obj._entries_lca())
2074
root_id = b'a-root-id'
2075
self.assertEqual([(b'c-id', True,
2076
((u'c', [u'b', u'b']), u'b', u'b'),
2068
root_id = 'a-root-id'
2069
self.assertEqual([('c-id', True,
2077
2070
((root_id, [root_id, root_id]), root_id, root_id),
2078
2071
((u'c', [u'b', u'b']), u'b', u'b'),
2079
2072
((False, [False, False]), False, False)),
2082
2075
def test_interesting_file_in_lca(self):
2083
2076
# This renamed the file, but it should still match the entry in LCA
2084
2077
builder = self.get_builder()
2085
2078
builder.build_snapshot(None,
2086
[('add', (u'', b'a-root-id', 'directory', None)),
2087
('add', (u'a', b'a-id', 'file', b'content\n')),
2088
('add', (u'b', b'b-id', 'file', b'content\n'))],
2089
revision_id=b'A-id')
2090
builder.build_snapshot([b'A-id'],
2091
[('rename', ('b', 'c'))], revision_id=b'B-id')
2092
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2093
builder.build_snapshot([b'C-id', b'B-id'],
2094
[('modify', ('a', b'new-content\n')),
2095
('modify', ('b', b'new-content\n'))],
2096
revision_id=b'E-id')
2097
builder.build_snapshot([b'B-id', b'C-id'],
2098
[('rename', ('c', 'b'))], revision_id=b'D-id')
2099
merge_obj = self.make_merge_obj(builder, b'E-id',
2079
[('add', (u'', 'a-root-id', 'directory', None)),
2080
('add', (u'a', 'a-id', 'file', 'content\n')),
2081
('add', (u'b', 'b-id', 'file', 'content\n'))],
2083
builder.build_snapshot(['A-id'],
2084
[('rename', ('b', 'c'))], revision_id='B-id')
2085
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2086
builder.build_snapshot(['C-id', 'B-id'],
2087
[('modify', ('a-id', 'new-content\n')),
2088
('modify', ('b-id', 'new-content\n'))],
2090
builder.build_snapshot(['B-id', 'C-id'],
2091
[('rename', ('c', 'b'))], revision_id='D-id')
2092
merge_obj = self.make_merge_obj(builder, 'E-id',
2100
2093
interesting_files=['c'])
2101
2094
entries = list(merge_obj._entries_lca())
2102
root_id = b'a-root-id'
2103
self.assertEqual([(b'b-id', True,
2104
((u'b', [u'c', u'b']), u'b', u'b'),
2095
root_id = 'a-root-id'
2096
self.assertEqual([('b-id', True,
2105
2097
((root_id, [root_id, root_id]), root_id, root_id),
2106
2098
((u'b', [u'c', u'b']), u'b', u'b'),
2107
2099
((False, [False, False]), False, False)),
2110
def test_interesting_files(self):
2102
def test_interesting_ids(self):
2111
2103
# Two files modified, but we should filter one of them
2112
2104
builder = self.get_builder()
2113
2105
builder.build_snapshot(None,
2114
[('add', (u'', b'a-root-id', 'directory', None)),
2115
('add', (u'a', b'a-id', 'file', b'content\n')),
2116
('add', (u'b', b'b-id', 'file', b'content\n'))],
2117
revision_id=b'A-id')
2118
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2119
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2120
builder.build_snapshot([b'C-id', b'B-id'],
2121
[('modify', ('a', b'new-content\n')),
2122
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2123
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2124
merge_obj = self.make_merge_obj(builder, b'E-id',
2125
interesting_files=['b'])
2106
[('add', (u'', 'a-root-id', 'directory', None)),
2107
('add', (u'a', 'a-id', 'file', 'content\n')),
2108
('add', (u'b', 'b-id', 'file', 'content\n'))],
2110
builder.build_snapshot(['A-id'], [], revision_id='B-id')
2111
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2112
builder.build_snapshot(['C-id', 'B-id'],
2113
[('modify', ('a-id', 'new-content\n')),
2114
('modify', ('b-id', 'new-content\n'))], revision_id='E-id')
2115
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2116
merge_obj = self.make_merge_obj(builder, 'E-id',
2117
interesting_ids=['b-id'])
2126
2118
entries = list(merge_obj._entries_lca())
2127
root_id = b'a-root-id'
2128
self.assertEqual([(b'b-id', True,
2129
((u'b', [u'b', u'b']), u'b', u'b'),
2119
root_id = 'a-root-id'
2120
self.assertEqual([('b-id', True,
2130
2121
((root_id, [root_id, root_id]), root_id, root_id),
2131
2122
((u'b', [u'b', u'b']), u'b', u'b'),
2132
2123
((False, [False, False]), False, False)),
2136
2128
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2408
2404
wt.lock_write()
2409
2405
self.addCleanup(wt.unlock)
2410
2406
os.symlink('bar', 'path/foo')
2411
wt.add(['foo'], [b'foo-id'])
2412
wt.commit('A add symlink', rev_id=b'A-id')
2407
wt.add(['foo'], ['foo-id'])
2408
wt.commit('A add symlink', rev_id='A-id')
2413
2409
wt.rename_one('foo', 'barry')
2414
wt.commit('B foo => barry', rev_id=b'B-id')
2415
wt.set_last_revision(b'A-id')
2416
wt.branch.set_last_revision_info(1, b'A-id')
2410
wt.commit('B foo => barry', rev_id='B-id')
2411
wt.set_last_revision('A-id')
2412
wt.branch.set_last_revision_info(1, 'A-id')
2418
wt.commit('C', rev_id=b'C-id')
2419
wt.merge_from_branch(wt.branch, b'B-id')
2420
self.assertEqual('barry', wt.id2path(b'foo-id'))
2414
wt.commit('C', rev_id='C-id')
2415
wt.merge_from_branch(wt.branch, 'B-id')
2416
self.assertEqual('barry', wt.id2path('foo-id'))
2421
2417
self.assertEqual('bar', wt.get_symlink_target('barry'))
2422
wt.commit('E merges C & B', rev_id=b'E-id')
2418
wt.commit('E merges C & B', rev_id='E-id')
2423
2419
wt.rename_one('barry', 'blah')
2424
wt.commit('F barry => blah', rev_id=b'F-id')
2425
wt.set_last_revision(b'B-id')
2426
wt.branch.set_last_revision_info(2, b'B-id')
2420
wt.commit('F barry => blah', rev_id='F-id')
2421
wt.set_last_revision('B-id')
2422
wt.branch.set_last_revision_info(2, 'B-id')
2428
wt.merge_from_branch(wt.branch, b'C-id')
2429
wt.commit('D merges B & C', rev_id=b'D-id')
2430
self.assertEqual('barry', wt.id2path(b'foo-id'))
2424
wt.merge_from_branch(wt.branch, 'C-id')
2425
wt.commit('D merges B & C', rev_id='D-id')
2426
self.assertEqual('barry', wt.id2path('foo-id'))
2431
2427
# Check the output of the Merger object directly
2432
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2428
merger = _mod_merge.Merger.from_revision_ids(wt, 'F-id')
2433
2429
merger.merge_type = _mod_merge.Merge3Merger
2434
2430
merge_obj = merger.make_merger()
2435
2431
root_id = wt.path2id('')
2436
2432
entries = list(merge_obj._entries_lca())
2437
2433
# No content change, just a path change
2438
self.assertEqual([(b'foo-id', False,
2439
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2434
self.assertEqual([('foo-id', False,
2440
2435
((root_id, [root_id, root_id]), root_id, root_id),
2441
2436
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2442
2437
((False, [False, False]), False, False)),
2444
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2439
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2445
2440
self.assertEqual(0, conflicts)
2446
self.assertEqual('blah', wt.id2path(b'foo-id'))
2441
self.assertEqual('blah', wt.id2path('foo-id'))
2448
2443
def test_symlink_no_content_change(self):
2449
2444
self.requireFeature(features.SymlinkFeature)
2566
2560
wt.lock_write()
2567
2561
self.addCleanup(wt.unlock)
2568
2562
os.symlink('bar', 'path/foo')
2569
wt.add(['foo'], [b'foo-id'])
2570
wt.commit('add symlink', rev_id=b'A-id')
2563
wt.add(['foo'], ['foo-id'])
2564
wt.commit('add symlink', rev_id='A-id')
2571
2565
os.remove('path/foo')
2572
2566
os.symlink('baz', 'path/foo')
2573
wt.commit('foo => baz', rev_id=b'B-id')
2574
wt.set_last_revision(b'A-id')
2575
wt.branch.set_last_revision_info(1, b'A-id')
2567
wt.commit('foo => baz', rev_id='B-id')
2568
wt.set_last_revision('A-id')
2569
wt.branch.set_last_revision_info(1, 'A-id')
2577
wt.commit('C', rev_id=b'C-id')
2578
wt.merge_from_branch(wt.branch, b'B-id')
2571
wt.commit('C', rev_id='C-id')
2572
wt.merge_from_branch(wt.branch, 'B-id')
2579
2573
self.assertEqual('baz', wt.get_symlink_target('foo'))
2580
wt.commit('E merges C & B', rev_id=b'E-id')
2574
wt.commit('E merges C & B', rev_id='E-id')
2581
2575
os.remove('path/foo')
2582
2576
os.symlink('bing', 'path/foo')
2583
wt.commit('F foo => bing', rev_id=b'F-id')
2584
wt.set_last_revision(b'B-id')
2585
wt.branch.set_last_revision_info(2, b'B-id')
2577
wt.commit('F foo => bing', rev_id='F-id')
2578
wt.set_last_revision('B-id')
2579
wt.branch.set_last_revision_info(2, 'B-id')
2587
wt.merge_from_branch(wt.branch, b'C-id')
2588
wt.commit('D merges B & C', rev_id=b'D-id')
2589
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2581
wt.merge_from_branch(wt.branch, 'C-id')
2582
wt.commit('D merges B & C', rev_id='D-id')
2583
wt_base = wt.controldir.sprout('base', 'A-id').open_workingtree()
2590
2584
wt_base.lock_read()
2591
2585
self.addCleanup(wt_base.unlock)
2592
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2586
wt_lca1 = wt.controldir.sprout('b-tree', 'B-id').open_workingtree()
2593
2587
wt_lca1.lock_read()
2594
2588
self.addCleanup(wt_lca1.unlock)
2595
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2589
wt_lca2 = wt.controldir.sprout('c-tree', 'C-id').open_workingtree()
2596
2590
wt_lca2.lock_read()
2597
2591
self.addCleanup(wt_lca2.unlock)
2598
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2592
wt_other = wt.controldir.sprout('other', 'F-id').open_workingtree()
2599
2593
wt_other.lock_read()
2600
2594
self.addCleanup(wt_other.unlock)
2601
2595
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2602
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2596
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2603
2597
entries = list(merge_obj._entries_lca())
2604
2598
root_id = wt.path2id('')
2605
self.assertEqual([(b'foo-id', True,
2606
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2599
self.assertEqual([('foo-id', True,
2607
2600
((root_id, [root_id, root_id]), root_id, root_id),
2608
2601
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2609
2602
((False, [False, False]), False, False)),
2612
2605
def test_other_reverted_path_to_base(self):
2613
2606
# A Path at 'foo'
2621
2614
# F Path at 'foo'
2622
2615
builder = self.get_builder()
2623
2616
builder.build_snapshot(None,
2624
[('add', (u'', b'a-root-id', 'directory', None)),
2625
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2626
revision_id=b'A-id')
2627
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2628
builder.build_snapshot([b'A-id'],
2629
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2630
builder.build_snapshot([b'C-id', b'B-id'],
2631
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2632
builder.build_snapshot([b'E-id'],
2633
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2634
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2635
wt, conflicts = self.do_merge(builder, b'F-id')
2617
[('add', (u'', 'a-root-id', 'directory', None)),
2618
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))],
2620
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2621
builder.build_snapshot(['A-id'],
2622
[('rename', ('foo', 'bar'))], revision_id='B-id')
2623
builder.build_snapshot(['C-id', 'B-id'],
2624
[('rename', ('foo', 'bar'))], revision_id='E-id') # merge the rename
2625
builder.build_snapshot(['E-id'],
2626
[('rename', ('bar', 'foo'))], revision_id='F-id') # Rename back to BASE
2627
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2628
wt, conflicts = self.do_merge(builder, 'F-id')
2636
2629
self.assertEqual(0, conflicts)
2637
self.assertEqual('foo', wt.id2path(b'foo-id'))
2630
self.assertEqual('foo', wt.id2path('foo-id'))
2639
2632
def test_other_reverted_content_to_base(self):
2640
2633
builder = self.get_builder()
2641
2634
builder.build_snapshot(None,
2642
[('add', (u'', b'a-root-id', 'directory', None)),
2643
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2644
revision_id=b'A-id')
2645
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2646
builder.build_snapshot([b'A-id'],
2647
[('modify', ('foo', b'B content\n'))],
2648
revision_id=b'B-id')
2649
builder.build_snapshot([b'C-id', b'B-id'],
2650
[('modify', ('foo', b'B content\n'))],
2651
revision_id=b'E-id') # merge the content
2652
builder.build_snapshot([b'E-id'],
2653
[('modify', ('foo', b'base content\n'))],
2654
revision_id=b'F-id') # Revert back to BASE
2655
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2656
wt, conflicts = self.do_merge(builder, b'F-id')
2635
[('add', (u'', 'a-root-id', 'directory', None)),
2636
('add', (u'foo', 'foo-id', 'file', 'base content\n'))],
2638
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2639
builder.build_snapshot(['A-id'],
2640
[('modify', ('foo-id', 'B content\n'))],
2642
builder.build_snapshot(['C-id', 'B-id'],
2643
[('modify', ('foo-id', 'B content\n'))],
2644
revision_id='E-id') # merge the content
2645
builder.build_snapshot(['E-id'],
2646
[('modify', ('foo-id', 'base content\n'))],
2647
revision_id='F-id') # Revert back to BASE
2648
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2649
wt, conflicts = self.do_merge(builder, 'F-id')
2657
2650
self.assertEqual(0, conflicts)
2658
2651
# TODO: We need to use the per-file graph to properly select a BASE
2659
2652
# before this will work. Or at least use the LCA trees to find
2660
2653
# the appropriate content base. (which is B, not A).
2661
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2654
self.assertEqual('base content\n', wt.get_file_text('foo'))
2663
2656
def test_other_modified_content(self):
2664
2657
builder = self.get_builder()
2665
2658
builder.build_snapshot(None,
2666
[('add', (u'', b'a-root-id', 'directory', None)),
2667
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2668
revision_id=b'A-id')
2669
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2670
builder.build_snapshot([b'A-id'],
2671
[('modify', ('foo', b'B content\n'))],
2672
revision_id=b'B-id')
2673
builder.build_snapshot([b'C-id', b'B-id'],
2674
[('modify', ('foo', b'B content\n'))],
2675
revision_id=b'E-id') # merge the content
2676
builder.build_snapshot([b'E-id'],
2677
[('modify', ('foo', b'F content\n'))],
2678
revision_id=b'F-id') # Override B content
2679
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2680
wt, conflicts = self.do_merge(builder, b'F-id')
2659
[('add', (u'', 'a-root-id', 'directory', None)),
2660
('add', (u'foo', 'foo-id', 'file', 'base content\n'))],
2662
builder.build_snapshot(['A-id'], [], revision_id='C-id')
2663
builder.build_snapshot(['A-id'],
2664
[('modify', ('foo-id', 'B content\n'))],
2666
builder.build_snapshot(['C-id', 'B-id'],
2667
[('modify', ('foo-id', 'B content\n'))],
2668
revision_id='E-id') # merge the content
2669
builder.build_snapshot(['E-id'],
2670
[('modify', ('foo-id', 'F content\n'))],
2671
revision_id='F-id') # Override B content
2672
builder.build_snapshot(['B-id', 'C-id'], [], revision_id='D-id')
2673
wt, conflicts = self.do_merge(builder, 'F-id')
2681
2674
self.assertEqual(0, conflicts)
2682
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2675
self.assertEqual('F content\n', wt.get_file_text('foo'))
2684
2677
def test_all_wt(self):
2685
2678
"""Check behavior if all trees are Working Trees."""
2693
2686
# D E E updates content, renames 'b' => 'c'
2694
2687
builder = self.get_builder()
2695
2688
builder.build_snapshot(None,
2696
[('add', (u'', b'a-root-id', 'directory', None)),
2697
('add', (u'a', b'a-id', 'file', b'base content\n')),
2698
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2699
revision_id=b'A-id')
2700
builder.build_snapshot([b'A-id'],
2701
[('modify', ('foo', b'B content\n'))],
2702
revision_id=b'B-id')
2703
builder.build_snapshot([b'A-id'],
2704
[('rename', ('a', 'b'))],
2705
revision_id=b'C-id')
2706
builder.build_snapshot([b'C-id', b'B-id'],
2707
[('rename', ('b', 'c')),
2708
('modify', ('foo', b'E content\n'))],
2709
revision_id=b'E-id')
2710
builder.build_snapshot([b'B-id', b'C-id'],
2711
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2689
[('add', (u'', 'a-root-id', 'directory', None)),
2690
('add', (u'a', 'a-id', 'file', 'base content\n')),
2691
('add', (u'foo', 'foo-id', 'file', 'base content\n'))],
2693
builder.build_snapshot(['A-id'],
2694
[('modify', ('foo-id', 'B content\n'))],
2696
builder.build_snapshot(['A-id'],
2697
[('rename', ('a', 'b'))],
2699
builder.build_snapshot(['C-id', 'B-id'],
2700
[('rename', ('b', 'c')),
2701
('modify', ('foo-id', 'E content\n'))],
2703
builder.build_snapshot(['B-id', 'C-id'],
2704
[('rename', ('a', 'b'))], revision_id='D-id') # merged change
2712
2705
wt_this = self.get_wt_from_builder(builder)
2713
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2706
wt_base = wt_this.controldir.sprout('base', 'A-id').open_workingtree()
2714
2707
wt_base.lock_read()
2715
2708
self.addCleanup(wt_base.unlock)
2716
wt_lca1 = wt_this.controldir.sprout(
2717
'b-tree', b'B-id').open_workingtree()
2709
wt_lca1 = wt_this.controldir.sprout('b-tree', 'B-id').open_workingtree()
2718
2710
wt_lca1.lock_read()
2719
2711
self.addCleanup(wt_lca1.unlock)
2720
wt_lca2 = wt_this.controldir.sprout(
2721
'c-tree', b'C-id').open_workingtree()
2712
wt_lca2 = wt_this.controldir.sprout('c-tree', 'C-id').open_workingtree()
2722
2713
wt_lca2.lock_read()
2723
2714
self.addCleanup(wt_lca2.unlock)
2724
wt_other = wt_this.controldir.sprout(
2725
'other', b'E-id').open_workingtree()
2715
wt_other = wt_this.controldir.sprout('other', 'E-id').open_workingtree()
2726
2716
wt_other.lock_read()
2727
2717
self.addCleanup(wt_other.unlock)
2728
2718
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2729
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2719
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2730
2720
entries = list(merge_obj._entries_lca())
2731
root_id = b'a-root-id'
2732
self.assertEqual([(b'a-id', False,
2733
((u'a', [u'a', u'b']), u'c', u'b'),
2734
((root_id, [root_id, root_id]), root_id, root_id),
2735
((u'a', [u'a', u'b']), u'c', u'b'),
2736
((False, [False, False]), False, False)),
2738
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2739
((root_id, [root_id, root_id]), root_id, root_id),
2740
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2741
((False, [False, False]), False, False)),
2721
root_id = 'a-root-id'
2722
self.assertEqual([('a-id', False,
2723
((root_id, [root_id, root_id]), root_id, root_id),
2724
((u'a', [u'a', u'b']), u'c', u'b'),
2725
((False, [False, False]), False, False)),
2727
((root_id, [root_id, root_id]), root_id, root_id),
2728
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2729
((False, [False, False]), False, False)),
2744
2732
def test_nested_tree_unmodified(self):
2745
2733
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2746
2734
# 'tree-reference'
2747
2735
wt = self.make_branch_and_tree('tree',
2748
format='development-subtree')
2736
format='development-subtree')
2749
2737
wt.lock_write()
2750
2738
self.addCleanup(wt.unlock)
2751
2739
sub_tree = self.make_branch_and_tree('tree/sub-tree',
2752
format='development-subtree')
2753
wt.set_root_id(b'a-root-id')
2754
sub_tree.set_root_id(b'sub-tree-root')
2755
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2740
format='development-subtree')
2741
wt.set_root_id('a-root-id')
2742
sub_tree.set_root_id('sub-tree-root')
2743
self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2756
2744
sub_tree.add('file')
2757
sub_tree.commit('foo', rev_id=b'sub-A-id')
2745
sub_tree.commit('foo', rev_id='sub-A-id')
2758
2746
wt.add_reference(sub_tree)
2759
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2747
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2760
2748
# Now create a criss-cross merge in the parent, without modifying the
2762
wt.commit('B', rev_id=b'B-id', recursive=None)
2763
wt.set_last_revision(b'A-id')
2764
wt.branch.set_last_revision_info(1, b'A-id')
2765
wt.commit('C', rev_id=b'C-id', recursive=None)
2766
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2767
wt.commit('E', rev_id=b'E-id', recursive=None)
2768
wt.set_parent_ids([b'B-id', b'C-id'])
2769
wt.branch.set_last_revision_info(2, b'B-id')
2770
wt.commit('D', rev_id=b'D-id', recursive=None)
2750
wt.commit('B', rev_id='B-id', recursive=None)
2751
wt.set_last_revision('A-id')
2752
wt.branch.set_last_revision_info(1, 'A-id')
2753
wt.commit('C', rev_id='C-id', recursive=None)
2754
wt.merge_from_branch(wt.branch, to_revision='B-id')
2755
wt.commit('E', rev_id='E-id', recursive=None)
2756
wt.set_parent_ids(['B-id', 'C-id'])
2757
wt.branch.set_last_revision_info(2, 'B-id')
2758
wt.commit('D', rev_id='D-id', recursive=None)
2772
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2760
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2773
2761
merger.merge_type = _mod_merge.Merge3Merger
2774
2762
merge_obj = merger.make_merger()
2775
2763
entries = list(merge_obj._entries_lca())
2817
2805
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2818
2806
# 'tree-reference'
2819
2807
wt = self.make_branch_and_tree('tree',
2820
format='development-subtree')
2808
format='development-subtree')
2821
2809
wt.lock_write()
2822
2810
self.addCleanup(wt.unlock)
2823
2811
sub_tree = self.make_branch_and_tree('tree/sub',
2824
format='development-subtree')
2825
wt.set_root_id(b'a-root-id')
2826
sub_tree.set_root_id(b'sub-tree-root')
2827
self.build_tree_contents([('tree/sub/file', b'text1')])
2812
format='development-subtree')
2813
wt.set_root_id('a-root-id')
2814
sub_tree.set_root_id('sub-tree-root')
2815
self.build_tree_contents([('tree/sub/file', 'text1')])
2828
2816
sub_tree.add('file')
2829
sub_tree.commit('foo', rev_id=b'sub-A-id')
2817
sub_tree.commit('foo', rev_id='sub-A-id')
2830
2818
wt.add_reference(sub_tree)
2831
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2819
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2832
2820
# Now create a criss-cross merge in the parent, without modifying the
2834
wt.commit('B', rev_id=b'B-id', recursive=None)
2835
wt.set_last_revision(b'A-id')
2836
wt.branch.set_last_revision_info(1, b'A-id')
2837
wt.commit('C', rev_id=b'C-id', recursive=None)
2838
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2822
wt.commit('B', rev_id='B-id', recursive=None)
2823
wt.set_last_revision('A-id')
2824
wt.branch.set_last_revision_info(1, 'A-id')
2825
wt.commit('C', rev_id='C-id', recursive=None)
2826
wt.merge_from_branch(wt.branch, to_revision='B-id')
2839
2827
wt.rename_one('sub', 'alt_sub')
2840
wt.commit('E', rev_id=b'E-id', recursive=None)
2841
wt.set_last_revision(b'B-id')
2828
wt.commit('E', rev_id='E-id', recursive=None)
2829
wt.set_last_revision('B-id')
2843
wt.set_parent_ids([b'B-id', b'C-id'])
2844
wt.branch.set_last_revision_info(2, b'B-id')
2845
wt.commit('D', rev_id=b'D-id', recursive=None)
2831
wt.set_parent_ids(['B-id', 'C-id'])
2832
wt.branch.set_last_revision_info(2, 'B-id')
2833
wt.commit('D', rev_id='D-id', recursive=None)
2847
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2835
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2848
2836
merger.merge_type = _mod_merge.Merge3Merger
2849
2837
merge_obj = merger.make_merger()
2850
2838
entries = list(merge_obj._entries_lca())
2851
root_id = b'a-root-id'
2852
self.assertEqual([(b'sub-tree-root', False,
2853
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2839
root_id = 'a-root-id'
2840
self.assertEqual([('sub-tree-root', False,
2854
2841
((root_id, [root_id, root_id]), root_id, root_id),
2855
2842
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2856
2843
((False, [False, False]), False, False)),
2859
2846
def test_nested_tree_subtree_renamed_and_modified(self):
2860
2847
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2861
2848
# 'tree-reference'
2862
2849
wt = self.make_branch_and_tree('tree',
2863
format='development-subtree')
2850
format='development-subtree')
2864
2851
wt.lock_write()
2865
2852
self.addCleanup(wt.unlock)
2866
2853
sub_tree = self.make_branch_and_tree('tree/sub',
2867
format='development-subtree')
2868
wt.set_root_id(b'a-root-id')
2869
sub_tree.set_root_id(b'sub-tree-root')
2870
self.build_tree_contents([('tree/sub/file', b'text1')])
2854
format='development-subtree')
2855
wt.set_root_id('a-root-id')
2856
sub_tree.set_root_id('sub-tree-root')
2857
self.build_tree_contents([('tree/sub/file', 'text1')])
2871
2858
sub_tree.add('file')
2872
sub_tree.commit('foo', rev_id=b'sub-A-id')
2859
sub_tree.commit('foo', rev_id='sub-A-id')
2873
2860
wt.add_reference(sub_tree)
2874
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2861
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2875
2862
# Now create a criss-cross merge in the parent, without modifying the
2877
wt.commit('B', rev_id=b'B-id', recursive=None)
2878
wt.set_last_revision(b'A-id')
2879
wt.branch.set_last_revision_info(1, b'A-id')
2880
wt.commit('C', rev_id=b'C-id', recursive=None)
2881
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2882
self.build_tree_contents([('tree/sub/file', b'text2')])
2883
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2864
wt.commit('B', rev_id='B-id', recursive=None)
2865
wt.set_last_revision('A-id')
2866
wt.branch.set_last_revision_info(1, 'A-id')
2867
wt.commit('C', rev_id='C-id', recursive=None)
2868
wt.merge_from_branch(wt.branch, to_revision='B-id')
2869
self.build_tree_contents([('tree/sub/file', 'text2')])
2870
sub_tree.commit('modify contents', rev_id='sub-B-id')
2884
2871
wt.rename_one('sub', 'alt_sub')
2885
wt.commit('E', rev_id=b'E-id', recursive=None)
2886
wt.set_last_revision(b'B-id')
2872
wt.commit('E', rev_id='E-id', recursive=None)
2873
wt.set_last_revision('B-id')
2888
wt.set_parent_ids([b'B-id', b'C-id'])
2889
wt.branch.set_last_revision_info(2, b'B-id')
2890
wt.commit('D', rev_id=b'D-id', recursive=None)
2875
wt.set_parent_ids(['B-id', 'C-id'])
2876
wt.branch.set_last_revision_info(2, 'B-id')
2877
wt.commit('D', rev_id='D-id', recursive=None)
2892
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2879
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2893
2880
merger.merge_type = _mod_merge.Merge3Merger
2894
2881
merge_obj = merger.make_merger()
2895
2882
entries = list(merge_obj._entries_lca())
2896
root_id = b'a-root-id'
2897
self.assertEqual([(b'sub-tree-root', False,
2898
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2883
root_id = 'a-root-id'
2884
self.assertEqual([('sub-tree-root', False,
2899
2885
((root_id, [root_id, root_id]), root_id, root_id),
2900
2886
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2901
2887
((False, [False, False]), False, False)),
2905
2891
class TestLCAMultiWay(tests.TestCase):
2907
2893
def assertLCAMultiWay(self, expected, base, lcas, other, this,
2908
2894
allow_overriding_lca=True):
2909
2895
self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2910
(base, lcas), other, this,
2911
allow_overriding_lca=allow_overriding_lca))
2896
(base, lcas), other, this,
2897
allow_overriding_lca=allow_overriding_lca))
2913
2899
def test_other_equal_equal_lcas(self):
2914
2900
"""Test when OTHER=LCA and all LCAs are identical."""
2915
2901
self.assertLCAMultiWay('this',
2916
'bval', ['bval', 'bval'], 'bval', 'bval')
2917
self.assertLCAMultiWay('this',
2918
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2919
self.assertLCAMultiWay('this',
2920
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2921
self.assertLCAMultiWay('this',
2922
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2923
self.assertLCAMultiWay('this',
2924
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2902
'bval', ['bval', 'bval'], 'bval', 'bval')
2903
self.assertLCAMultiWay('this',
2904
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2905
self.assertLCAMultiWay('this',
2906
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2907
self.assertLCAMultiWay('this',
2908
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2909
self.assertLCAMultiWay('this',
2910
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2926
2912
def test_other_equal_this(self):
2927
2913
"""Test when other and this are identical."""
2928
2914
self.assertLCAMultiWay('this',
2929
'bval', ['bval', 'bval'], 'oval', 'oval')
2930
self.assertLCAMultiWay('this',
2931
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2932
self.assertLCAMultiWay('this',
2933
'bval', ['cval', 'dval'], 'oval', 'oval')
2934
self.assertLCAMultiWay('this',
2935
'bval', [None, 'lcaval'], 'oval', 'oval')
2936
self.assertLCAMultiWay('this',
2937
None, [None, 'lcaval'], 'oval', 'oval')
2938
self.assertLCAMultiWay('this',
2939
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2940
self.assertLCAMultiWay('this',
2941
None, ['cval', 'dval'], 'oval', 'oval')
2942
self.assertLCAMultiWay('this',
2943
None, ['cval', 'dval'], None, None)
2944
self.assertLCAMultiWay('this',
2945
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2915
'bval', ['bval', 'bval'], 'oval', 'oval')
2916
self.assertLCAMultiWay('this',
2917
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2918
self.assertLCAMultiWay('this',
2919
'bval', ['cval', 'dval'], 'oval', 'oval')
2920
self.assertLCAMultiWay('this',
2921
'bval', [None, 'lcaval'], 'oval', 'oval')
2922
self.assertLCAMultiWay('this',
2923
None, [None, 'lcaval'], 'oval', 'oval')
2924
self.assertLCAMultiWay('this',
2925
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2926
self.assertLCAMultiWay('this',
2927
None, ['cval', 'dval'], 'oval', 'oval')
2928
self.assertLCAMultiWay('this',
2929
None, ['cval', 'dval'], None, None)
2930
self.assertLCAMultiWay('this',
2931
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2947
2933
def test_no_lcas(self):
2948
2934
self.assertLCAMultiWay('this',
2949
'bval', [], 'bval', 'tval')
2935
'bval', [], 'bval', 'tval')
2950
2936
self.assertLCAMultiWay('other',
2951
'bval', [], 'oval', 'bval')
2937
'bval', [], 'oval', 'bval')
2952
2938
self.assertLCAMultiWay('conflict',
2953
'bval', [], 'oval', 'tval')
2939
'bval', [], 'oval', 'tval')
2954
2940
self.assertLCAMultiWay('this',
2955
'bval', [], 'oval', 'oval')
2941
'bval', [], 'oval', 'oval')
2957
2943
def test_lca_supersedes_other_lca(self):
2958
2944
"""If one lca == base, the other lca takes precedence"""
2959
2945
self.assertLCAMultiWay('this',
2960
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2946
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2961
2947
self.assertLCAMultiWay('this',
2962
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2948
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2963
2949
# This is actually considered a 'revert' because the 'lcaval' in LCAS
2964
2950
# supersedes the BASE val (in the other LCA) but then OTHER reverts it
2965
2951
# back to bval.
2966
2952
self.assertLCAMultiWay('other',
2967
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2953
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2968
2954
self.assertLCAMultiWay('conflict',
2969
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2955
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2971
2957
def test_other_and_this_pick_different_lca(self):
2972
2958
# OTHER and THIS resolve the lca conflict in different ways
2973
2959
self.assertLCAMultiWay('conflict',
2974
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2975
self.assertLCAMultiWay('conflict',
2976
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2977
self.assertLCAMultiWay('conflict',
2978
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2960
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2961
self.assertLCAMultiWay('conflict',
2962
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2963
self.assertLCAMultiWay('conflict',
2964
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2980
2966
def test_other_in_lca(self):
2981
2967
# OTHER takes a value of one of the LCAs, THIS takes a new value, which
2982
2968
# theoretically supersedes both LCA values and 'wins'
2983
self.assertLCAMultiWay(
2984
'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2985
self.assertLCAMultiWay(
2986
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
2988
self.assertLCAMultiWay('conflict',
2990
'lca2val'], 'lca1val', 'newval',
2991
allow_overriding_lca=False)
2992
self.assertLCAMultiWay('conflict',
2993
'bval', ['lca1val', 'lca2val',
2994
'lca3val'], 'lca1val', 'newval',
2995
allow_overriding_lca=False)
2969
self.assertLCAMultiWay('this',
2970
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2971
self.assertLCAMultiWay('this',
2972
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
2973
self.assertLCAMultiWay('conflict',
2974
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2975
allow_overriding_lca=False)
2976
self.assertLCAMultiWay('conflict',
2977
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2978
allow_overriding_lca=False)
2996
2979
# THIS reverted back to BASE, but that is an explicit supersede of all
2998
self.assertLCAMultiWay(
2999
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3001
self.assertLCAMultiWay(
3002
'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3003
self.assertLCAMultiWay('conflict',
3004
'bval', ['lca1val', 'lca2val',
3005
'lca3val'], 'lca1val', 'bval',
3006
allow_overriding_lca=False)
3007
self.assertLCAMultiWay('conflict',
3008
'bval', ['lca1val', 'lca2val',
3009
'bval'], 'lca1val', 'bval',
3010
allow_overriding_lca=False)
2981
self.assertLCAMultiWay('this',
2982
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2983
self.assertLCAMultiWay('this',
2984
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
2985
self.assertLCAMultiWay('conflict',
2986
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2987
allow_overriding_lca=False)
2988
self.assertLCAMultiWay('conflict',
2989
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2990
allow_overriding_lca=False)
3012
2992
def test_this_in_lca(self):
3013
2993
# THIS takes a value of one of the LCAs, OTHER takes a new value, which
3014
2994
# theoretically supersedes both LCA values and 'wins'
3015
self.assertLCAMultiWay(
3016
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3017
self.assertLCAMultiWay(
3018
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3019
self.assertLCAMultiWay('conflict',
3021
'lca2val'], 'oval', 'lca1val',
3022
allow_overriding_lca=False)
3023
self.assertLCAMultiWay('conflict',
3025
'lca2val'], 'oval', 'lca2val',
3026
allow_overriding_lca=False)
2995
self.assertLCAMultiWay('other',
2996
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2997
self.assertLCAMultiWay('other',
2998
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
2999
self.assertLCAMultiWay('conflict',
3000
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
3001
allow_overriding_lca=False)
3002
self.assertLCAMultiWay('conflict',
3003
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
3004
allow_overriding_lca=False)
3027
3005
# OTHER reverted back to BASE, but that is an explicit supersede of all
3029
self.assertLCAMultiWay(
3030
'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
3032
self.assertLCAMultiWay(
3033
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
3034
'bval', 'lca3val', allow_overriding_lca=False)
3007
self.assertLCAMultiWay('other',
3008
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
3009
self.assertLCAMultiWay('conflict',
3010
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
3011
allow_overriding_lca=False)
3036
3013
def test_all_differ(self):
3037
self.assertLCAMultiWay(
3038
'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3039
self.assertLCAMultiWay(
3040
'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
3042
self.assertLCAMultiWay(
3043
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
3014
self.assertLCAMultiWay('conflict',
3015
'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3016
self.assertLCAMultiWay('conflict',
3017
'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
3018
self.assertLCAMultiWay('conflict',
3019
'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
3047
3022
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3257
3230
project_wt.lock_read()
3258
3231
self.addCleanup(project_wt.unlock)
3259
3232
# The r1-lib1 revision should be merged into this one
3260
self.assertEqual([b'r1-project', b'r1-lib1'],
3261
project_wt.get_parent_ids())
3233
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3262
3234
new_lib1_id = project_wt.path2id('lib1')
3263
3235
self.assertNotEqual(None, new_lib1_id)
3264
3236
self.assertTreeEntriesEqual(
3265
3237
[('', root_id),
3266
('README', b'project-README-id'),
3267
('dir', b'project-dir-id'),
3238
('README', 'project-README-id'),
3239
('dir', 'project-dir-id'),
3268
3240
('lib1', new_lib1_id),
3269
('dir/file.c', b'project-file.c-id'),
3270
('lib1/Makefile', b'lib1-Makefile-id'),
3271
('lib1/README', b'lib1-README-id'),
3272
('lib1/foo.c', b'lib1-foo.c-id'),
3241
('dir/file.c', 'project-file.c-id'),
3242
('lib1/Makefile', 'lib1-Makefile-id'),
3243
('lib1/README', 'lib1-README-id'),
3244
('lib1/foo.c', 'lib1-foo.c-id'),
3275
3247
def test_name_conflict(self):
3276
3248
"""When the target directory name already exists a conflict is
3277
3249
generated and the original directory is renamed to foo.moved.
3279
3251
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3280
self.setup_simple_branch('src', ['README'])
3252
src_wt = self.setup_simple_branch('src', ['README'])
3281
3253
conflicts = self.do_merge_into('src', 'dest/dir')
3282
3254
self.assertEqual(1, conflicts)
3283
3255
dest_wt.lock_read()
3284
3256
self.addCleanup(dest_wt.unlock)
3285
3257
# The r1-lib1 revision should be merged into this one
3286
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3258
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3287
3259
self.assertTreeEntriesEqual(
3288
[('', b'dest-root-id'),
3289
('dir', b'src-root-id'),
3290
('dir.moved', b'dest-dir-id'),
3291
('dir/README', b'src-README-id'),
3292
('dir.moved/file.txt', b'dest-file.txt-id'),
3260
[('', 'dest-root-id'),
3261
('dir', 'src-root-id'),
3262
('dir.moved', 'dest-dir-id'),
3263
('dir/README', 'src-README-id'),
3264
('dir.moved/file.txt', 'dest-file.txt-id'),
3295
3267
def test_file_id_conflict(self):
3296
3268
"""A conflict is generated if the merge-into adds a file (or other
3297
3269
inventory entry) with a file-id that already exists in the target tree.
3299
self.setup_simple_branch('dest', ['file.txt'])
3271
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3300
3272
# Make a second tree with a file-id that will clash with file.txt in
3302
3274
src_wt = self.make_branch_and_tree('src')
3303
3275
self.build_tree(['src/README'])
3304
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3305
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3276
src_wt.add(['README'], ids=['dest-file.txt-id'])
3277
src_wt.commit("Rev 1 of src.", rev_id='r1-src')
3306
3278
conflicts = self.do_merge_into('src', 'dest/dir')
3307
3279
# This is an edge case that shouldn't happen to users very often. So
3308
3280
# we don't care really about the exact presentation of the conflict,