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-id')
479
self.assertEqual('1\n2a\n', tree_file.read())
482
preview_file = preview_tree.get_file('file-id')
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-id')
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
1398
class TestMergerEntriesLCA(TestMergerBase):
1397
1400
def make_merge_obj(self, builder, other_revision_id,
1398
interesting_files=None):
1401
interesting_files=None, interesting_ids=None):
1399
1402
merger = self.make_Merger(builder, other_revision_id,
1400
interesting_files=interesting_files)
1403
interesting_files=interesting_files,
1404
interesting_ids=interesting_ids)
1401
1405
return merger.make_merger()
1403
1407
def test_simple(self):
1404
1408
builder = self.get_builder()
1405
builder.build_snapshot(None,
1406
[('add', (u'', b'a-root-id', 'directory', None)),
1407
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1408
revision_id=b'A-id')
1409
builder.build_snapshot([b'A-id'],
1410
[('modify', ('a', b'a\nb\nC\nc\n'))],
1411
revision_id=b'C-id')
1412
builder.build_snapshot([b'A-id'],
1413
[('modify', ('a', b'a\nB\nb\nc\n'))],
1414
revision_id=b'B-id')
1415
builder.build_snapshot([b'C-id', b'B-id'],
1416
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1417
revision_id=b'E-id')
1418
builder.build_snapshot([b'B-id', b'C-id'],
1419
[('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1420
revision_id=b'D-id', )
1421
merge_obj = self.make_merge_obj(builder, b'E-id')
1409
builder.build_snapshot('A-id', None,
1410
[('add', (u'', 'a-root-id', 'directory', None)),
1411
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1412
builder.build_snapshot('C-id', ['A-id'],
1413
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1414
builder.build_snapshot('B-id', ['A-id'],
1415
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1416
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1417
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1418
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1419
[('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
1420
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())
1422
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1423
for t in merge_obj._lca_trees])
1424
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1426
1425
entries = list(merge_obj._entries_lca())
1428
1427
# (file_id, changed, parents, names, executable)
1429
1428
# 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'),
1429
root_id = 'a-root-id'
1430
self.assertEqual([('a-id', True,
1433
1431
((root_id, [root_id, root_id]), root_id, root_id),
1434
1432
((u'a', [u'a', u'a']), u'a', u'a'),
1435
((False, [False, False]), False, False),
1433
((False, [False, False]), False, False)),
1439
1436
def test_not_in_base(self):
1440
1437
# LCAs all have the same last-modified revision for the file, as do
1449
1446
# G modifies 'bar'
1451
1448
builder = self.get_builder()
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')
1449
builder.build_snapshot('A-id', None,
1450
[('add', (u'', 'a-root-id', 'directory', None))])
1451
builder.build_snapshot('B-id', ['A-id'],
1452
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1453
builder.build_snapshot('C-id', ['A-id'],
1454
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1455
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1456
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1457
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1458
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1459
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1460
[('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1461
builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
1462
merge_obj = self.make_merge_obj(builder, 'G-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())
1464
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1465
for t in merge_obj._lca_trees])
1466
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1476
1467
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1469
self.assertEqual([('bar-id', True,
1480
1470
((None, [root_id, root_id]), root_id, root_id),
1481
1471
((None, [u'bar', u'bar']), u'bar', u'bar'),
1482
((None, [False, False]), False, False),
1472
((None, [False, False]), False, False)),
1486
1475
def test_not_in_this(self):
1487
1476
builder = self.get_builder()
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')
1477
builder.build_snapshot('A-id', None,
1478
[('add', (u'', 'a-root-id', 'directory', None)),
1479
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1480
builder.build_snapshot('B-id', ['A-id'],
1481
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1482
builder.build_snapshot('C-id', ['A-id'],
1483
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1484
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1485
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1486
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1487
[('unversion', 'a-id')])
1488
merge_obj = self.make_merge_obj(builder, 'E-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())
1490
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1491
for t in merge_obj._lca_trees])
1492
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1510
1494
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1496
self.assertEqual([('a-id', True,
1514
1497
((root_id, [root_id, root_id]), root_id, None),
1515
1498
((u'a', [u'a', u'a']), u'a', None),
1516
((False, [False, False]), False, None),
1499
((False, [False, False]), False, None)),
1520
1502
def test_file_not_in_one_lca(self):
1521
1503
# A # just root
1525
1507
# D E # D and E both have the file, unchanged from C
1526
1508
builder = self.get_builder()
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')
1509
builder.build_snapshot('A-id', None,
1510
[('add', (u'', 'a-root-id', 'directory', None))])
1511
builder.build_snapshot('B-id', ['A-id'], [])
1512
builder.build_snapshot('C-id', ['A-id'],
1513
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1514
builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1515
builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
1516
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1517
merge_obj = self.make_merge_obj(builder, 'E-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())
1519
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1520
for t in merge_obj._lca_trees])
1521
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1545
1523
entries = list(merge_obj._entries_lca())
1546
1524
self.assertEqual([], entries)
1548
1526
def test_not_in_other(self):
1549
1527
builder = self.get_builder()
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')
1528
builder.build_snapshot('A-id', None,
1529
[('add', (u'', 'a-root-id', 'directory', None)),
1530
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1531
builder.build_snapshot('B-id', ['A-id'], [])
1532
builder.build_snapshot('C-id', ['A-id'], [])
1533
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1534
[('unversion', 'a-id')])
1535
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1536
merge_obj = self.make_merge_obj(builder, 'E-id')
1562
1538
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1540
self.assertEqual([('a-id', True,
1566
1541
((root_id, [root_id, root_id]), None, root_id),
1567
1542
((u'a', [u'a', u'a']), None, u'a'),
1568
((False, [False, False]), None, False),
1543
((False, [False, False]), None, False)),
1572
1546
def test_not_in_other_or_lca(self):
1573
1547
# A base, introduces 'foo'
1653
1621
# A => C, add file, thus C supersedes B
1654
1622
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1655
1623
builder = self.get_builder()
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')
1624
builder.build_snapshot('A-id', None,
1625
[('add', (u'', 'a-root-id', 'directory', None))])
1626
builder.build_snapshot('B-id', ['A-id'], [])
1627
builder.build_snapshot('C-id', ['A-id'],
1628
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1629
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1630
[('unversion', 'a-id')])
1631
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1632
merge_obj = self.make_merge_obj(builder, 'E-id')
1669
1634
entries = list(merge_obj._entries_lca())
1670
1635
self.assertEqual([], entries)
1672
1637
def test_only_in_other(self):
1673
1638
builder = self.get_builder()
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')
1639
builder.build_snapshot('A-id', None,
1640
[('add', (u'', 'a-root-id', 'directory', None))])
1641
builder.build_snapshot('B-id', ['A-id'], [])
1642
builder.build_snapshot('C-id', ['A-id'], [])
1643
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1644
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1645
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1646
merge_obj = self.make_merge_obj(builder, 'E-id')
1685
1648
entries = list(merge_obj._entries_lca())
1686
root_id = b'a-root-id'
1687
self.assertEqual([(b'a-id', True,
1688
((None, [None, None]), u'a', None),
1649
root_id = 'a-root-id'
1650
self.assertEqual([('a-id', True,
1689
1651
((None, [None, None]), root_id, None),
1690
1652
((None, [None, None]), u'a', None),
1691
((None, [None, None]), False, None),
1653
((None, [None, None]), False, None)),
1695
1656
def test_one_lca_supersedes(self):
1696
1657
# One LCA supersedes the other LCAs last modified value, but the
1795
1747
# be pruned from the LCAs, even though it was newly introduced by E
1796
1748
# (superseding B).
1797
1749
builder = self.get_builder()
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')
1750
builder.build_snapshot('A-id', None,
1751
[('add', (u'', 'a-root-id', 'directory', None)),
1752
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1753
builder.build_snapshot('C-id', ['A-id'], [])
1754
builder.build_snapshot('B-id', ['A-id'],
1755
[('rename', ('foo', 'bar'))])
1756
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1757
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1758
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1759
[('rename', ('foo', 'bar'))])
1760
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1761
[('rename', ('bar', 'bing'))]) # should end up conflicting
1762
merge_obj = self.make_merge_obj(builder, 'G-id')
1816
1764
entries = list(merge_obj._entries_lca())
1817
root_id = b'a-root-id'
1765
root_id = 'a-root-id'
1818
1766
self.expectFailure("We prune values from BASE even when relevant.",
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),
1769
((root_id, [root_id, root_id]), root_id, root_id),
1770
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1771
((False, [False, False]), False, False)),
1827
1774
def test_both_sides_revert(self):
1828
1775
# Both sides of a criss-cross revert the text to the lca
1869
1811
# We need to emit an entry for 'foo', because D & E differed on the
1870
1812
# merge resolution
1871
1813
builder = self.get_builder()
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')
1814
builder.build_snapshot('A-id', None,
1815
[('add', (u'', 'a-root-id', 'directory', None)),
1816
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1817
builder.build_snapshot('B-id', ['A-id'],
1818
[('modify', ('foo-id', 'B content\n'))])
1819
builder.build_snapshot('C-id', ['A-id'],
1820
[('modify', ('foo-id', 'C content\n'))])
1821
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1822
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1823
builder.build_snapshot('F-id', ['D-id'],
1824
[('modify', ('foo-id', 'F content\n'))])
1825
merge_obj = self.make_merge_obj(builder, 'E-id')
1889
1827
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1829
self.assertEqual([('foo-id', True,
1893
1830
((root_id, [root_id, root_id]), root_id, root_id),
1894
1831
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1895
((False, [False, False]), False, False),
1832
((False, [False, False]), False, False)),
1899
1835
def test_same_lca_resolution_one_side_updates_content(self):
1900
1836
# Both sides converge, but then one side updates the text.
1911
1847
# We need to conflict.
1913
1849
builder = self.get_builder()
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')
1850
builder.build_snapshot('A-id', None,
1851
[('add', (u'', 'a-root-id', 'directory', None)),
1852
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1853
builder.build_snapshot('B-id', ['A-id'],
1854
[('modify', ('foo-id', 'B content\n'))])
1855
builder.build_snapshot('C-id', ['A-id'],
1856
[('modify', ('foo-id', 'C content\n'))])
1857
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1858
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1859
[('modify', ('foo-id', 'C content\n'))]) # Same as E
1860
builder.build_snapshot('F-id', ['D-id'],
1861
[('modify', ('foo-id', 'F content\n'))])
1862
merge_obj = self.make_merge_obj(builder, 'E-id')
1933
1864
entries = list(merge_obj._entries_lca())
1934
1865
self.expectFailure("We don't detect that LCA resolution was the"
1935
1866
" same on both sides",
1936
self.assertEqual, [], entries)
1867
self.assertEqual, [], entries)
1938
1869
def test_only_path_changed(self):
1939
1870
builder = self.get_builder()
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')
1871
builder.build_snapshot('A-id', None,
1872
[('add', (u'', 'a-root-id', 'directory', None)),
1873
('add', (u'a', 'a-id', 'file', 'content\n'))])
1874
builder.build_snapshot('B-id', ['A-id'], [])
1875
builder.build_snapshot('C-id', ['A-id'], [])
1876
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1877
[('rename', (u'a', u'b'))])
1878
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1879
merge_obj = self.make_merge_obj(builder, 'E-id')
1951
1880
entries = list(merge_obj._entries_lca())
1952
root_id = b'a-root-id'
1881
root_id = 'a-root-id'
1953
1882
# The content was not changed, only the path
1954
self.assertEqual([(b'a-id', False,
1955
((u'a', [u'a', u'a']), u'b', u'a'),
1883
self.assertEqual([('a-id', False,
1956
1884
((root_id, [root_id, root_id]), root_id, root_id),
1957
1885
((u'a', [u'a', u'a']), u'b', u'a'),
1958
((False, [False, False]), False, False),
1886
((False, [False, False]), False, False)),
1962
1889
def test_kind_changed(self):
1963
1890
# Identical content, except 'D' changes a-id into a directory
1964
1891
builder = self.get_builder()
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')
1892
builder.build_snapshot('A-id', None,
1893
[('add', (u'', 'a-root-id', 'directory', None)),
1894
('add', (u'a', 'a-id', 'file', 'content\n'))])
1895
builder.build_snapshot('B-id', ['A-id'], [])
1896
builder.build_snapshot('C-id', ['A-id'], [])
1897
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1898
[('unversion', 'a-id'),
1900
('add', (u'a', 'a-id', 'directory', None))])
1901
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1902
merge_obj = self.make_merge_obj(builder, 'E-id')
1978
1903
entries = list(merge_obj._entries_lca())
1979
root_id = b'a-root-id'
1904
root_id = 'a-root-id'
1980
1905
# Only the kind was changed (content)
1981
self.assertEqual([(b'a-id', True,
1982
((u'a', [u'a', u'a']), u'a', u'a'),
1906
self.assertEqual([('a-id', True,
1983
1907
((root_id, [root_id, root_id]), root_id, root_id),
1984
1908
((u'a', [u'a', u'a']), u'a', u'a'),
1985
((False, [False, False]), False, False),
1909
((False, [False, False]), False, False)),
1989
1912
def test_this_changed_kind(self):
1990
1913
# Identical content, but THIS changes a file to a directory
1991
1914
builder = self.get_builder()
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')
1915
builder.build_snapshot('A-id', None,
1916
[('add', (u'', 'a-root-id', 'directory', None)),
1917
('add', (u'a', 'a-id', 'file', 'content\n'))])
1918
builder.build_snapshot('B-id', ['A-id'], [])
1919
builder.build_snapshot('C-id', ['A-id'], [])
1920
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1921
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1922
[('unversion', 'a-id'),
1924
('add', (u'a', 'a-id', 'directory', None))])
1925
merge_obj = self.make_merge_obj(builder, 'E-id')
2005
1926
entries = list(merge_obj._entries_lca())
2006
1927
# Only the kind was changed (content)
2007
1928
self.assertEqual([], entries)
2009
1930
def test_interesting_files(self):
2010
1931
# Two files modified, but we should filter one of them
2011
1932
builder = self.get_builder()
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',
1933
builder.build_snapshot('A-id', None,
1934
[('add', (u'', 'a-root-id', 'directory', None)),
1935
('add', (u'a', 'a-id', 'file', 'content\n')),
1936
('add', (u'b', 'b-id', 'file', 'content\n'))])
1937
builder.build_snapshot('B-id', ['A-id'], [])
1938
builder.build_snapshot('C-id', ['A-id'], [])
1939
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1940
[('modify', ('a-id', 'new-content\n')),
1941
('modify', ('b-id', 'new-content\n'))])
1942
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1943
merge_obj = self.make_merge_obj(builder, 'E-id',
2025
1944
interesting_files=['b'])
2026
1945
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1947
self.assertEqual([('b-id', True,
2030
1948
((root_id, [root_id, root_id]), root_id, root_id),
2031
1949
((u'b', [u'b', u'b']), u'b', u'b'),
2032
((False, [False, False]), False, False),
1950
((False, [False, False]), False, False)),
2036
1953
def test_interesting_file_in_this(self):
2037
1954
# This renamed the file, but it should still match the entry in other
2038
1955
builder = self.get_builder()
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',
1956
builder.build_snapshot('A-id', None,
1957
[('add', (u'', 'a-root-id', 'directory', None)),
1958
('add', (u'a', 'a-id', 'file', 'content\n')),
1959
('add', (u'b', 'b-id', 'file', 'content\n'))])
1960
builder.build_snapshot('B-id', ['A-id'], [])
1961
builder.build_snapshot('C-id', ['A-id'], [])
1962
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1963
[('modify', ('a-id', 'new-content\n')),
1964
('modify', ('b-id', 'new-content\n'))])
1965
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1966
[('rename', ('b', 'c'))])
1967
merge_obj = self.make_merge_obj(builder, 'E-id',
2054
1968
interesting_files=['c'])
2055
1969
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1971
self.assertEqual([('b-id', True,
2059
1972
((root_id, [root_id, root_id]), root_id, root_id),
2060
1973
((u'b', [u'b', u'b']), u'b', u'c'),
2061
((False, [False, False]), False, False),
1974
((False, [False, False]), False, False)),
2065
1977
def test_interesting_file_in_base(self):
2066
1978
# This renamed the file, but it should still match the entry in BASE
2067
1979
builder = self.get_builder()
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',
1980
builder.build_snapshot('A-id', None,
1981
[('add', (u'', 'a-root-id', 'directory', None)),
1982
('add', (u'a', 'a-id', 'file', 'content\n')),
1983
('add', (u'c', 'c-id', 'file', 'content\n'))])
1984
builder.build_snapshot('B-id', ['A-id'],
1985
[('rename', ('c', 'b'))])
1986
builder.build_snapshot('C-id', ['A-id'],
1987
[('rename', ('c', 'b'))])
1988
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1989
[('modify', ('a-id', 'new-content\n')),
1990
('modify', ('c-id', 'new-content\n'))])
1991
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1992
merge_obj = self.make_merge_obj(builder, 'E-id',
2085
1993
interesting_files=['c'])
2086
1994
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
1996
self.assertEqual([('c-id', True,
2090
1997
((root_id, [root_id, root_id]), root_id, root_id),
2091
1998
((u'c', [u'b', u'b']), u'b', u'b'),
2092
((False, [False, False]), False, False),
1999
((False, [False, False]), False, False)),
2096
2002
def test_interesting_file_in_lca(self):
2097
2003
# This renamed the file, but it should still match the entry in LCA
2098
2004
builder = self.get_builder()
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',
2005
builder.build_snapshot('A-id', None,
2006
[('add', (u'', 'a-root-id', 'directory', None)),
2007
('add', (u'a', 'a-id', 'file', 'content\n')),
2008
('add', (u'b', 'b-id', 'file', 'content\n'))])
2009
builder.build_snapshot('B-id', ['A-id'],
2010
[('rename', ('b', 'c'))])
2011
builder.build_snapshot('C-id', ['A-id'], [])
2012
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2013
[('modify', ('a-id', 'new-content\n')),
2014
('modify', ('b-id', 'new-content\n'))])
2015
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2016
[('rename', ('c', 'b'))])
2017
merge_obj = self.make_merge_obj(builder, 'E-id',
2114
2018
interesting_files=['c'])
2115
2019
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
2021
self.assertEqual([('b-id', True,
2119
2022
((root_id, [root_id, root_id]), root_id, root_id),
2120
2023
((u'b', [u'c', u'b']), u'b', u'b'),
2121
((False, [False, False]), False, False),
2024
((False, [False, False]), False, False)),
2125
def test_interesting_files(self):
2027
def test_interesting_ids(self):
2126
2028
# Two files modified, but we should filter one of them
2127
2029
builder = self.get_builder()
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'])
2030
builder.build_snapshot('A-id', None,
2031
[('add', (u'', 'a-root-id', 'directory', None)),
2032
('add', (u'a', 'a-id', 'file', 'content\n')),
2033
('add', (u'b', 'b-id', 'file', 'content\n'))])
2034
builder.build_snapshot('B-id', ['A-id'], [])
2035
builder.build_snapshot('C-id', ['A-id'], [])
2036
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2037
[('modify', ('a-id', 'new-content\n')),
2038
('modify', ('b-id', 'new-content\n'))])
2039
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2040
merge_obj = self.make_merge_obj(builder, 'E-id',
2041
interesting_ids=['b-id'])
2141
2042
entries = list(merge_obj._entries_lca())
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
root_id = 'a-root-id'
2044
self.assertEqual([('b-id', True,
2145
2045
((root_id, [root_id, root_id]), root_id, root_id),
2146
2046
((u'b', [u'b', u'b']), u'b', u'b'),
2147
((False, [False, False]), False, False),
2047
((False, [False, False]), False, False)),
2152
2052
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2424
2317
wt.lock_write()
2425
2318
self.addCleanup(wt.unlock)
2426
2319
os.symlink('bar', 'path/foo')
2427
wt.add(['foo'], [b'foo-id'])
2428
wt.commit('A add symlink', rev_id=b'A-id')
2320
wt.add(['foo'], ['foo-id'])
2321
wt.commit('A add symlink', rev_id='A-id')
2429
2322
wt.rename_one('foo', 'barry')
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')
2323
wt.commit('B foo => barry', rev_id='B-id')
2324
wt.set_last_revision('A-id')
2325
wt.branch.set_last_revision_info(1, 'A-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')
2327
wt.commit('C', rev_id='C-id')
2328
wt.merge_from_branch(wt.branch, 'B-id')
2329
self.assertEqual('barry', wt.id2path('foo-id'))
2330
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2331
wt.commit('E merges C & B', rev_id='E-id')
2439
2332
wt.rename_one('barry', 'blah')
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')
2333
wt.commit('F barry => blah', rev_id='F-id')
2334
wt.set_last_revision('B-id')
2335
wt.branch.set_last_revision_info(2, 'B-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'))
2337
wt.merge_from_branch(wt.branch, 'C-id')
2338
wt.commit('D merges B & C', rev_id='D-id')
2339
self.assertEqual('barry', wt.id2path('foo-id'))
2447
2340
# Check the output of the Merger object directly
2448
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2341
merger = _mod_merge.Merger.from_revision_ids(wt, 'F-id')
2449
2342
merger.merge_type = _mod_merge.Merge3Merger
2450
2343
merge_obj = merger.make_merger()
2451
2344
root_id = wt.path2id('')
2452
2345
entries = list(merge_obj._entries_lca())
2453
2346
# No content change, just a path change
2454
self.assertEqual([(b'foo-id', False,
2455
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2347
self.assertEqual([('foo-id', False,
2456
2348
((root_id, [root_id, root_id]), root_id, root_id),
2457
2349
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2458
((False, [False, False]), False, False),
2461
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2350
((False, [False, False]), False, False)),
2352
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2462
2353
self.assertEqual(0, conflicts)
2463
self.assertEqual('blah', wt.id2path(b'foo-id'))
2354
self.assertEqual('blah', wt.id2path('foo-id'))
2465
2356
def test_symlink_no_content_change(self):
2466
2357
self.requireFeature(features.SymlinkFeature)
2527
2418
wt = self.make_branch_and_tree('path')
2528
2419
wt.lock_write()
2529
2420
self.addCleanup(wt.unlock)
2530
wt.commit('base', rev_id=b'A-id')
2421
wt.commit('base', rev_id='A-id')
2531
2422
os.symlink('bar', 'path/foo')
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')
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')
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'))
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'))
2540
2431
os.remove('path/foo')
2541
2432
# We have to change the link in E, or it won't try to do a comparison
2542
2433
os.symlink('bing', 'path/foo')
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')
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')
2547
wt.merge_from_branch(wt.branch, b'C-id')
2438
wt.merge_from_branch(wt.branch, 'C-id')
2548
2439
os.remove('path/foo')
2549
self.build_tree_contents([('path/foo', b'file content\n')])
2440
self.build_tree_contents([('path/foo', 'file content\n')])
2550
2441
# XXX: workaround, WT doesn't detect kind changes unless you do
2551
2442
# iter_changes()
2552
2443
list(wt.iter_changes(wt.basis_tree()))
2553
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2444
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2555
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2446
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2556
2447
merger.merge_type = _mod_merge.Merge3Merger
2557
2448
merge_obj = merger.make_merger()
2558
2449
entries = list(merge_obj._entries_lca())
2559
2450
root_id = wt.path2id('')
2560
self.assertEqual([(b'foo-id', True,
2561
((None, [u'foo', None]), u'foo', u'foo'),
2451
self.assertEqual([('foo-id', True,
2562
2452
((None, [root_id, None]), root_id, root_id),
2563
2453
((None, [u'foo', None]), u'foo', u'foo'),
2564
((None, [False, None]), False, False),
2454
((None, [False, None]), False, False)),
2568
2457
def test_symlink_all_wt(self):
2569
2458
"""Check behavior if all trees are Working Trees."""
2584
2473
wt.lock_write()
2585
2474
self.addCleanup(wt.unlock)
2586
2475
os.symlink('bar', 'path/foo')
2587
wt.add(['foo'], [b'foo-id'])
2588
wt.commit('add symlink', rev_id=b'A-id')
2476
wt.add(['foo'], ['foo-id'])
2477
wt.commit('add symlink', rev_id='A-id')
2589
2478
os.remove('path/foo')
2590
2479
os.symlink('baz', 'path/foo')
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')
2480
wt.commit('foo => baz', rev_id='B-id')
2481
wt.set_last_revision('A-id')
2482
wt.branch.set_last_revision_info(1, 'A-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')
2484
wt.commit('C', rev_id='C-id')
2485
wt.merge_from_branch(wt.branch, 'B-id')
2486
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2487
wt.commit('E merges C & B', rev_id='E-id')
2599
2488
os.remove('path/foo')
2600
2489
os.symlink('bing', 'path/foo')
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')
2490
wt.commit('F foo => bing', rev_id='F-id')
2491
wt.set_last_revision('B-id')
2492
wt.branch.set_last_revision_info(2, 'B-id')
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()
2494
wt.merge_from_branch(wt.branch, 'C-id')
2495
wt.commit('D merges B & C', rev_id='D-id')
2496
wt_base = wt.controldir.sprout('base', 'A-id').open_workingtree()
2608
2497
wt_base.lock_read()
2609
2498
self.addCleanup(wt_base.unlock)
2610
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2499
wt_lca1 = wt.controldir.sprout('b-tree', 'B-id').open_workingtree()
2611
2500
wt_lca1.lock_read()
2612
2501
self.addCleanup(wt_lca1.unlock)
2613
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2502
wt_lca2 = wt.controldir.sprout('c-tree', 'C-id').open_workingtree()
2614
2503
wt_lca2.lock_read()
2615
2504
self.addCleanup(wt_lca2.unlock)
2616
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2505
wt_other = wt.controldir.sprout('other', 'F-id').open_workingtree()
2617
2506
wt_other.lock_read()
2618
2507
self.addCleanup(wt_other.unlock)
2619
2508
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2620
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2509
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2621
2510
entries = list(merge_obj._entries_lca())
2622
2511
root_id = wt.path2id('')
2623
self.assertEqual([(b'foo-id', True,
2624
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2512
self.assertEqual([('foo-id', True,
2625
2513
((root_id, [root_id, root_id]), root_id, root_id),
2626
2514
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2627
((False, [False, False]), False, False),
2515
((False, [False, False]), False, False)),
2631
2518
def test_other_reverted_path_to_base(self):
2632
2519
# A Path at 'foo'
2640
2527
# F Path at 'foo'
2641
2528
builder = self.get_builder()
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')
2529
builder.build_snapshot('A-id', None,
2530
[('add', (u'', 'a-root-id', 'directory', None)),
2531
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2532
builder.build_snapshot('C-id', ['A-id'], [])
2533
builder.build_snapshot('B-id', ['A-id'],
2534
[('rename', ('foo', 'bar'))])
2535
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2536
[('rename', ('foo', 'bar'))]) # merge the rename
2537
builder.build_snapshot('F-id', ['E-id'],
2538
[('rename', ('bar', 'foo'))]) # Rename back to BASE
2539
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2540
wt, conflicts = self.do_merge(builder, 'F-id')
2655
2541
self.assertEqual(0, conflicts)
2656
self.assertEqual('foo', wt.id2path(b'foo-id'))
2542
self.assertEqual('foo', wt.id2path('foo-id'))
2658
2544
def test_other_reverted_content_to_base(self):
2659
2545
builder = self.get_builder()
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')
2546
builder.build_snapshot('A-id', None,
2547
[('add', (u'', 'a-root-id', 'directory', None)),
2548
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2549
builder.build_snapshot('C-id', ['A-id'], [])
2550
builder.build_snapshot('B-id', ['A-id'],
2551
[('modify', ('foo-id', 'B content\n'))])
2552
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2553
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2554
builder.build_snapshot('F-id', ['E-id'],
2555
[('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2556
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2557
wt, conflicts = self.do_merge(builder, 'F-id')
2676
2558
self.assertEqual(0, conflicts)
2677
2559
# TODO: We need to use the per-file graph to properly select a BASE
2678
2560
# before this will work. Or at least use the LCA trees to find
2679
2561
# the appropriate content base. (which is B, not A).
2680
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2562
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2682
2564
def test_other_modified_content(self):
2683
2565
builder = self.get_builder()
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')
2566
builder.build_snapshot('A-id', None,
2567
[('add', (u'', 'a-root-id', 'directory', None)),
2568
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2569
builder.build_snapshot('C-id', ['A-id'], [])
2570
builder.build_snapshot('B-id', ['A-id'],
2571
[('modify', ('foo-id', 'B content\n'))])
2572
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2573
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2574
builder.build_snapshot('F-id', ['E-id'],
2575
[('modify', ('foo-id', 'F content\n'))]) # Override B content
2576
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2577
wt, conflicts = self.do_merge(builder, 'F-id')
2700
2578
self.assertEqual(0, conflicts)
2701
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2579
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2703
2581
def test_all_wt(self):
2704
2582
"""Check behavior if all trees are Working Trees."""
2712
2590
# D E E updates content, renames 'b' => 'c'
2713
2591
builder = self.get_builder()
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
2592
builder.build_snapshot('A-id', None,
2593
[('add', (u'', 'a-root-id', 'directory', None)),
2594
('add', (u'a', 'a-id', 'file', 'base content\n')),
2595
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2596
builder.build_snapshot('B-id', ['A-id'],
2597
[('modify', ('foo-id', 'B content\n'))])
2598
builder.build_snapshot('C-id', ['A-id'],
2599
[('rename', ('a', 'b'))])
2600
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2601
[('rename', ('b', 'c')),
2602
('modify', ('foo-id', 'E content\n'))])
2603
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2604
[('rename', ('a', 'b'))]) # merged change
2731
2605
wt_this = self.get_wt_from_builder(builder)
2732
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2606
wt_base = wt_this.controldir.sprout('base', 'A-id').open_workingtree()
2733
2607
wt_base.lock_read()
2734
2608
self.addCleanup(wt_base.unlock)
2735
wt_lca1 = wt_this.controldir.sprout(
2736
'b-tree', b'B-id').open_workingtree()
2609
wt_lca1 = wt_this.controldir.sprout('b-tree', 'B-id').open_workingtree()
2737
2610
wt_lca1.lock_read()
2738
2611
self.addCleanup(wt_lca1.unlock)
2739
wt_lca2 = wt_this.controldir.sprout(
2740
'c-tree', b'C-id').open_workingtree()
2612
wt_lca2 = wt_this.controldir.sprout('c-tree', 'C-id').open_workingtree()
2741
2613
wt_lca2.lock_read()
2742
2614
self.addCleanup(wt_lca2.unlock)
2743
wt_other = wt_this.controldir.sprout(
2744
'other', b'E-id').open_workingtree()
2615
wt_other = wt_this.controldir.sprout('other', 'E-id').open_workingtree()
2745
2616
wt_other.lock_read()
2746
2617
self.addCleanup(wt_other.unlock)
2747
2618
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2748
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2619
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2749
2620
entries = list(merge_obj._entries_lca())
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),
2621
root_id = 'a-root-id'
2622
self.assertEqual([('a-id', False,
2623
((root_id, [root_id, root_id]), root_id, root_id),
2624
((u'a', [u'a', u'b']), u'c', u'b'),
2625
((False, [False, False]), False, False)),
2627
((root_id, [root_id, root_id]), root_id, root_id),
2628
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2629
((False, [False, False]), False, False)),
2765
2632
def test_nested_tree_unmodified(self):
2766
2633
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2767
2634
# 'tree-reference'
2768
2635
wt = self.make_branch_and_tree('tree',
2769
format='development-subtree')
2636
format='development-subtree')
2770
2637
wt.lock_write()
2771
2638
self.addCleanup(wt.unlock)
2772
2639
sub_tree = self.make_branch_and_tree('tree/sub-tree',
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')])
2640
format='development-subtree')
2641
wt.set_root_id('a-root-id')
2642
sub_tree.set_root_id('sub-tree-root')
2643
self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2777
2644
sub_tree.add('file')
2778
sub_tree.commit('foo', rev_id=b'sub-A-id')
2645
sub_tree.commit('foo', rev_id='sub-A-id')
2779
2646
wt.add_reference(sub_tree)
2780
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2647
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2781
2648
# Now create a criss-cross merge in the parent, without modifying the
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)
2650
wt.commit('B', rev_id='B-id', recursive=None)
2651
wt.set_last_revision('A-id')
2652
wt.branch.set_last_revision_info(1, 'A-id')
2653
wt.commit('C', rev_id='C-id', recursive=None)
2654
wt.merge_from_branch(wt.branch, to_revision='B-id')
2655
wt.commit('E', rev_id='E-id', recursive=None)
2656
wt.set_parent_ids(['B-id', 'C-id'])
2657
wt.branch.set_last_revision_info(2, 'B-id')
2658
wt.commit('D', rev_id='D-id', recursive=None)
2793
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2660
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2794
2661
merger.merge_type = _mod_merge.Merge3Merger
2795
2662
merge_obj = merger.make_merger()
2796
2663
entries = list(merge_obj._entries_lca())
2838
2705
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2839
2706
# 'tree-reference'
2840
2707
wt = self.make_branch_and_tree('tree',
2841
format='development-subtree')
2708
format='development-subtree')
2842
2709
wt.lock_write()
2843
2710
self.addCleanup(wt.unlock)
2844
2711
sub_tree = self.make_branch_and_tree('tree/sub',
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')])
2712
format='development-subtree')
2713
wt.set_root_id('a-root-id')
2714
sub_tree.set_root_id('sub-tree-root')
2715
self.build_tree_contents([('tree/sub/file', 'text1')])
2849
2716
sub_tree.add('file')
2850
sub_tree.commit('foo', rev_id=b'sub-A-id')
2717
sub_tree.commit('foo', rev_id='sub-A-id')
2851
2718
wt.add_reference(sub_tree)
2852
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2719
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2853
2720
# Now create a criss-cross merge in the parent, without modifying the
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')
2722
wt.commit('B', rev_id='B-id', recursive=None)
2723
wt.set_last_revision('A-id')
2724
wt.branch.set_last_revision_info(1, 'A-id')
2725
wt.commit('C', rev_id='C-id', recursive=None)
2726
wt.merge_from_branch(wt.branch, to_revision='B-id')
2860
2727
wt.rename_one('sub', 'alt_sub')
2861
wt.commit('E', rev_id=b'E-id', recursive=None)
2862
wt.set_last_revision(b'B-id')
2728
wt.commit('E', rev_id='E-id', recursive=None)
2729
wt.set_last_revision('B-id')
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)
2731
wt.set_parent_ids(['B-id', 'C-id'])
2732
wt.branch.set_last_revision_info(2, 'B-id')
2733
wt.commit('D', rev_id='D-id', recursive=None)
2868
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2735
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2869
2736
merger.merge_type = _mod_merge.Merge3Merger
2870
2737
merge_obj = merger.make_merger()
2871
2738
entries = list(merge_obj._entries_lca())
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'),
2739
root_id = 'a-root-id'
2740
self.assertEqual([('sub-tree-root', False,
2875
2741
((root_id, [root_id, root_id]), root_id, root_id),
2876
2742
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2877
((False, [False, False]), False, False),
2743
((False, [False, False]), False, False)),
2881
2746
def test_nested_tree_subtree_renamed_and_modified(self):
2882
2747
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2883
2748
# 'tree-reference'
2884
2749
wt = self.make_branch_and_tree('tree',
2885
format='development-subtree')
2750
format='development-subtree')
2886
2751
wt.lock_write()
2887
2752
self.addCleanup(wt.unlock)
2888
2753
sub_tree = self.make_branch_and_tree('tree/sub',
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')])
2754
format='development-subtree')
2755
wt.set_root_id('a-root-id')
2756
sub_tree.set_root_id('sub-tree-root')
2757
self.build_tree_contents([('tree/sub/file', 'text1')])
2893
2758
sub_tree.add('file')
2894
sub_tree.commit('foo', rev_id=b'sub-A-id')
2759
sub_tree.commit('foo', rev_id='sub-A-id')
2895
2760
wt.add_reference(sub_tree)
2896
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2761
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2897
2762
# Now create a criss-cross merge in the parent, without modifying the
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')
2764
wt.commit('B', rev_id='B-id', recursive=None)
2765
wt.set_last_revision('A-id')
2766
wt.branch.set_last_revision_info(1, 'A-id')
2767
wt.commit('C', rev_id='C-id', recursive=None)
2768
wt.merge_from_branch(wt.branch, to_revision='B-id')
2769
self.build_tree_contents([('tree/sub/file', 'text2')])
2770
sub_tree.commit('modify contents', rev_id='sub-B-id')
2906
2771
wt.rename_one('sub', 'alt_sub')
2907
wt.commit('E', rev_id=b'E-id', recursive=None)
2908
wt.set_last_revision(b'B-id')
2772
wt.commit('E', rev_id='E-id', recursive=None)
2773
wt.set_last_revision('B-id')
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)
2775
wt.set_parent_ids(['B-id', 'C-id'])
2776
wt.branch.set_last_revision_info(2, 'B-id')
2777
wt.commit('D', rev_id='D-id', recursive=None)
2914
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2779
merger = _mod_merge.Merger.from_revision_ids(wt, 'E-id')
2915
2780
merger.merge_type = _mod_merge.Merge3Merger
2916
2781
merge_obj = merger.make_merger()
2917
2782
entries = list(merge_obj._entries_lca())
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'),
2783
root_id = 'a-root-id'
2784
self.assertEqual([('sub-tree-root', False,
2921
2785
((root_id, [root_id, root_id]), root_id, root_id),
2922
2786
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2923
((False, [False, False]), False, False),
2787
((False, [False, False]), False, False)),
2928
2791
class TestLCAMultiWay(tests.TestCase):
2930
2793
def assertLCAMultiWay(self, expected, base, lcas, other, this,
2931
2794
allow_overriding_lca=True):
2932
2795
self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2933
(base, lcas), other, this,
2934
allow_overriding_lca=allow_overriding_lca))
2796
(base, lcas), other, this,
2797
allow_overriding_lca=allow_overriding_lca))
2936
2799
def test_other_equal_equal_lcas(self):
2937
2800
"""Test when OTHER=LCA and all LCAs are identical."""
2938
2801
self.assertLCAMultiWay('this',
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)
2802
'bval', ['bval', 'bval'], 'bval', 'bval')
2803
self.assertLCAMultiWay('this',
2804
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2805
self.assertLCAMultiWay('this',
2806
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2807
self.assertLCAMultiWay('this',
2808
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2809
self.assertLCAMultiWay('this',
2810
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2949
2812
def test_other_equal_this(self):
2950
2813
"""Test when other and this are identical."""
2951
2814
self.assertLCAMultiWay('this',
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')
2815
'bval', ['bval', 'bval'], 'oval', 'oval')
2816
self.assertLCAMultiWay('this',
2817
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2818
self.assertLCAMultiWay('this',
2819
'bval', ['cval', 'dval'], 'oval', 'oval')
2820
self.assertLCAMultiWay('this',
2821
'bval', [None, 'lcaval'], 'oval', 'oval')
2822
self.assertLCAMultiWay('this',
2823
None, [None, 'lcaval'], 'oval', 'oval')
2824
self.assertLCAMultiWay('this',
2825
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2826
self.assertLCAMultiWay('this',
2827
None, ['cval', 'dval'], 'oval', 'oval')
2828
self.assertLCAMultiWay('this',
2829
None, ['cval', 'dval'], None, None)
2830
self.assertLCAMultiWay('this',
2831
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2970
2833
def test_no_lcas(self):
2971
2834
self.assertLCAMultiWay('this',
2972
'bval', [], 'bval', 'tval')
2835
'bval', [], 'bval', 'tval')
2973
2836
self.assertLCAMultiWay('other',
2974
'bval', [], 'oval', 'bval')
2837
'bval', [], 'oval', 'bval')
2975
2838
self.assertLCAMultiWay('conflict',
2976
'bval', [], 'oval', 'tval')
2839
'bval', [], 'oval', 'tval')
2977
2840
self.assertLCAMultiWay('this',
2978
'bval', [], 'oval', 'oval')
2841
'bval', [], 'oval', 'oval')
2980
2843
def test_lca_supersedes_other_lca(self):
2981
2844
"""If one lca == base, the other lca takes precedence"""
2982
2845
self.assertLCAMultiWay('this',
2983
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2846
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2984
2847
self.assertLCAMultiWay('this',
2985
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2848
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2986
2849
# This is actually considered a 'revert' because the 'lcaval' in LCAS
2987
2850
# supersedes the BASE val (in the other LCA) but then OTHER reverts it
2988
2851
# back to bval.
2989
2852
self.assertLCAMultiWay('other',
2990
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2853
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2991
2854
self.assertLCAMultiWay('conflict',
2992
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2855
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2994
2857
def test_other_and_this_pick_different_lca(self):
2995
2858
# OTHER and THIS resolve the lca conflict in different ways
2996
2859
self.assertLCAMultiWay('conflict',
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')
2860
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2861
self.assertLCAMultiWay('conflict',
2862
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2863
self.assertLCAMultiWay('conflict',
2864
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
3003
2866
def test_other_in_lca(self):
3004
2867
# OTHER takes a value of one of the LCAs, THIS takes a new value, which
3005
2868
# theoretically supersedes both LCA values and 'wins'
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)
2869
self.assertLCAMultiWay('this',
2870
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2871
self.assertLCAMultiWay('this',
2872
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
2873
self.assertLCAMultiWay('conflict',
2874
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2875
allow_overriding_lca=False)
2876
self.assertLCAMultiWay('conflict',
2877
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2878
allow_overriding_lca=False)
3019
2879
# THIS reverted back to BASE, but that is an explicit supersede of all
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)
2881
self.assertLCAMultiWay('this',
2882
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2883
self.assertLCAMultiWay('this',
2884
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
2885
self.assertLCAMultiWay('conflict',
2886
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2887
allow_overriding_lca=False)
2888
self.assertLCAMultiWay('conflict',
2889
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2890
allow_overriding_lca=False)
3035
2892
def test_this_in_lca(self):
3036
2893
# THIS takes a value of one of the LCAs, OTHER takes a new value, which
3037
2894
# theoretically supersedes both LCA values and 'wins'
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)
2895
self.assertLCAMultiWay('other',
2896
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2897
self.assertLCAMultiWay('other',
2898
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
2899
self.assertLCAMultiWay('conflict',
2900
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2901
allow_overriding_lca=False)
2902
self.assertLCAMultiWay('conflict',
2903
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2904
allow_overriding_lca=False)
3050
2905
# OTHER reverted back to BASE, but that is an explicit supersede of all
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)
2907
self.assertLCAMultiWay('other',
2908
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
2909
self.assertLCAMultiWay('conflict',
2910
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2911
allow_overriding_lca=False)
3059
2913
def test_all_differ(self):
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',
2914
self.assertLCAMultiWay('conflict',
2915
'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2916
self.assertLCAMultiWay('conflict',
2917
'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2918
self.assertLCAMultiWay('conflict',
2919
'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
3070
2922
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3280
3130
project_wt.lock_read()
3281
3131
self.addCleanup(project_wt.unlock)
3282
3132
# The r1-lib1 revision should be merged into this one
3283
self.assertEqual([b'r1-project', b'r1-lib1'],
3284
project_wt.get_parent_ids())
3133
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3285
3134
new_lib1_id = project_wt.path2id('lib1')
3286
3135
self.assertNotEqual(None, new_lib1_id)
3287
3136
self.assertTreeEntriesEqual(
3288
3137
[('', root_id),
3289
('README', b'project-README-id'),
3290
('dir', b'project-dir-id'),
3138
('README', 'project-README-id'),
3139
('dir', 'project-dir-id'),
3291
3140
('lib1', new_lib1_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'),
3141
('dir/file.c', 'project-file.c-id'),
3142
('lib1/Makefile', 'lib1-Makefile-id'),
3143
('lib1/README', 'lib1-README-id'),
3144
('lib1/foo.c', 'lib1-foo.c-id'),
3298
3147
def test_name_conflict(self):
3299
3148
"""When the target directory name already exists a conflict is
3300
3149
generated and the original directory is renamed to foo.moved.
3302
3151
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3303
self.setup_simple_branch('src', ['README'])
3152
src_wt = self.setup_simple_branch('src', ['README'])
3304
3153
conflicts = self.do_merge_into('src', 'dest/dir')
3305
3154
self.assertEqual(1, conflicts)
3306
3155
dest_wt.lock_read()
3307
3156
self.addCleanup(dest_wt.unlock)
3308
3157
# The r1-lib1 revision should be merged into this one
3309
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3158
self.assertEqual(['r1-dest', 'r1-src'], dest_wt.get_parent_ids())
3310
3159
self.assertTreeEntriesEqual(
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'),
3160
[('', 'dest-root-id'),
3161
('dir', 'src-root-id'),
3162
('dir.moved', 'dest-dir-id'),
3163
('dir/README', 'src-README-id'),
3164
('dir.moved/file.txt', 'dest-file.txt-id'),
3318
3167
def test_file_id_conflict(self):
3319
3168
"""A conflict is generated if the merge-into adds a file (or other
3320
3169
inventory entry) with a file-id that already exists in the target tree.
3322
self.setup_simple_branch('dest', ['file.txt'])
3171
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3323
3172
# Make a second tree with a file-id that will clash with file.txt in
3325
3174
src_wt = self.make_branch_and_tree('src')
3326
3175
self.build_tree(['src/README'])
3327
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3328
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3176
src_wt.add(['README'], ids=['dest-file.txt-id'])
3177
src_wt.commit("Rev 1 of src.", rev_id='r1-src')
3329
3178
conflicts = self.do_merge_into('src', 'dest/dir')
3330
3179
# This is an edge case that shouldn't happen to users very often. So
3331
3180
# we don't care really about the exact presentation of the conflict,