326
365
'c', but not 'b'.
328
367
this_tree = self.make_branch_and_tree('this')
329
self.build_tree_contents([('this/file', "a\n")])
368
self.build_tree_contents([('this/file', b"a\n")])
330
369
this_tree.add('file')
331
370
this_tree.commit('rev1')
332
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
333
self.build_tree_contents([('other/file', "a\nb\n")])
334
other_tree.commit('rev2b', rev_id='rev2b')
335
self.build_tree_contents([('other/file', "c\na\nb\n")])
336
other_tree.commit('rev3b', rev_id='rev3b')
371
other_tree = this_tree.controldir.sprout('other').open_workingtree()
372
self.build_tree_contents([('other/file', b"a\nb\n")])
373
other_tree.commit('rev2b', rev_id=b'rev2b')
374
self.build_tree_contents([('other/file', b"c\na\nb\n")])
375
other_tree.commit('rev3b', rev_id=b'rev3b')
337
376
this_tree.lock_write()
338
377
self.addCleanup(this_tree.unlock)
339
378
return this_tree, other_tree
341
380
def test_weave_cherrypick(self):
342
381
this_tree, other_tree = self.prepare_cherrypick()
343
merger = _mod_merge.Merger.from_revision_ids(None,
344
this_tree, 'rev3b', 'rev2b', other_tree.branch)
382
merger = _mod_merge.Merger.from_revision_ids(
383
this_tree, b'rev3b', b'rev2b', other_tree.branch)
345
384
merger.merge_type = _mod_merge.WeaveMerger
346
385
merger.do_merge()
347
self.assertFileEqual('c\na\n', 'this/file')
386
self.assertFileEqual(b'c\na\n', 'this/file')
349
388
def test_weave_cannot_reverse_cherrypick(self):
350
389
this_tree, other_tree = self.prepare_cherrypick()
351
merger = _mod_merge.Merger.from_revision_ids(None,
352
this_tree, 'rev2b', 'rev3b', other_tree.branch)
390
merger = _mod_merge.Merger.from_revision_ids(
391
this_tree, b'rev2b', b'rev3b', other_tree.branch)
353
392
merger.merge_type = _mod_merge.WeaveMerger
354
393
self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
356
395
def test_merge3_can_reverse_cherrypick(self):
357
396
this_tree, other_tree = self.prepare_cherrypick()
358
merger = _mod_merge.Merger.from_revision_ids(None,
359
this_tree, 'rev2b', 'rev3b', other_tree.branch)
397
merger = _mod_merge.Merger.from_revision_ids(
398
this_tree, b'rev2b', b'rev3b', other_tree.branch)
360
399
merger.merge_type = _mod_merge.Merge3Merger
361
400
merger.do_merge()
363
402
def test_merge3_will_detect_cherrypick(self):
364
403
this_tree = self.make_branch_and_tree('this')
365
self.build_tree_contents([('this/file', "a\n")])
404
self.build_tree_contents([('this/file', b"a\n")])
366
405
this_tree.add('file')
367
406
this_tree.commit('rev1')
368
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
369
self.build_tree_contents([('other/file', "a\nb\n")])
370
other_tree.commit('rev2b', rev_id='rev2b')
371
self.build_tree_contents([('other/file', "a\nb\nc\n")])
372
other_tree.commit('rev3b', rev_id='rev3b')
407
other_tree = this_tree.controldir.sprout('other').open_workingtree()
408
self.build_tree_contents([('other/file', b"a\nb\n")])
409
other_tree.commit('rev2b', rev_id=b'rev2b')
410
self.build_tree_contents([('other/file', b"a\nb\nc\n")])
411
other_tree.commit('rev3b', rev_id=b'rev3b')
373
412
this_tree.lock_write()
374
413
self.addCleanup(this_tree.unlock)
376
merger = _mod_merge.Merger.from_revision_ids(None,
377
this_tree, 'rev3b', 'rev2b', other_tree.branch)
415
merger = _mod_merge.Merger.from_revision_ids(
416
this_tree, b'rev3b', b'rev2b', other_tree.branch)
378
417
merger.merge_type = _mod_merge.Merge3Merger
379
418
merger.do_merge()
380
self.assertFileEqual('a\n'
384
'>>>>>>> MERGE-SOURCE\n',
419
self.assertFileEqual(b'a\n'
423
b'>>>>>>> MERGE-SOURCE\n',
426
def test_merge_reverse_revision_range(self):
427
tree = self.make_branch_and_tree(".")
429
self.addCleanup(tree.unlock)
430
self.build_tree(['a'])
432
first_rev = tree.commit("added a")
433
merger = _mod_merge.Merger.from_revision_ids(tree,
434
_mod_revision.NULL_REVISION,
436
merger.merge_type = _mod_merge.Merge3Merger
437
merger.interesting_files = 'a'
438
conflict_count = merger.do_merge()
439
self.assertEqual(0, conflict_count)
441
self.assertPathDoesNotExist("a")
443
self.assertPathExists("a")
387
445
def test_make_merger(self):
388
446
this_tree = self.make_branch_and_tree('this')
389
this_tree.commit('rev1', rev_id='rev1')
390
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
391
this_tree.commit('rev2', rev_id='rev2a')
392
other_tree.commit('rev2', rev_id='rev2b')
447
this_tree.commit('rev1', rev_id=b'rev1')
448
other_tree = this_tree.controldir.sprout('other').open_workingtree()
449
this_tree.commit('rev2', rev_id=b'rev2a')
450
other_tree.commit('rev2', rev_id=b'rev2b')
393
451
this_tree.lock_write()
394
452
self.addCleanup(this_tree.unlock)
395
merger = _mod_merge.Merger.from_revision_ids(None,
396
this_tree, 'rev2b', other_branch=other_tree.branch)
453
merger = _mod_merge.Merger.from_revision_ids(
454
this_tree, b'rev2b', other_branch=other_tree.branch)
397
455
merger.merge_type = _mod_merge.Merge3Merger
398
456
tree_merger = merger.make_merger()
399
457
self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
400
self.assertEqual('rev2b', tree_merger.other_tree.get_revision_id())
401
self.assertEqual('rev1', tree_merger.base_tree.get_revision_id())
458
self.assertEqual(b'rev2b',
459
tree_merger.other_tree.get_revision_id())
460
self.assertEqual(b'rev1',
461
tree_merger.base_tree.get_revision_id())
462
self.assertEqual(other_tree.branch, tree_merger.other_branch)
403
464
def test_make_preview_transform(self):
404
465
this_tree = self.make_branch_and_tree('this')
405
self.build_tree_contents([('this/file', '1\n')])
406
this_tree.add('file', 'file-id')
407
this_tree.commit('rev1', rev_id='rev1')
408
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
409
self.build_tree_contents([('this/file', '1\n2a\n')])
410
this_tree.commit('rev2', rev_id='rev2a')
411
self.build_tree_contents([('other/file', '2b\n1\n')])
412
other_tree.commit('rev2', rev_id='rev2b')
466
self.build_tree_contents([('this/file', b'1\n')])
467
this_tree.add('file', b'file-id')
468
this_tree.commit('rev1', rev_id=b'rev1')
469
other_tree = this_tree.controldir.sprout('other').open_workingtree()
470
self.build_tree_contents([('this/file', b'1\n2a\n')])
471
this_tree.commit('rev2', rev_id=b'rev2a')
472
self.build_tree_contents([('other/file', b'2b\n1\n')])
473
other_tree.commit('rev2', rev_id=b'rev2b')
413
474
this_tree.lock_write()
414
475
self.addCleanup(this_tree.unlock)
415
merger = _mod_merge.Merger.from_revision_ids(None,
416
this_tree, 'rev2b', other_branch=other_tree.branch)
476
merger = _mod_merge.Merger.from_revision_ids(
477
this_tree, b'rev2b', other_branch=other_tree.branch)
417
478
merger.merge_type = _mod_merge.Merge3Merger
418
479
tree_merger = merger.make_merger()
419
480
tt = tree_merger.make_preview_transform()
420
481
self.addCleanup(tt.finalize)
421
482
preview_tree = tt.get_preview_tree()
422
tree_file = this_tree.get_file('file-id')
424
self.assertEqual('1\n2a\n', tree_file.read())
427
preview_file = preview_tree.get_file('file-id')
429
self.assertEqual('2b\n1\n2a\n', preview_file.read())
483
with this_tree.get_file('file') as tree_file:
484
self.assertEqual(b'1\n2a\n', tree_file.read())
485
with preview_tree.get_file('file') as preview_file:
486
self.assertEqual(b'2b\n1\n2a\n', preview_file.read())
433
488
def test_do_merge(self):
434
489
this_tree = self.make_branch_and_tree('this')
435
self.build_tree_contents([('this/file', '1\n')])
436
this_tree.add('file', 'file-id')
437
this_tree.commit('rev1', rev_id='rev1')
438
other_tree = this_tree.bzrdir.sprout('other').open_workingtree()
439
self.build_tree_contents([('this/file', '1\n2a\n')])
440
this_tree.commit('rev2', rev_id='rev2a')
441
self.build_tree_contents([('other/file', '2b\n1\n')])
442
other_tree.commit('rev2', rev_id='rev2b')
490
self.build_tree_contents([('this/file', b'1\n')])
491
this_tree.add('file', b'file-id')
492
this_tree.commit('rev1', rev_id=b'rev1')
493
other_tree = this_tree.controldir.sprout('other').open_workingtree()
494
self.build_tree_contents([('this/file', b'1\n2a\n')])
495
this_tree.commit('rev2', rev_id=b'rev2a')
496
self.build_tree_contents([('other/file', b'2b\n1\n')])
497
other_tree.commit('rev2', rev_id=b'rev2b')
443
498
this_tree.lock_write()
444
499
self.addCleanup(this_tree.unlock)
445
merger = _mod_merge.Merger.from_revision_ids(None,
446
this_tree, 'rev2b', other_branch=other_tree.branch)
500
merger = _mod_merge.Merger.from_revision_ids(
501
this_tree, b'rev2b', other_branch=other_tree.branch)
447
502
merger.merge_type = _mod_merge.Merge3Merger
448
503
tree_merger = merger.make_merger()
449
504
tt = tree_merger.do_merge()
450
tree_file = this_tree.get_file('file-id')
452
self.assertEqual('2b\n1\n2a\n', tree_file.read())
505
with this_tree.get_file('file') as tree_file:
506
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
508
def test_merge_require_tree_root(self):
509
tree = self.make_branch_and_tree(".")
511
self.addCleanup(tree.unlock)
512
self.build_tree(['a'])
514
first_rev = tree.commit("added a")
515
old_root_id = tree.get_root_id()
516
merger = _mod_merge.Merger.from_revision_ids(tree,
517
_mod_revision.NULL_REVISION,
519
merger.merge_type = _mod_merge.Merge3Merger
520
conflict_count = merger.do_merge()
521
self.assertEqual(0, conflict_count)
522
self.assertEqual({''}, set(tree.all_versioned_paths()))
523
tree.set_parent_ids([])
456
525
def test_merge_add_into_deleted_root(self):
457
526
# Yes, people actually do this. And report bugs if it breaks.
458
527
source = self.make_branch_and_tree('source', format='rich-root-pack')
459
528
self.build_tree(['source/foo/'])
460
source.add('foo', 'foo-id')
529
source.add('foo', b'foo-id')
461
530
source.commit('Add foo')
462
target = source.bzrdir.sprout('target').open_workingtree()
463
subtree = target.extract('foo-id')
531
target = source.controldir.sprout('target').open_workingtree()
532
subtree = target.extract('foo')
464
533
subtree.commit('Delete root')
465
534
self.build_tree(['source/bar'])
466
source.add('bar', 'bar-id')
535
source.add('bar', b'bar-id')
467
536
source.commit('Add bar')
468
537
subtree.merge_from_branch(source.branch)
506
576
def add_uncommitted_version(self, key, parents, text):
507
577
self.plan_merge_vf.add_lines(key, parents,
508
[c+'\n' for c in text])
578
[int2byte(c) + b'\n' for c in bytearray(text)])
510
580
def setup_plan_merge(self):
511
self.add_rev('root', 'A', [], 'abc')
512
self.add_rev('root', 'B', ['A'], 'acehg')
513
self.add_rev('root', 'C', ['A'], 'fabg')
514
return _PlanMerge('B', 'C', self.plan_merge_vf, ('root',))
581
self.add_rev(b'root', b'A', [], b'abc')
582
self.add_rev(b'root', b'B', [b'A'], b'acehg')
583
self.add_rev(b'root', b'C', [b'A'], b'fabg')
584
return _PlanMerge(b'B', b'C', self.plan_merge_vf, (b'root',))
516
586
def setup_plan_merge_uncommitted(self):
517
self.add_version(('root', 'A'), [], 'abc')
518
self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
519
self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
520
return _PlanMerge('B:', 'C:', self.plan_merge_vf, ('root',))
587
self.add_version((b'root', b'A'), [], b'abc')
588
self.add_uncommitted_version(
589
(b'root', b'B:'), [(b'root', b'A')], b'acehg')
590
self.add_uncommitted_version(
591
(b'root', b'C:'), [(b'root', b'A')], b'fabg')
592
return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
522
594
def test_base_from_plan(self):
523
595
self.setup_plan_merge()
524
plan = self.plan_merge_vf.plan_merge('B', 'C')
596
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
525
597
pwm = versionedfile.PlanWeaveMerge(plan)
526
self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
598
self.assertEqual([b'a\n', b'b\n', b'c\n'], pwm.base_from_plan())
528
600
def test_unique_lines(self):
529
601
plan = self.setup_plan_merge()
530
602
self.assertEqual(plan._unique_lines(
531
plan._get_matching_blocks('B', 'C')),
603
plan._get_matching_blocks(b'B', b'C')),
532
604
([1, 2, 3], [0, 2]))
534
606
def test_plan_merge(self):
535
607
self.setup_plan_merge()
536
plan = self.plan_merge_vf.plan_merge('B', 'C')
608
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
537
609
self.assertEqual([
539
('unchanged', 'a\n'),
611
('unchanged', b'a\n'),
612
('killed-a', b'b\n'),
613
('killed-b', b'c\n'),
548
620
def test_plan_merge_cherrypick(self):
549
self.add_rev('root', 'A', [], 'abc')
550
self.add_rev('root', 'B', ['A'], 'abcde')
551
self.add_rev('root', 'C', ['A'], 'abcefg')
552
self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
553
my_plan = _PlanMerge('B', 'D', self.plan_merge_vf, ('root',))
621
self.add_rev(b'root', b'A', [], b'abc')
622
self.add_rev(b'root', b'B', [b'A'], b'abcde')
623
self.add_rev(b'root', b'C', [b'A'], b'abcefg')
624
self.add_rev(b'root', b'D', [b'A', b'B', b'C'], b'abcdegh')
625
my_plan = _PlanMerge(b'B', b'D', self.plan_merge_vf, (b'root',))
554
626
# We shortcut when one text supersedes the other in the per-file graph.
555
627
# We don't actually need to compare the texts at this point.
556
628
self.assertEqual([
564
list(my_plan.plan_merge()))
636
list(my_plan.plan_merge()))
566
638
def test_plan_merge_no_common_ancestor(self):
567
self.add_rev('root', 'A', [], 'abc')
568
self.add_rev('root', 'B', [], 'xyz')
569
my_plan = _PlanMerge('A', 'B', self.plan_merge_vf, ('root',))
639
self.add_rev(b'root', b'A', [], b'abc')
640
self.add_rev(b'root', b'B', [], b'xyz')
641
my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
570
642
self.assertEqual([
577
list(my_plan.plan_merge()))
649
list(my_plan.plan_merge()))
579
651
def test_plan_merge_tail_ancestors(self):
580
652
# The graph looks like this:
851
923
# XX unused ancestor, should not show up in the weave
855
927
# B C B & C both introduce a new line
859
931
# D E B & C are both merged, so both are common ancestors
860
932
# In the process of merging, both sides order the new
861
933
# lines differently
863
self.add_rev('root', 'XX', [], 'qrs')
864
self.add_rev('root', 'A', ['XX'], 'abcdef')
865
self.add_rev('root', 'B', ['A'], 'abcdgef')
866
self.add_rev('root', 'C', ['A'], 'abcdhef')
867
self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
868
self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
869
plan = list(self.plan_merge_vf.plan_merge('D', 'E'))
935
self.add_rev(b'root', b'XX', [], b'qrs')
936
self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
937
self.add_rev(b'root', b'B', [b'A'], b'abcdgef')
938
self.add_rev(b'root', b'C', [b'A'], b'abcdhef')
939
self.add_rev(b'root', b'D', [b'B', b'C'], b'abcdghef')
940
self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
941
plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
870
942
self.assertEqual([
871
('unchanged', 'a\n'),
872
('unchanged', 'b\n'),
873
('unchanged', 'c\n'),
874
('unchanged', 'd\n'),
876
('unchanged', 'g\n'),
878
('unchanged', 'e\n'),
879
('unchanged', 'f\n'),
943
('unchanged', b'a\n'),
944
('unchanged', b'b\n'),
945
('unchanged', b'c\n'),
946
('unchanged', b'd\n'),
948
('unchanged', b'g\n'),
949
('killed-b', b'h\n'),
950
('unchanged', b'e\n'),
951
('unchanged', b'f\n'),
881
953
pwm = versionedfile.PlanWeaveMerge(plan)
882
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
883
''.join(pwm.base_from_plan()))
954
self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
955
b''.join(pwm.base_from_plan()))
884
956
# Reversing the order reverses the merge plan, and final order of 'hg'
886
plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
958
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
887
959
self.assertEqual([
888
('unchanged', 'a\n'),
889
('unchanged', 'b\n'),
890
('unchanged', 'c\n'),
891
('unchanged', 'd\n'),
893
('unchanged', 'h\n'),
895
('unchanged', 'e\n'),
896
('unchanged', 'f\n'),
960
('unchanged', b'a\n'),
961
('unchanged', b'b\n'),
962
('unchanged', b'c\n'),
963
('unchanged', b'd\n'),
965
('unchanged', b'h\n'),
966
('killed-b', b'g\n'),
967
('unchanged', b'e\n'),
968
('unchanged', b'f\n'),
898
970
pwm = versionedfile.PlanWeaveMerge(plan)
899
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
900
''.join(pwm.base_from_plan()))
971
self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
972
b''.join(pwm.base_from_plan()))
901
973
# This is where lca differs, in that it (fairly correctly) determines
902
974
# that there is a conflict because both sides resolved the merge
904
plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
976
plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
905
977
self.assertEqual([
906
('unchanged', 'a\n'),
907
('unchanged', 'b\n'),
908
('unchanged', 'c\n'),
909
('unchanged', 'd\n'),
910
('conflicted-b', 'h\n'),
911
('unchanged', 'g\n'),
912
('conflicted-a', 'h\n'),
913
('unchanged', 'e\n'),
914
('unchanged', 'f\n'),
978
('unchanged', b'a\n'),
979
('unchanged', b'b\n'),
980
('unchanged', b'c\n'),
981
('unchanged', b'd\n'),
982
('conflicted-b', b'h\n'),
983
('unchanged', b'g\n'),
984
('conflicted-a', b'h\n'),
985
('unchanged', b'e\n'),
986
('unchanged', b'f\n'),
916
988
pwm = versionedfile.PlanWeaveMerge(plan)
917
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
918
''.join(pwm.base_from_plan()))
989
self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
990
b''.join(pwm.base_from_plan()))
919
991
# Reversing it changes what line is doubled, but still gives a
920
992
# double-conflict
921
plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
993
plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
922
994
self.assertEqual([
923
('unchanged', 'a\n'),
924
('unchanged', 'b\n'),
925
('unchanged', 'c\n'),
926
('unchanged', 'd\n'),
927
('conflicted-b', 'g\n'),
928
('unchanged', 'h\n'),
929
('conflicted-a', 'g\n'),
930
('unchanged', 'e\n'),
931
('unchanged', 'f\n'),
995
('unchanged', b'a\n'),
996
('unchanged', b'b\n'),
997
('unchanged', b'c\n'),
998
('unchanged', b'd\n'),
999
('conflicted-b', b'g\n'),
1000
('unchanged', b'h\n'),
1001
('conflicted-a', b'g\n'),
1002
('unchanged', b'e\n'),
1003
('unchanged', b'f\n'),
933
1005
pwm = versionedfile.PlanWeaveMerge(plan)
934
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
935
''.join(pwm.base_from_plan()))
1006
self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
1007
b''.join(pwm.base_from_plan()))
937
1009
def assertRemoveExternalReferences(self, filtered_parent_map,
938
1010
child_map, tails, parent_map):
996
1068
self.assertPruneTails({1: []}, [5],
997
1069
{1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
998
1070
# Prune a partial chain
999
self.assertPruneTails({1: [6], 6:[]}, [5],
1071
self.assertPruneTails({1: [6], 6: []}, [5],
1000
1072
{1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
1002
1074
# Prune a chain with multiple tips, that pulls out intermediates
1003
self.assertPruneTails({1:[3], 3:[]}, [4, 5],
1004
{1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1005
self.assertPruneTails({1:[3], 3:[]}, [5, 4],
1006
{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: []})
1008
1080
def test_subtract_plans(self):
1010
('unchanged', 'a\n'),
1012
('killed-a', 'c\n'),
1015
('killed-b', 'f\n'),
1016
('killed-b', 'g\n'),
1082
('unchanged', b'a\n'),
1084
('killed-a', b'c\n'),
1087
('killed-b', b'f\n'),
1088
('killed-b', b'g\n'),
1019
('unchanged', 'a\n'),
1021
('killed-a', 'c\n'),
1024
('killed-b', 'f\n'),
1025
('killed-b', 'i\n'),
1091
('unchanged', b'a\n'),
1093
('killed-a', b'c\n'),
1096
('killed-b', b'f\n'),
1097
('killed-b', b'i\n'),
1027
1099
subtracted_plan = [
1028
('unchanged', 'a\n'),
1030
('killed-a', 'c\n'),
1032
('unchanged', 'f\n'),
1033
('killed-b', 'i\n'),
1100
('unchanged', b'a\n'),
1102
('killed-a', b'c\n'),
1104
('unchanged', b'f\n'),
1105
('killed-b', b'i\n'),
1035
1107
self.assertEqual(subtracted_plan,
1036
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1108
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1038
1110
def setup_merge_with_base(self):
1039
self.add_rev('root', 'COMMON', [], 'abc')
1040
self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
1041
self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
1042
self.add_rev('root', 'OTHER', ['BASE'], 'eafb')
1111
self.add_rev(b'root', b'COMMON', [], b'abc')
1112
self.add_rev(b'root', b'THIS', [b'COMMON'], b'abcd')
1113
self.add_rev(b'root', b'BASE', [b'COMMON'], b'eabc')
1114
self.add_rev(b'root', b'OTHER', [b'BASE'], b'eafb')
1044
1116
def test_plan_merge_with_base(self):
1045
1117
self.setup_merge_with_base()
1046
plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
1047
self.assertEqual([('unchanged', 'a\n'),
1049
('unchanged', 'b\n'),
1050
('killed-b', 'c\n'),
1118
plan = self.plan_merge_vf.plan_merge(b'THIS', b'OTHER', b'BASE')
1119
self.assertEqual([('unchanged', b'a\n'),
1121
('unchanged', b'b\n'),
1122
('killed-b', b'c\n'),
1054
1126
def test_plan_lca_merge(self):
1055
1127
self.setup_plan_merge()
1056
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1128
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1057
1129
self.assertEqual([
1059
('unchanged', 'a\n'),
1060
('killed-b', 'c\n'),
1063
('killed-a', 'b\n'),
1064
('unchanged', 'g\n')],
1131
('unchanged', b'a\n'),
1132
('killed-b', b'c\n'),
1135
('killed-a', b'b\n'),
1136
('unchanged', b'g\n')],
1067
1139
def test_plan_lca_merge_uncommitted_files(self):
1068
1140
self.setup_plan_merge_uncommitted()
1069
plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1141
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1070
1142
self.assertEqual([
1072
('unchanged', 'a\n'),
1073
('killed-b', 'c\n'),
1076
('killed-a', 'b\n'),
1077
('unchanged', 'g\n')],
1144
('unchanged', b'a\n'),
1145
('killed-b', b'c\n'),
1148
('killed-a', b'b\n'),
1149
('unchanged', b'g\n')],
1080
1152
def test_plan_lca_merge_with_base(self):
1081
1153
self.setup_merge_with_base()
1082
plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
1083
self.assertEqual([('unchanged', 'a\n'),
1085
('unchanged', 'b\n'),
1086
('killed-b', 'c\n'),
1154
plan = self.plan_merge_vf.plan_lca_merge(b'THIS', b'OTHER', b'BASE')
1155
self.assertEqual([('unchanged', b'a\n'),
1157
('unchanged', b'b\n'),
1158
('killed-b', b'c\n'),
1090
1162
def test_plan_lca_merge_with_criss_cross(self):
1091
self.add_version(('root', 'ROOT'), [], 'abc')
1163
self.add_version((b'root', b'ROOT'), [], b'abc')
1092
1164
# each side makes a change
1093
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1094
self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
1165
self.add_version((b'root', b'REV1'), [(b'root', b'ROOT')], b'abcd')
1166
self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
1095
1167
# both sides merge, discarding others' changes
1096
self.add_version(('root', 'LCA1'),
1097
[('root', 'REV1'), ('root', 'REV2')], 'abcd')
1098
self.add_version(('root', 'LCA2'),
1099
[('root', 'REV1'), ('root', 'REV2')], 'fabce')
1100
plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
1101
self.assertEqual([('new-b', 'f\n'),
1102
('unchanged', 'a\n'),
1103
('unchanged', 'b\n'),
1104
('unchanged', 'c\n'),
1105
('conflicted-a', 'd\n'),
1106
('conflicted-b', 'e\n'),
1168
self.add_version((b'root', b'LCA1'),
1169
[(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1170
self.add_version((b'root', b'LCA2'),
1171
[(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1172
plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1173
self.assertEqual([('new-b', b'f\n'),
1174
('unchanged', b'a\n'),
1175
('unchanged', b'b\n'),
1176
('unchanged', b'c\n'),
1177
('conflicted-a', b'd\n'),
1178
('conflicted-b', b'e\n'),
1109
1181
def test_plan_lca_merge_with_null(self):
1110
self.add_version(('root', 'A'), [], 'ab')
1111
self.add_version(('root', 'B'), [], 'bc')
1112
plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
1113
self.assertEqual([('new-a', 'a\n'),
1114
('unchanged', 'b\n'),
1182
self.add_version((b'root', b'A'), [], b'ab')
1183
self.add_version((b'root', b'B'), [], b'bc')
1184
plan = self.plan_merge_vf.plan_lca_merge(b'A', b'B')
1185
self.assertEqual([('new-a', b'a\n'),
1186
('unchanged', b'b\n'),
1118
1190
def test_plan_merge_with_delete_and_change(self):
1119
self.add_rev('root', 'C', [], 'a')
1120
self.add_rev('root', 'A', ['C'], 'b')
1121
self.add_rev('root', 'B', ['C'], '')
1122
plan = self.plan_merge_vf.plan_merge('A', 'B')
1123
self.assertEqual([('killed-both', 'a\n'),
1191
self.add_rev(b'root', b'C', [], b'a')
1192
self.add_rev(b'root', b'A', [b'C'], b'b')
1193
self.add_rev(b'root', b'B', [b'C'], b'')
1194
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1195
self.assertEqual([('killed-both', b'a\n'),
1127
1199
def test_plan_merge_with_move_and_change(self):
1128
self.add_rev('root', 'C', [], 'abcd')
1129
self.add_rev('root', 'A', ['C'], 'acbd')
1130
self.add_rev('root', 'B', ['C'], 'aBcd')
1131
plan = self.plan_merge_vf.plan_merge('A', 'B')
1132
self.assertEqual([('unchanged', 'a\n'),
1134
('killed-b', 'b\n'),
1136
('killed-a', 'c\n'),
1137
('unchanged', 'd\n'),
1200
self.add_rev(b'root', b'C', [], b'abcd')
1201
self.add_rev(b'root', b'A', [b'C'], b'acbd')
1202
self.add_rev(b'root', b'B', [b'C'], b'aBcd')
1203
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1204
self.assertEqual([('unchanged', b'a\n'),
1206
('killed-b', b'b\n'),
1208
('killed-a', b'c\n'),
1209
('unchanged', b'd\n'),
1141
1213
class LoggingMerger(object):
1262
1332
builder = self.setup_criss_cross_graph()
1263
builder.build_snapshot('F-id', ['A-id'], [])
1264
builder.build_snapshot('H-id', ['E-id', 'F-id'], [])
1265
builder.build_snapshot('G-id', ['D-id', 'F-id'], [])
1266
merger = self.make_Merger(builder, 'H-id')
1267
self.assertEqual(['B-id', 'C-id', 'F-id'],
1333
builder.build_snapshot([b'A-id'], [], revision_id=b'F-id')
1334
builder.build_snapshot([b'E-id', b'F-id'], [], revision_id=b'H-id')
1335
builder.build_snapshot([b'D-id', b'F-id'], [], revision_id=b'G-id')
1336
merger = self.make_Merger(builder, b'H-id')
1337
self.assertEqual([b'B-id', b'C-id', b'F-id'],
1268
1338
[t.get_revision_id() for t in merger._lca_trees])
1340
def test_find_base_new_root_criss_cross(self):
1346
builder = self.get_builder()
1347
builder.build_snapshot(None,
1348
[('add', ('', None, 'directory', None))],
1349
revision_id=b'A-id')
1350
builder.build_snapshot([],
1351
[('add', ('', None, 'directory', None))],
1352
revision_id=b'B-id')
1353
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'D-id')
1354
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'C-id')
1355
merger = self.make_Merger(builder, b'D-id')
1356
self.assertEqual(b'A-id', merger.base_rev_id)
1357
self.assertTrue(merger._is_criss_cross)
1358
self.assertEqual([b'A-id', b'B-id'], [t.get_revision_id()
1359
for t in merger._lca_trees])
1270
1361
def test_no_criss_cross_passed_to_merge_type(self):
1271
1362
class LCATreesMerger(LoggingMerger):
1272
1363
supports_lca_trees = True
1274
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1365
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1275
1366
merger.merge_type = LCATreesMerger
1276
1367
merge_obj = merger.make_merger()
1277
1368
self.assertIsInstance(merge_obj, LCATreesMerger)
1278
1369
self.assertFalse('lca_trees' in merge_obj.kwargs)
1280
1371
def test_criss_cross_passed_to_merge_type(self):
1281
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1372
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1282
1373
merger.merge_type = _mod_merge.Merge3Merger
1283
1374
merge_obj = merger.make_merger()
1284
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1285
for t in merger._lca_trees])
1375
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1376
for t in merger._lca_trees])
1287
1378
def test_criss_cross_not_supported_merge_type(self):
1288
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1379
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1289
1380
# We explicitly do not define supports_lca_trees
1290
1381
merger.merge_type = LoggingMerger
1291
1382
merge_obj = merger.make_merger()
1306
1397
class TestMergerEntriesLCA(TestMergerBase):
1308
1399
def make_merge_obj(self, builder, other_revision_id,
1309
interesting_files=None, interesting_ids=None):
1400
interesting_files=None):
1310
1401
merger = self.make_Merger(builder, other_revision_id,
1311
interesting_files=interesting_files,
1312
interesting_ids=interesting_ids)
1402
interesting_files=interesting_files)
1313
1403
return merger.make_merger()
1315
1405
def test_simple(self):
1316
1406
builder = self.get_builder()
1317
builder.build_snapshot('A-id', None,
1318
[('add', (u'', 'a-root-id', 'directory', None)),
1319
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1320
builder.build_snapshot('C-id', ['A-id'],
1321
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1322
builder.build_snapshot('B-id', ['A-id'],
1323
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1324
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1325
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1326
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1327
[('modify', ('a-id', 'a\nB\nb\nC\nc\n'))])
1328
merge_obj = self.make_merge_obj(builder, 'E-id')
1407
builder.build_snapshot(None,
1408
[('add', (u'', b'a-root-id', 'directory', None)),
1409
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1410
revision_id=b'A-id')
1411
builder.build_snapshot([b'A-id'],
1412
[('modify', ('a', b'a\nb\nC\nc\n'))],
1413
revision_id=b'C-id')
1414
builder.build_snapshot([b'A-id'],
1415
[('modify', ('a', b'a\nB\nb\nc\n'))],
1416
revision_id=b'B-id')
1417
builder.build_snapshot([b'C-id', b'B-id'],
1418
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1419
revision_id=b'E-id')
1420
builder.build_snapshot([b'B-id', b'C-id'],
1421
[('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1422
revision_id=b'D-id', )
1423
merge_obj = self.make_merge_obj(builder, b'E-id')
1330
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1331
for t in merge_obj._lca_trees])
1332
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1425
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1426
for t in merge_obj._lca_trees])
1427
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1333
1428
entries = list(merge_obj._entries_lca())
1335
1430
# (file_id, changed, parents, names, executable)
1336
1431
# BASE, lca1, lca2, OTHER, THIS
1337
root_id = 'a-root-id'
1338
self.assertEqual([('a-id', True,
1432
root_id = b'a-root-id'
1433
self.assertEqual([(b'a-id', True,
1434
((u'a', [u'a', u'a']), u'a', u'a'),
1339
1435
((root_id, [root_id, root_id]), root_id, root_id),
1340
1436
((u'a', [u'a', u'a']), u'a', u'a'),
1341
1437
((False, [False, False]), False, False)),
1344
1440
def test_not_in_base(self):
1345
1441
# LCAs all have the same last-modified revision for the file, as do
1354
1450
# G modifies 'bar'
1356
1452
builder = self.get_builder()
1357
builder.build_snapshot('A-id', None,
1358
[('add', (u'', 'a-root-id', 'directory', None))])
1359
builder.build_snapshot('B-id', ['A-id'],
1360
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1361
builder.build_snapshot('C-id', ['A-id'],
1362
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1363
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1364
[('add', (u'bar', 'bar-id', 'file', 'd\ne\nf\n'))])
1365
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1366
[('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
1367
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1368
[('modify', (u'bar-id', 'd\ne\nf\nG\n'))])
1369
builder.build_snapshot('F-id', ['D-id', 'E-id'], [])
1370
merge_obj = self.make_merge_obj(builder, 'G-id')
1453
builder.build_snapshot(None,
1454
[('add', (u'', b'a-root-id', 'directory', None))],
1455
revision_id=b'A-id')
1456
builder.build_snapshot([b'A-id'],
1457
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1458
revision_id=b'B-id')
1459
builder.build_snapshot([b'A-id'],
1460
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1461
revision_id=b'C-id')
1462
builder.build_snapshot([b'B-id', b'C-id'],
1463
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1464
revision_id=b'D-id')
1465
builder.build_snapshot([b'C-id', b'B-id'],
1466
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1467
revision_id=b'E-id')
1468
builder.build_snapshot([b'E-id', b'D-id'],
1469
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1470
revision_id=b'G-id')
1471
builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1472
merge_obj = self.make_merge_obj(builder, b'G-id')
1372
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1373
for t in merge_obj._lca_trees])
1374
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1474
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1475
for t in merge_obj._lca_trees])
1476
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1375
1477
entries = list(merge_obj._entries_lca())
1376
root_id = 'a-root-id'
1377
self.assertEqual([('bar-id', True,
1478
root_id = b'a-root-id'
1479
self.assertEqual([(b'bar-id', True,
1480
((None, [u'bar', u'bar']), u'bar', u'bar'),
1378
1481
((None, [root_id, root_id]), root_id, root_id),
1379
1482
((None, [u'bar', u'bar']), u'bar', u'bar'),
1380
1483
((None, [False, False]), False, False)),
1383
1486
def test_not_in_this(self):
1384
1487
builder = self.get_builder()
1385
builder.build_snapshot('A-id', None,
1386
[('add', (u'', 'a-root-id', 'directory', None)),
1387
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1388
builder.build_snapshot('B-id', ['A-id'],
1389
[('modify', ('a-id', 'a\nB\nb\nc\n'))])
1390
builder.build_snapshot('C-id', ['A-id'],
1391
[('modify', ('a-id', 'a\nb\nC\nc\n'))])
1392
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1393
[('modify', ('a-id', 'a\nB\nb\nC\nc\nE\n'))])
1394
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1395
[('unversion', 'a-id')])
1396
merge_obj = self.make_merge_obj(builder, 'E-id')
1488
builder.build_snapshot(None,
1489
[('add', (u'', b'a-root-id', 'directory', None)),
1490
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1491
revision_id=b'A-id')
1492
builder.build_snapshot([b'A-id'],
1493
[('modify', ('a', b'a\nB\nb\nc\n'))],
1494
revision_id=b'B-id')
1495
builder.build_snapshot([b'A-id'],
1496
[('modify', ('a', b'a\nb\nC\nc\n'))],
1497
revision_id=b'C-id')
1498
builder.build_snapshot([b'C-id', b'B-id'],
1499
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1500
revision_id=b'E-id')
1501
builder.build_snapshot([b'B-id', b'C-id'],
1502
[('unversion', 'a')],
1503
revision_id=b'D-id')
1504
merge_obj = self.make_merge_obj(builder, b'E-id')
1398
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1399
for t in merge_obj._lca_trees])
1400
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1506
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1507
for t in merge_obj._lca_trees])
1508
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1402
1510
entries = list(merge_obj._entries_lca())
1403
root_id = 'a-root-id'
1404
self.assertEqual([('a-id', True,
1511
root_id = b'a-root-id'
1512
self.assertEqual([(b'a-id', True,
1513
((u'a', [u'a', u'a']), u'a', None),
1405
1514
((root_id, [root_id, root_id]), root_id, None),
1406
1515
((u'a', [u'a', u'a']), u'a', None),
1407
1516
((False, [False, False]), False, None)),
1410
1519
def test_file_not_in_one_lca(self):
1411
1520
# A # just root
1415
1524
# D E # D and E both have the file, unchanged from C
1416
1525
builder = self.get_builder()
1417
builder.build_snapshot('A-id', None,
1418
[('add', (u'', 'a-root-id', 'directory', None))])
1419
builder.build_snapshot('B-id', ['A-id'], [])
1420
builder.build_snapshot('C-id', ['A-id'],
1421
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1422
builder.build_snapshot('E-id', ['C-id', 'B-id'], []) # Inherited from C
1423
builder.build_snapshot('D-id', ['B-id', 'C-id'], # Merged from C
1424
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1425
merge_obj = self.make_merge_obj(builder, 'E-id')
1526
builder.build_snapshot(None,
1527
[('add', (u'', b'a-root-id', 'directory', None))],
1528
revision_id=b'A-id')
1529
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1530
builder.build_snapshot([b'A-id'],
1531
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1532
revision_id=b'C-id')
1533
builder.build_snapshot([b'C-id', b'B-id'],
1534
[], revision_id=b'E-id') # Inherited from C
1535
builder.build_snapshot([b'B-id', b'C-id'], # Merged from C
1536
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1537
revision_id=b'D-id')
1538
merge_obj = self.make_merge_obj(builder, b'E-id')
1427
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1428
for t in merge_obj._lca_trees])
1429
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1540
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1541
for t in merge_obj._lca_trees])
1542
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1431
1544
entries = list(merge_obj._entries_lca())
1432
1545
self.assertEqual([], entries)
1434
1547
def test_not_in_other(self):
1435
1548
builder = self.get_builder()
1436
builder.build_snapshot('A-id', None,
1437
[('add', (u'', 'a-root-id', 'directory', None)),
1438
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1439
builder.build_snapshot('B-id', ['A-id'], [])
1440
builder.build_snapshot('C-id', ['A-id'], [])
1441
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1442
[('unversion', 'a-id')])
1443
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1444
merge_obj = self.make_merge_obj(builder, 'E-id')
1549
builder.build_snapshot(None,
1550
[('add', (u'', b'a-root-id', 'directory', None)),
1551
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1552
revision_id=b'A-id')
1553
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1554
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1555
builder.build_snapshot(
1557
[('unversion', 'a')], revision_id=b'E-id')
1558
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1559
merge_obj = self.make_merge_obj(builder, b'E-id')
1446
1561
entries = list(merge_obj._entries_lca())
1447
root_id = 'a-root-id'
1448
self.assertEqual([('a-id', True,
1562
root_id = b'a-root-id'
1563
self.assertEqual([(b'a-id', True,
1564
((u'a', [u'a', u'a']), None, u'a'),
1449
1565
((root_id, [root_id, root_id]), None, root_id),
1450
1566
((u'a', [u'a', u'a']), None, u'a'),
1451
1567
((False, [False, False]), None, False)),
1454
1570
def test_not_in_other_or_lca(self):
1455
1571
# A base, introduces 'foo'
1529
1650
# A => C, add file, thus C supersedes B
1530
1651
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1531
1652
builder = self.get_builder()
1532
builder.build_snapshot('A-id', None,
1533
[('add', (u'', 'a-root-id', 'directory', None))])
1534
builder.build_snapshot('B-id', ['A-id'], [])
1535
builder.build_snapshot('C-id', ['A-id'],
1536
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1537
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1538
[('unversion', 'a-id')])
1539
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1540
merge_obj = self.make_merge_obj(builder, 'E-id')
1653
builder.build_snapshot(None,
1654
[('add', (u'', b'a-root-id', 'directory', None))],
1655
revision_id=b'A-id')
1656
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1657
builder.build_snapshot([b'A-id'],
1658
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1659
revision_id=b'C-id')
1660
builder.build_snapshot([b'C-id', b'B-id'],
1661
[('unversion', 'a')],
1662
revision_id=b'E-id')
1663
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1664
merge_obj = self.make_merge_obj(builder, b'E-id')
1542
1666
entries = list(merge_obj._entries_lca())
1543
1667
self.assertEqual([], entries)
1545
1669
def test_only_in_other(self):
1546
1670
builder = self.get_builder()
1547
builder.build_snapshot('A-id', None,
1548
[('add', (u'', 'a-root-id', 'directory', None))])
1549
builder.build_snapshot('B-id', ['A-id'], [])
1550
builder.build_snapshot('C-id', ['A-id'], [])
1551
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1552
[('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1553
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1554
merge_obj = self.make_merge_obj(builder, 'E-id')
1671
builder.build_snapshot(None,
1672
[('add', (u'', b'a-root-id', 'directory', None))],
1673
revision_id=b'A-id')
1674
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1675
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1676
builder.build_snapshot([b'C-id', b'B-id'],
1677
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1678
revision_id=b'E-id')
1679
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1680
merge_obj = self.make_merge_obj(builder, b'E-id')
1556
1682
entries = list(merge_obj._entries_lca())
1557
root_id = 'a-root-id'
1558
self.assertEqual([('a-id', True,
1683
root_id = b'a-root-id'
1684
self.assertEqual([(b'a-id', True,
1685
((None, [None, None]), u'a', None),
1559
1686
((None, [None, None]), root_id, None),
1560
1687
((None, [None, None]), u'a', None),
1561
1688
((None, [None, None]), False, None)),
1564
1691
def test_one_lca_supersedes(self):
1565
1692
# One LCA supersedes the other LCAs last modified value, but the
1655
1791
# be pruned from the LCAs, even though it was newly introduced by E
1656
1792
# (superseding B).
1657
1793
builder = self.get_builder()
1658
builder.build_snapshot('A-id', None,
1659
[('add', (u'', 'a-root-id', 'directory', None)),
1660
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1661
builder.build_snapshot('C-id', ['A-id'], [])
1662
builder.build_snapshot('B-id', ['A-id'],
1663
[('rename', ('foo', 'bar'))])
1664
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1665
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1666
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1667
[('rename', ('foo', 'bar'))])
1668
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1669
[('rename', ('bar', 'bing'))]) # should end up conflicting
1670
merge_obj = self.make_merge_obj(builder, 'G-id')
1794
builder.build_snapshot(None,
1795
[('add', (u'', b'a-root-id', 'directory', None)),
1796
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1797
revision_id=b'A-id')
1798
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1799
builder.build_snapshot([b'A-id'],
1800
[('rename', ('foo', 'bar'))],
1801
revision_id=b'B-id')
1802
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1803
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1804
builder.build_snapshot([b'E-id', b'D-id'],
1805
[('rename', ('foo', 'bar'))],
1806
revision_id=b'G-id')
1807
builder.build_snapshot([b'D-id', b'E-id'],
1808
[('rename', ('bar', 'bing'))],
1809
revision_id=b'F-id') # should end up conflicting
1810
merge_obj = self.make_merge_obj(builder, b'G-id')
1672
1812
entries = list(merge_obj._entries_lca())
1673
root_id = 'a-root-id'
1813
root_id = b'a-root-id'
1674
1814
self.expectFailure("We prune values from BASE even when relevant.",
1677
((root_id, [root_id, root_id]), root_id, root_id),
1678
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1679
((False, [False, False]), False, False)),
1817
((root_id, [root_id, root_id]), root_id, root_id),
1818
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1819
((False, [False, False]), False, False)),
1682
1822
def test_both_sides_revert(self):
1683
1823
# Both sides of a criss-cross revert the text to the lca
1755
1904
# We need to conflict.
1757
1906
builder = self.get_builder()
1758
builder.build_snapshot('A-id', None,
1759
[('add', (u'', 'a-root-id', 'directory', None)),
1760
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1761
builder.build_snapshot('B-id', ['A-id'],
1762
[('modify', ('foo-id', 'B content\n'))])
1763
builder.build_snapshot('C-id', ['A-id'],
1764
[('modify', ('foo-id', 'C content\n'))])
1765
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1766
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1767
[('modify', ('foo-id', 'C content\n'))]) # Same as E
1768
builder.build_snapshot('F-id', ['D-id'],
1769
[('modify', ('foo-id', 'F content\n'))])
1770
merge_obj = self.make_merge_obj(builder, 'E-id')
1907
builder.build_snapshot(None,
1908
[('add', (u'', b'a-root-id', 'directory', None)),
1909
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1910
revision_id=b'A-id')
1911
builder.build_snapshot([b'A-id'],
1912
[('modify', ('foo', b'B content\n'))],
1913
revision_id=b'B-id')
1914
builder.build_snapshot([b'A-id'],
1915
[('modify', ('foo', b'C content\n'))],
1916
revision_id=b'C-id')
1917
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1918
builder.build_snapshot([b'B-id', b'C-id'],
1919
[('modify', ('foo', b'C content\n'))],
1920
revision_id=b'D-id') # Same as E
1921
builder.build_snapshot([b'D-id'],
1922
[('modify', ('foo', b'F content\n'))],
1923
revision_id=b'F-id')
1924
merge_obj = self.make_merge_obj(builder, b'E-id')
1772
1926
entries = list(merge_obj._entries_lca())
1773
1927
self.expectFailure("We don't detect that LCA resolution was the"
1774
1928
" same on both sides",
1775
self.assertEqual, [], entries)
1929
self.assertEqual, [], entries)
1777
1931
def test_only_path_changed(self):
1778
1932
builder = self.get_builder()
1779
builder.build_snapshot('A-id', None,
1780
[('add', (u'', 'a-root-id', 'directory', None)),
1781
('add', (u'a', 'a-id', 'file', 'content\n'))])
1782
builder.build_snapshot('B-id', ['A-id'], [])
1783
builder.build_snapshot('C-id', ['A-id'], [])
1784
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1785
[('rename', (u'a', u'b'))])
1786
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1787
merge_obj = self.make_merge_obj(builder, 'E-id')
1933
builder.build_snapshot(None,
1934
[('add', (u'', b'a-root-id', 'directory', None)),
1935
('add', (u'a', b'a-id', 'file', b'content\n'))],
1936
revision_id=b'A-id')
1937
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1938
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1939
builder.build_snapshot([b'C-id', b'B-id'],
1940
[('rename', (u'a', u'b'))],
1941
revision_id=b'E-id')
1942
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1943
merge_obj = self.make_merge_obj(builder, b'E-id')
1788
1944
entries = list(merge_obj._entries_lca())
1789
root_id = 'a-root-id'
1945
root_id = b'a-root-id'
1790
1946
# The content was not changed, only the path
1791
self.assertEqual([('a-id', False,
1947
self.assertEqual([(b'a-id', False,
1948
((u'a', [u'a', u'a']), u'b', u'a'),
1792
1949
((root_id, [root_id, root_id]), root_id, root_id),
1793
1950
((u'a', [u'a', u'a']), u'b', u'a'),
1794
1951
((False, [False, False]), False, False)),
1797
1954
def test_kind_changed(self):
1798
1955
# Identical content, except 'D' changes a-id into a directory
1799
1956
builder = self.get_builder()
1800
builder.build_snapshot('A-id', None,
1801
[('add', (u'', 'a-root-id', 'directory', None)),
1802
('add', (u'a', 'a-id', 'file', 'content\n'))])
1803
builder.build_snapshot('B-id', ['A-id'], [])
1804
builder.build_snapshot('C-id', ['A-id'], [])
1805
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1806
[('unversion', 'a-id'),
1807
('add', (u'a', 'a-id', 'directory', None))])
1808
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1809
merge_obj = self.make_merge_obj(builder, 'E-id')
1957
builder.build_snapshot(None,
1958
[('add', (u'', b'a-root-id', 'directory', None)),
1959
('add', (u'a', b'a-id', 'file', b'content\n'))],
1960
revision_id=b'A-id')
1961
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1962
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1963
builder.build_snapshot([b'C-id', b'B-id'],
1964
[('unversion', 'a'),
1966
('add', (u'a', b'a-id', 'directory', None))],
1967
revision_id=b'E-id')
1968
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1969
merge_obj = self.make_merge_obj(builder, b'E-id')
1810
1970
entries = list(merge_obj._entries_lca())
1811
root_id = 'a-root-id'
1971
root_id = b'a-root-id'
1812
1972
# Only the kind was changed (content)
1813
self.assertEqual([('a-id', True,
1973
self.assertEqual([(b'a-id', True,
1974
((u'a', [u'a', u'a']), u'a', u'a'),
1814
1975
((root_id, [root_id, root_id]), root_id, root_id),
1815
1976
((u'a', [u'a', u'a']), u'a', u'a'),
1816
1977
((False, [False, False]), False, False)),
1819
1980
def test_this_changed_kind(self):
1820
1981
# Identical content, but THIS changes a file to a directory
1821
1982
builder = self.get_builder()
1822
builder.build_snapshot('A-id', None,
1823
[('add', (u'', 'a-root-id', 'directory', None)),
1824
('add', (u'a', 'a-id', 'file', 'content\n'))])
1825
builder.build_snapshot('B-id', ['A-id'], [])
1826
builder.build_snapshot('C-id', ['A-id'], [])
1827
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1828
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1829
[('unversion', 'a-id'),
1830
('add', (u'a', 'a-id', 'directory', None))])
1831
merge_obj = self.make_merge_obj(builder, 'E-id')
1983
builder.build_snapshot(None,
1984
[('add', (u'', b'a-root-id', 'directory', None)),
1985
('add', (u'a', b'a-id', 'file', b'content\n'))],
1986
revision_id=b'A-id')
1987
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1988
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1989
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1990
builder.build_snapshot([b'B-id', b'C-id'],
1991
[('unversion', 'a'),
1993
('add', (u'a', b'a-id', 'directory', None))],
1994
revision_id=b'D-id')
1995
merge_obj = self.make_merge_obj(builder, b'E-id')
1832
1996
entries = list(merge_obj._entries_lca())
1833
1997
# Only the kind was changed (content)
1834
1998
self.assertEqual([], entries)
1836
2000
def test_interesting_files(self):
1837
2001
# Two files modified, but we should filter one of them
1838
2002
builder = self.get_builder()
1839
builder.build_snapshot('A-id', None,
1840
[('add', (u'', 'a-root-id', 'directory', None)),
1841
('add', (u'a', 'a-id', 'file', 'content\n')),
1842
('add', (u'b', 'b-id', 'file', 'content\n'))])
1843
builder.build_snapshot('B-id', ['A-id'], [])
1844
builder.build_snapshot('C-id', ['A-id'], [])
1845
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1846
[('modify', ('a-id', 'new-content\n')),
1847
('modify', ('b-id', 'new-content\n'))])
1848
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1849
merge_obj = self.make_merge_obj(builder, 'E-id',
2003
builder.build_snapshot(None,
2004
[('add', (u'', b'a-root-id', 'directory', None)),
2005
('add', (u'a', b'a-id', 'file', b'content\n')),
2006
('add', (u'b', b'b-id', 'file', b'content\n'))],
2007
revision_id=b'A-id')
2008
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2009
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2010
builder.build_snapshot([b'C-id', b'B-id'],
2011
[('modify', ('a', b'new-content\n')),
2012
('modify', ('b', b'new-content\n'))],
2013
revision_id=b'E-id')
2014
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2015
merge_obj = self.make_merge_obj(builder, b'E-id',
1850
2016
interesting_files=['b'])
1851
2017
entries = list(merge_obj._entries_lca())
1852
root_id = 'a-root-id'
1853
self.assertEqual([('b-id', True,
2018
root_id = b'a-root-id'
2019
self.assertEqual([(b'b-id', True,
2020
((u'b', [u'b', u'b']), u'b', u'b'),
1854
2021
((root_id, [root_id, root_id]), root_id, root_id),
1855
2022
((u'b', [u'b', u'b']), u'b', u'b'),
1856
2023
((False, [False, False]), False, False)),
1859
2026
def test_interesting_file_in_this(self):
1860
2027
# This renamed the file, but it should still match the entry in other
1861
2028
builder = self.get_builder()
1862
builder.build_snapshot('A-id', None,
1863
[('add', (u'', 'a-root-id', 'directory', None)),
1864
('add', (u'a', 'a-id', 'file', 'content\n')),
1865
('add', (u'b', 'b-id', 'file', 'content\n'))])
1866
builder.build_snapshot('B-id', ['A-id'], [])
1867
builder.build_snapshot('C-id', ['A-id'], [])
1868
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1869
[('modify', ('a-id', 'new-content\n')),
1870
('modify', ('b-id', 'new-content\n'))])
1871
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1872
[('rename', ('b', 'c'))])
1873
merge_obj = self.make_merge_obj(builder, 'E-id',
2029
builder.build_snapshot(None,
2030
[('add', (u'', b'a-root-id', 'directory', None)),
2031
('add', (u'a', b'a-id', 'file', b'content\n')),
2032
('add', (u'b', b'b-id', 'file', b'content\n'))],
2033
revision_id=b'A-id')
2034
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2035
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2036
builder.build_snapshot([b'C-id', b'B-id'],
2037
[('modify', ('a', b'new-content\n')),
2038
('modify', ('b', b'new-content\n'))],
2039
revision_id=b'E-id')
2040
builder.build_snapshot([b'B-id', b'C-id'],
2041
[('rename', ('b', 'c'))],
2042
revision_id=b'D-id')
2043
merge_obj = self.make_merge_obj(builder, b'E-id',
1874
2044
interesting_files=['c'])
1875
2045
entries = list(merge_obj._entries_lca())
1876
root_id = 'a-root-id'
1877
self.assertEqual([('b-id', True,
2046
root_id = b'a-root-id'
2047
self.assertEqual([(b'b-id', True,
2048
((u'b', [u'b', u'b']), u'b', u'c'),
1878
2049
((root_id, [root_id, root_id]), root_id, root_id),
1879
2050
((u'b', [u'b', u'b']), u'b', u'c'),
1880
2051
((False, [False, False]), False, False)),
1883
2054
def test_interesting_file_in_base(self):
1884
2055
# This renamed the file, but it should still match the entry in BASE
1885
2056
builder = self.get_builder()
1886
builder.build_snapshot('A-id', None,
1887
[('add', (u'', 'a-root-id', 'directory', None)),
1888
('add', (u'a', 'a-id', 'file', 'content\n')),
1889
('add', (u'c', 'c-id', 'file', 'content\n'))])
1890
builder.build_snapshot('B-id', ['A-id'],
1891
[('rename', ('c', 'b'))])
1892
builder.build_snapshot('C-id', ['A-id'],
1893
[('rename', ('c', 'b'))])
1894
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1895
[('modify', ('a-id', 'new-content\n')),
1896
('modify', ('c-id', 'new-content\n'))])
1897
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1898
merge_obj = self.make_merge_obj(builder, 'E-id',
2057
builder.build_snapshot(None,
2058
[('add', (u'', b'a-root-id', 'directory', None)),
2059
('add', (u'a', b'a-id', 'file', b'content\n')),
2060
('add', (u'c', b'c-id', 'file', b'content\n'))],
2061
revision_id=b'A-id')
2062
builder.build_snapshot([b'A-id'],
2063
[('rename', ('c', 'b'))],
2064
revision_id=b'B-id')
2065
builder.build_snapshot([b'A-id'],
2066
[('rename', ('c', 'b'))],
2067
revision_id=b'C-id')
2068
builder.build_snapshot([b'C-id', b'B-id'],
2069
[('modify', ('a', b'new-content\n')),
2070
('modify', ('b', b'new-content\n'))],
2071
revision_id=b'E-id')
2072
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2073
merge_obj = self.make_merge_obj(builder, b'E-id',
1899
2074
interesting_files=['c'])
1900
2075
entries = list(merge_obj._entries_lca())
1901
root_id = 'a-root-id'
1902
self.assertEqual([('c-id', True,
2076
root_id = b'a-root-id'
2077
self.assertEqual([(b'c-id', True,
2078
((u'c', [u'b', u'b']), u'b', u'b'),
1903
2079
((root_id, [root_id, root_id]), root_id, root_id),
1904
2080
((u'c', [u'b', u'b']), u'b', u'b'),
1905
2081
((False, [False, False]), False, False)),
1908
2084
def test_interesting_file_in_lca(self):
1909
2085
# This renamed the file, but it should still match the entry in LCA
1910
2086
builder = self.get_builder()
1911
builder.build_snapshot('A-id', None,
1912
[('add', (u'', 'a-root-id', 'directory', None)),
1913
('add', (u'a', 'a-id', 'file', 'content\n')),
1914
('add', (u'b', 'b-id', 'file', 'content\n'))])
1915
builder.build_snapshot('B-id', ['A-id'],
1916
[('rename', ('b', 'c'))])
1917
builder.build_snapshot('C-id', ['A-id'], [])
1918
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1919
[('modify', ('a-id', 'new-content\n')),
1920
('modify', ('b-id', 'new-content\n'))])
1921
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1922
[('rename', ('c', 'b'))])
1923
merge_obj = self.make_merge_obj(builder, 'E-id',
2087
builder.build_snapshot(None,
2088
[('add', (u'', b'a-root-id', 'directory', None)),
2089
('add', (u'a', b'a-id', 'file', b'content\n')),
2090
('add', (u'b', b'b-id', 'file', b'content\n'))],
2091
revision_id=b'A-id')
2092
builder.build_snapshot([b'A-id'],
2093
[('rename', ('b', 'c'))], revision_id=b'B-id')
2094
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2095
builder.build_snapshot([b'C-id', b'B-id'],
2096
[('modify', ('a', b'new-content\n')),
2097
('modify', ('b', b'new-content\n'))],
2098
revision_id=b'E-id')
2099
builder.build_snapshot([b'B-id', b'C-id'],
2100
[('rename', ('c', 'b'))], revision_id=b'D-id')
2101
merge_obj = self.make_merge_obj(builder, b'E-id',
1924
2102
interesting_files=['c'])
1925
2103
entries = list(merge_obj._entries_lca())
1926
root_id = 'a-root-id'
1927
self.assertEqual([('b-id', True,
2104
root_id = b'a-root-id'
2105
self.assertEqual([(b'b-id', True,
2106
((u'b', [u'c', u'b']), u'b', u'b'),
1928
2107
((root_id, [root_id, root_id]), root_id, root_id),
1929
2108
((u'b', [u'c', u'b']), u'b', u'b'),
1930
2109
((False, [False, False]), False, False)),
1933
def test_interesting_ids(self):
2112
def test_interesting_files(self):
1934
2113
# Two files modified, but we should filter one of them
1935
2114
builder = self.get_builder()
1936
builder.build_snapshot('A-id', None,
1937
[('add', (u'', 'a-root-id', 'directory', None)),
1938
('add', (u'a', 'a-id', 'file', 'content\n')),
1939
('add', (u'b', 'b-id', 'file', 'content\n'))])
1940
builder.build_snapshot('B-id', ['A-id'], [])
1941
builder.build_snapshot('C-id', ['A-id'], [])
1942
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1943
[('modify', ('a-id', 'new-content\n')),
1944
('modify', ('b-id', 'new-content\n'))])
1945
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1946
merge_obj = self.make_merge_obj(builder, 'E-id',
1947
interesting_ids=['b-id'])
2115
builder.build_snapshot(None,
2116
[('add', (u'', b'a-root-id', 'directory', None)),
2117
('add', (u'a', b'a-id', 'file', b'content\n')),
2118
('add', (u'b', b'b-id', 'file', b'content\n'))],
2119
revision_id=b'A-id')
2120
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2121
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2122
builder.build_snapshot([b'C-id', b'B-id'],
2123
[('modify', ('a', b'new-content\n')),
2124
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2125
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2126
merge_obj = self.make_merge_obj(builder, b'E-id',
2127
interesting_files=['b'])
1948
2128
entries = list(merge_obj._entries_lca())
1949
root_id = 'a-root-id'
1950
self.assertEqual([('b-id', True,
2129
root_id = b'a-root-id'
2130
self.assertEqual([(b'b-id', True,
2131
((u'b', [u'b', u'b']), u'b', u'b'),
1951
2132
((root_id, [root_id, root_id]), root_id, root_id),
1952
2133
((u'b', [u'b', u'b']), u'b', u'b'),
1953
2134
((False, [False, False]), False, False)),
1958
2138
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2223
2414
wt.lock_write()
2224
2415
self.addCleanup(wt.unlock)
2225
2416
os.symlink('bar', 'path/foo')
2226
wt.add(['foo'], ['foo-id'])
2227
wt.commit('A add symlink', rev_id='A-id')
2417
wt.add(['foo'], [b'foo-id'])
2418
wt.commit('A add symlink', rev_id=b'A-id')
2228
2419
wt.rename_one('foo', 'barry')
2229
wt.commit('B foo => barry', rev_id='B-id')
2230
wt.set_last_revision('A-id')
2231
wt.branch.set_last_revision_info(1, 'A-id')
2420
wt.commit('B foo => barry', rev_id=b'B-id')
2421
wt.set_last_revision(b'A-id')
2422
wt.branch.set_last_revision_info(1, b'A-id')
2233
wt.commit('C', rev_id='C-id')
2234
wt.merge_from_branch(wt.branch, 'B-id')
2235
self.assertEqual('barry', wt.id2path('foo-id'))
2236
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2237
wt.commit('E merges C & B', rev_id='E-id')
2424
wt.commit('C', rev_id=b'C-id')
2425
wt.merge_from_branch(wt.branch, b'B-id')
2426
self.assertEqual('barry', wt.id2path(b'foo-id'))
2427
self.assertEqual('bar', wt.get_symlink_target('barry'))
2428
wt.commit('E merges C & B', rev_id=b'E-id')
2238
2429
wt.rename_one('barry', 'blah')
2239
wt.commit('F barry => blah', rev_id='F-id')
2240
wt.set_last_revision('B-id')
2241
wt.branch.set_last_revision_info(2, 'B-id')
2430
wt.commit('F barry => blah', rev_id=b'F-id')
2431
wt.set_last_revision(b'B-id')
2432
wt.branch.set_last_revision_info(2, b'B-id')
2243
wt.merge_from_branch(wt.branch, 'C-id')
2244
wt.commit('D merges B & C', rev_id='D-id')
2245
self.assertEqual('barry', wt.id2path('foo-id'))
2434
wt.merge_from_branch(wt.branch, b'C-id')
2435
wt.commit('D merges B & C', rev_id=b'D-id')
2436
self.assertEqual('barry', wt.id2path(b'foo-id'))
2246
2437
# Check the output of the Merger object directly
2247
merger = _mod_merge.Merger.from_revision_ids(None,
2438
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2249
2439
merger.merge_type = _mod_merge.Merge3Merger
2250
2440
merge_obj = merger.make_merger()
2251
2441
root_id = wt.path2id('')
2252
2442
entries = list(merge_obj._entries_lca())
2253
2443
# No content change, just a path change
2254
self.assertEqual([('foo-id', False,
2444
self.assertEqual([(b'foo-id', False,
2445
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2255
2446
((root_id, [root_id, root_id]), root_id, root_id),
2256
2447
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2257
2448
((False, [False, False]), False, False)),
2259
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2450
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2260
2451
self.assertEqual(0, conflicts)
2261
self.assertEqual('blah', wt.id2path('foo-id'))
2452
self.assertEqual('blah', wt.id2path(b'foo-id'))
2263
2454
def test_symlink_no_content_change(self):
2264
self.requireFeature(tests.SymlinkFeature)
2455
self.requireFeature(features.SymlinkFeature)
2265
2456
# A Create symlink foo => bar
2267
2458
# B C B relinks foo => baz
2326
2516
wt = self.make_branch_and_tree('path')
2327
2517
wt.lock_write()
2328
2518
self.addCleanup(wt.unlock)
2329
wt.commit('base', rev_id='A-id')
2519
wt.commit('base', rev_id=b'A-id')
2330
2520
os.symlink('bar', 'path/foo')
2331
wt.add(['foo'], ['foo-id'])
2332
wt.commit('add symlink foo => bar', rev_id='B-id')
2333
wt.set_last_revision('A-id')
2334
wt.branch.set_last_revision_info(1, 'A-id')
2521
wt.add(['foo'], [b'foo-id'])
2522
wt.commit('add symlink foo => bar', rev_id=b'B-id')
2523
wt.set_last_revision(b'A-id')
2524
wt.branch.set_last_revision_info(1, b'A-id')
2336
wt.commit('C', rev_id='C-id')
2337
wt.merge_from_branch(wt.branch, 'B-id')
2338
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2526
wt.commit('C', rev_id=b'C-id')
2527
wt.merge_from_branch(wt.branch, b'B-id')
2528
self.assertEqual('bar', wt.get_symlink_target('foo'))
2339
2529
os.remove('path/foo')
2340
2530
# We have to change the link in E, or it won't try to do a comparison
2341
2531
os.symlink('bing', 'path/foo')
2342
wt.commit('E merges C & B, overrides to bing', rev_id='E-id')
2343
wt.set_last_revision('B-id')
2344
wt.branch.set_last_revision_info(2, 'B-id')
2532
wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2533
wt.set_last_revision(b'B-id')
2534
wt.branch.set_last_revision_info(2, b'B-id')
2346
wt.merge_from_branch(wt.branch, 'C-id')
2536
wt.merge_from_branch(wt.branch, b'C-id')
2347
2537
os.remove('path/foo')
2348
self.build_tree_contents([('path/foo', 'file content\n')])
2538
self.build_tree_contents([('path/foo', b'file content\n')])
2349
2539
# XXX: workaround, WT doesn't detect kind changes unless you do
2350
2540
# iter_changes()
2351
2541
list(wt.iter_changes(wt.basis_tree()))
2352
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2542
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2354
merger = _mod_merge.Merger.from_revision_ids(None,
2544
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2356
2545
merger.merge_type = _mod_merge.Merge3Merger
2357
2546
merge_obj = merger.make_merger()
2358
2547
entries = list(merge_obj._entries_lca())
2359
2548
root_id = wt.path2id('')
2360
self.assertEqual([('foo-id', True,
2549
self.assertEqual([(b'foo-id', True,
2550
((None, [u'foo', None]), u'foo', u'foo'),
2361
2551
((None, [root_id, None]), root_id, root_id),
2362
2552
((None, [u'foo', None]), u'foo', u'foo'),
2363
2553
((None, [False, None]), False, False)),
2366
2556
def test_symlink_all_wt(self):
2367
2557
"""Check behavior if all trees are Working Trees."""
2368
self.requireFeature(tests.SymlinkFeature)
2558
self.requireFeature(features.SymlinkFeature)
2369
2559
# The big issue is that entry.symlink_target is None for WorkingTrees.
2370
2560
# So we need to make sure we handle that case correctly.
2382
2572
wt.lock_write()
2383
2573
self.addCleanup(wt.unlock)
2384
2574
os.symlink('bar', 'path/foo')
2385
wt.add(['foo'], ['foo-id'])
2386
wt.commit('add symlink', rev_id='A-id')
2575
wt.add(['foo'], [b'foo-id'])
2576
wt.commit('add symlink', rev_id=b'A-id')
2387
2577
os.remove('path/foo')
2388
2578
os.symlink('baz', 'path/foo')
2389
wt.commit('foo => baz', rev_id='B-id')
2390
wt.set_last_revision('A-id')
2391
wt.branch.set_last_revision_info(1, 'A-id')
2579
wt.commit('foo => baz', rev_id=b'B-id')
2580
wt.set_last_revision(b'A-id')
2581
wt.branch.set_last_revision_info(1, b'A-id')
2393
wt.commit('C', rev_id='C-id')
2394
wt.merge_from_branch(wt.branch, 'B-id')
2395
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2396
wt.commit('E merges C & B', rev_id='E-id')
2583
wt.commit('C', rev_id=b'C-id')
2584
wt.merge_from_branch(wt.branch, b'B-id')
2585
self.assertEqual('baz', wt.get_symlink_target('foo'))
2586
wt.commit('E merges C & B', rev_id=b'E-id')
2397
2587
os.remove('path/foo')
2398
2588
os.symlink('bing', 'path/foo')
2399
wt.commit('F foo => bing', rev_id='F-id')
2400
wt.set_last_revision('B-id')
2401
wt.branch.set_last_revision_info(2, 'B-id')
2589
wt.commit('F foo => bing', rev_id=b'F-id')
2590
wt.set_last_revision(b'B-id')
2591
wt.branch.set_last_revision_info(2, b'B-id')
2403
wt.merge_from_branch(wt.branch, 'C-id')
2404
wt.commit('D merges B & C', rev_id='D-id')
2405
wt_base = wt.bzrdir.sprout('base', 'A-id').open_workingtree()
2593
wt.merge_from_branch(wt.branch, b'C-id')
2594
wt.commit('D merges B & C', rev_id=b'D-id')
2595
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2406
2596
wt_base.lock_read()
2407
2597
self.addCleanup(wt_base.unlock)
2408
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2598
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2409
2599
wt_lca1.lock_read()
2410
2600
self.addCleanup(wt_lca1.unlock)
2411
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2601
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2412
2602
wt_lca2.lock_read()
2413
2603
self.addCleanup(wt_lca2.unlock)
2414
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2604
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2415
2605
wt_other.lock_read()
2416
2606
self.addCleanup(wt_other.unlock)
2417
2607
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2418
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2608
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2419
2609
entries = list(merge_obj._entries_lca())
2420
2610
root_id = wt.path2id('')
2421
self.assertEqual([('foo-id', True,
2611
self.assertEqual([(b'foo-id', True,
2612
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2422
2613
((root_id, [root_id, root_id]), root_id, root_id),
2423
2614
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2424
2615
((False, [False, False]), False, False)),
2427
2618
def test_other_reverted_path_to_base(self):
2428
2619
# A Path at 'foo'
2436
2627
# F Path at 'foo'
2437
2628
builder = self.get_builder()
2438
builder.build_snapshot('A-id', None,
2439
[('add', (u'', 'a-root-id', 'directory', None)),
2440
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2441
builder.build_snapshot('C-id', ['A-id'], [])
2442
builder.build_snapshot('B-id', ['A-id'],
2443
[('rename', ('foo', 'bar'))])
2444
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2445
[('rename', ('foo', 'bar'))]) # merge the rename
2446
builder.build_snapshot('F-id', ['E-id'],
2447
[('rename', ('bar', 'foo'))]) # Rename back to BASE
2448
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2449
wt, conflicts = self.do_merge(builder, 'F-id')
2629
builder.build_snapshot(None,
2630
[('add', (u'', b'a-root-id', 'directory', None)),
2631
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2632
revision_id=b'A-id')
2633
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2634
builder.build_snapshot([b'A-id'],
2635
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2636
builder.build_snapshot([b'C-id', b'B-id'],
2637
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2638
builder.build_snapshot([b'E-id'],
2639
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2640
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2641
wt, conflicts = self.do_merge(builder, b'F-id')
2450
2642
self.assertEqual(0, conflicts)
2451
self.assertEqual('foo', wt.id2path('foo-id'))
2643
self.assertEqual('foo', wt.id2path(b'foo-id'))
2453
2645
def test_other_reverted_content_to_base(self):
2454
2646
builder = self.get_builder()
2455
builder.build_snapshot('A-id', None,
2456
[('add', (u'', 'a-root-id', 'directory', None)),
2457
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2458
builder.build_snapshot('C-id', ['A-id'], [])
2459
builder.build_snapshot('B-id', ['A-id'],
2460
[('modify', ('foo-id', 'B content\n'))])
2461
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2462
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2463
builder.build_snapshot('F-id', ['E-id'],
2464
[('modify', ('foo-id', 'base content\n'))]) # Revert back to BASE
2465
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2466
wt, conflicts = self.do_merge(builder, 'F-id')
2647
builder.build_snapshot(None,
2648
[('add', (u'', b'a-root-id', 'directory', None)),
2649
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2650
revision_id=b'A-id')
2651
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2652
builder.build_snapshot([b'A-id'],
2653
[('modify', ('foo', b'B content\n'))],
2654
revision_id=b'B-id')
2655
builder.build_snapshot([b'C-id', b'B-id'],
2656
[('modify', ('foo', b'B content\n'))],
2657
revision_id=b'E-id') # merge the content
2658
builder.build_snapshot([b'E-id'],
2659
[('modify', ('foo', b'base content\n'))],
2660
revision_id=b'F-id') # Revert back to BASE
2661
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2662
wt, conflicts = self.do_merge(builder, b'F-id')
2467
2663
self.assertEqual(0, conflicts)
2468
2664
# TODO: We need to use the per-file graph to properly select a BASE
2469
2665
# before this will work. Or at least use the LCA trees to find
2470
2666
# the appropriate content base. (which is B, not A).
2471
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2667
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2473
2669
def test_other_modified_content(self):
2474
2670
builder = self.get_builder()
2475
builder.build_snapshot('A-id', None,
2476
[('add', (u'', 'a-root-id', 'directory', None)),
2477
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2478
builder.build_snapshot('C-id', ['A-id'], [])
2479
builder.build_snapshot('B-id', ['A-id'],
2480
[('modify', ('foo-id', 'B content\n'))])
2481
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2482
[('modify', ('foo-id', 'B content\n'))]) # merge the content
2483
builder.build_snapshot('F-id', ['E-id'],
2484
[('modify', ('foo-id', 'F content\n'))]) # Override B content
2485
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2486
wt, conflicts = self.do_merge(builder, 'F-id')
2671
builder.build_snapshot(None,
2672
[('add', (u'', b'a-root-id', 'directory', None)),
2673
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2674
revision_id=b'A-id')
2675
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2676
builder.build_snapshot([b'A-id'],
2677
[('modify', ('foo', b'B content\n'))],
2678
revision_id=b'B-id')
2679
builder.build_snapshot([b'C-id', b'B-id'],
2680
[('modify', ('foo', b'B content\n'))],
2681
revision_id=b'E-id') # merge the content
2682
builder.build_snapshot([b'E-id'],
2683
[('modify', ('foo', b'F content\n'))],
2684
revision_id=b'F-id') # Override B content
2685
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2686
wt, conflicts = self.do_merge(builder, b'F-id')
2487
2687
self.assertEqual(0, conflicts)
2488
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2688
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2490
2690
def test_all_wt(self):
2491
2691
"""Check behavior if all trees are Working Trees."""
2499
2699
# D E E updates content, renames 'b' => 'c'
2500
2700
builder = self.get_builder()
2501
builder.build_snapshot('A-id', None,
2502
[('add', (u'', 'a-root-id', 'directory', None)),
2503
('add', (u'a', 'a-id', 'file', 'base content\n')),
2504
('add', (u'foo', 'foo-id', 'file', 'base content\n'))])
2505
builder.build_snapshot('B-id', ['A-id'],
2506
[('modify', ('foo-id', 'B content\n'))])
2507
builder.build_snapshot('C-id', ['A-id'],
2508
[('rename', ('a', 'b'))])
2509
builder.build_snapshot('E-id', ['C-id', 'B-id'],
2510
[('rename', ('b', 'c')),
2511
('modify', ('foo-id', 'E content\n'))])
2512
builder.build_snapshot('D-id', ['B-id', 'C-id'],
2513
[('rename', ('a', 'b'))]) # merged change
2701
builder.build_snapshot(None,
2702
[('add', (u'', b'a-root-id', 'directory', None)),
2703
('add', (u'a', b'a-id', 'file', b'base content\n')),
2704
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2705
revision_id=b'A-id')
2706
builder.build_snapshot([b'A-id'],
2707
[('modify', ('foo', b'B content\n'))],
2708
revision_id=b'B-id')
2709
builder.build_snapshot([b'A-id'],
2710
[('rename', ('a', 'b'))],
2711
revision_id=b'C-id')
2712
builder.build_snapshot([b'C-id', b'B-id'],
2713
[('rename', ('b', 'c')),
2714
('modify', ('foo', b'E content\n'))],
2715
revision_id=b'E-id')
2716
builder.build_snapshot([b'B-id', b'C-id'],
2717
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2514
2718
wt_this = self.get_wt_from_builder(builder)
2515
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2719
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2516
2720
wt_base.lock_read()
2517
2721
self.addCleanup(wt_base.unlock)
2518
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2722
wt_lca1 = wt_this.controldir.sprout(
2723
'b-tree', b'B-id').open_workingtree()
2519
2724
wt_lca1.lock_read()
2520
2725
self.addCleanup(wt_lca1.unlock)
2521
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2726
wt_lca2 = wt_this.controldir.sprout(
2727
'c-tree', b'C-id').open_workingtree()
2522
2728
wt_lca2.lock_read()
2523
2729
self.addCleanup(wt_lca2.unlock)
2524
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2730
wt_other = wt_this.controldir.sprout(
2731
'other', b'E-id').open_workingtree()
2525
2732
wt_other.lock_read()
2526
2733
self.addCleanup(wt_other.unlock)
2527
2734
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2528
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2735
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2529
2736
entries = list(merge_obj._entries_lca())
2530
root_id = 'a-root-id'
2531
self.assertEqual([('a-id', False,
2532
((root_id, [root_id, root_id]), root_id, root_id),
2533
((u'a', [u'a', u'b']), u'c', u'b'),
2534
((False, [False, False]), False, False)),
2536
((root_id, [root_id, root_id]), root_id, root_id),
2537
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2538
((False, [False, False]), False, False)),
2737
root_id = b'a-root-id'
2738
self.assertEqual([(b'a-id', False,
2739
((u'a', [u'a', u'b']), u'c', u'b'),
2740
((root_id, [root_id, root_id]), root_id, root_id),
2741
((u'a', [u'a', u'b']), u'c', u'b'),
2742
((False, [False, False]), False, False)),
2744
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2745
((root_id, [root_id, root_id]), root_id, root_id),
2746
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2747
((False, [False, False]), False, False)),
2541
2750
def test_nested_tree_unmodified(self):
2542
2751
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2543
2752
# 'tree-reference'
2544
2753
wt = self.make_branch_and_tree('tree',
2545
format='dirstate-with-subtree')
2754
format='development-subtree')
2546
2755
wt.lock_write()
2547
2756
self.addCleanup(wt.unlock)
2548
2757
sub_tree = self.make_branch_and_tree('tree/sub-tree',
2549
format='dirstate-with-subtree')
2550
wt.set_root_id('a-root-id')
2551
sub_tree.set_root_id('sub-tree-root')
2552
self.build_tree_contents([('tree/sub-tree/file', 'text1')])
2758
format='development-subtree')
2759
wt.set_root_id(b'a-root-id')
2760
sub_tree.set_root_id(b'sub-tree-root')
2761
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2553
2762
sub_tree.add('file')
2554
sub_tree.commit('foo', rev_id='sub-A-id')
2763
sub_tree.commit('foo', rev_id=b'sub-A-id')
2555
2764
wt.add_reference(sub_tree)
2556
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2765
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2557
2766
# Now create a criss-cross merge in the parent, without modifying the
2559
wt.commit('B', rev_id='B-id', recursive=None)
2560
wt.set_last_revision('A-id')
2561
wt.branch.set_last_revision_info(1, 'A-id')
2562
wt.commit('C', rev_id='C-id', recursive=None)
2563
wt.merge_from_branch(wt.branch, to_revision='B-id')
2564
wt.commit('E', rev_id='E-id', recursive=None)
2565
wt.set_parent_ids(['B-id', 'C-id'])
2566
wt.branch.set_last_revision_info(2, 'B-id')
2567
wt.commit('D', rev_id='D-id', recursive=None)
2768
wt.commit('B', rev_id=b'B-id', recursive=None)
2769
wt.set_last_revision(b'A-id')
2770
wt.branch.set_last_revision_info(1, b'A-id')
2771
wt.commit('C', rev_id=b'C-id', recursive=None)
2772
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2773
wt.commit('E', rev_id=b'E-id', recursive=None)
2774
wt.set_parent_ids([b'B-id', b'C-id'])
2775
wt.branch.set_last_revision_info(2, b'B-id')
2776
wt.commit('D', rev_id=b'D-id', recursive=None)
2569
merger = _mod_merge.Merger.from_revision_ids(None,
2778
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2571
2779
merger.merge_type = _mod_merge.Merge3Merger
2572
2780
merge_obj = merger.make_merger()
2573
2781
entries = list(merge_obj._entries_lca())
2616
2823
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2617
2824
# 'tree-reference'
2618
2825
wt = self.make_branch_and_tree('tree',
2619
format='dirstate-with-subtree')
2826
format='development-subtree')
2620
2827
wt.lock_write()
2621
2828
self.addCleanup(wt.unlock)
2622
2829
sub_tree = self.make_branch_and_tree('tree/sub',
2623
format='dirstate-with-subtree')
2624
wt.set_root_id('a-root-id')
2625
sub_tree.set_root_id('sub-tree-root')
2626
self.build_tree_contents([('tree/sub/file', 'text1')])
2830
format='development-subtree')
2831
wt.set_root_id(b'a-root-id')
2832
sub_tree.set_root_id(b'sub-tree-root')
2833
self.build_tree_contents([('tree/sub/file', b'text1')])
2627
2834
sub_tree.add('file')
2628
sub_tree.commit('foo', rev_id='sub-A-id')
2835
sub_tree.commit('foo', rev_id=b'sub-A-id')
2629
2836
wt.add_reference(sub_tree)
2630
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2837
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2631
2838
# Now create a criss-cross merge in the parent, without modifying the
2633
wt.commit('B', rev_id='B-id', recursive=None)
2634
wt.set_last_revision('A-id')
2635
wt.branch.set_last_revision_info(1, 'A-id')
2636
wt.commit('C', rev_id='C-id', recursive=None)
2637
wt.merge_from_branch(wt.branch, to_revision='B-id')
2840
wt.commit('B', rev_id=b'B-id', recursive=None)
2841
wt.set_last_revision(b'A-id')
2842
wt.branch.set_last_revision_info(1, b'A-id')
2843
wt.commit('C', rev_id=b'C-id', recursive=None)
2844
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2638
2845
wt.rename_one('sub', 'alt_sub')
2639
wt.commit('E', rev_id='E-id', recursive=None)
2640
wt.set_last_revision('B-id')
2846
wt.commit('E', rev_id=b'E-id', recursive=None)
2847
wt.set_last_revision(b'B-id')
2642
wt.set_parent_ids(['B-id', 'C-id'])
2643
wt.branch.set_last_revision_info(2, 'B-id')
2644
wt.commit('D', rev_id='D-id', recursive=None)
2849
wt.set_parent_ids([b'B-id', b'C-id'])
2850
wt.branch.set_last_revision_info(2, b'B-id')
2851
wt.commit('D', rev_id=b'D-id', recursive=None)
2646
merger = _mod_merge.Merger.from_revision_ids(None,
2853
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2648
2854
merger.merge_type = _mod_merge.Merge3Merger
2649
2855
merge_obj = merger.make_merger()
2650
2856
entries = list(merge_obj._entries_lca())
2651
root_id = 'a-root-id'
2652
self.assertEqual([('sub-tree-root', False,
2857
root_id = b'a-root-id'
2858
self.assertEqual([(b'sub-tree-root', False,
2859
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2653
2860
((root_id, [root_id, root_id]), root_id, root_id),
2654
2861
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2655
2862
((False, [False, False]), False, False)),
2658
2865
def test_nested_tree_subtree_renamed_and_modified(self):
2659
2866
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2660
2867
# 'tree-reference'
2661
2868
wt = self.make_branch_and_tree('tree',
2662
format='dirstate-with-subtree')
2869
format='development-subtree')
2663
2870
wt.lock_write()
2664
2871
self.addCleanup(wt.unlock)
2665
2872
sub_tree = self.make_branch_and_tree('tree/sub',
2666
format='dirstate-with-subtree')
2667
wt.set_root_id('a-root-id')
2668
sub_tree.set_root_id('sub-tree-root')
2669
self.build_tree_contents([('tree/sub/file', 'text1')])
2873
format='development-subtree')
2874
wt.set_root_id(b'a-root-id')
2875
sub_tree.set_root_id(b'sub-tree-root')
2876
self.build_tree_contents([('tree/sub/file', b'text1')])
2670
2877
sub_tree.add('file')
2671
sub_tree.commit('foo', rev_id='sub-A-id')
2878
sub_tree.commit('foo', rev_id=b'sub-A-id')
2672
2879
wt.add_reference(sub_tree)
2673
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2880
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2674
2881
# Now create a criss-cross merge in the parent, without modifying the
2676
wt.commit('B', rev_id='B-id', recursive=None)
2677
wt.set_last_revision('A-id')
2678
wt.branch.set_last_revision_info(1, 'A-id')
2679
wt.commit('C', rev_id='C-id', recursive=None)
2680
wt.merge_from_branch(wt.branch, to_revision='B-id')
2681
self.build_tree_contents([('tree/sub/file', 'text2')])
2682
sub_tree.commit('modify contents', rev_id='sub-B-id')
2883
wt.commit('B', rev_id=b'B-id', recursive=None)
2884
wt.set_last_revision(b'A-id')
2885
wt.branch.set_last_revision_info(1, b'A-id')
2886
wt.commit('C', rev_id=b'C-id', recursive=None)
2887
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2888
self.build_tree_contents([('tree/sub/file', b'text2')])
2889
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2683
2890
wt.rename_one('sub', 'alt_sub')
2684
wt.commit('E', rev_id='E-id', recursive=None)
2685
wt.set_last_revision('B-id')
2891
wt.commit('E', rev_id=b'E-id', recursive=None)
2892
wt.set_last_revision(b'B-id')
2687
wt.set_parent_ids(['B-id', 'C-id'])
2688
wt.branch.set_last_revision_info(2, 'B-id')
2689
wt.commit('D', rev_id='D-id', recursive=None)
2894
wt.set_parent_ids([b'B-id', b'C-id'])
2895
wt.branch.set_last_revision_info(2, b'B-id')
2896
wt.commit('D', rev_id=b'D-id', recursive=None)
2691
merger = _mod_merge.Merger.from_revision_ids(None,
2898
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2693
2899
merger.merge_type = _mod_merge.Merge3Merger
2694
2900
merge_obj = merger.make_merger()
2695
2901
entries = list(merge_obj._entries_lca())
2696
root_id = 'a-root-id'
2697
self.assertEqual([('sub-tree-root', False,
2902
root_id = b'a-root-id'
2903
self.assertEqual([(b'sub-tree-root', False,
2904
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2698
2905
((root_id, [root_id, root_id]), root_id, root_id),
2699
2906
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2700
2907
((False, [False, False]), False, False)),
2704
2911
class TestLCAMultiWay(tests.TestCase):
2706
2913
def assertLCAMultiWay(self, expected, base, lcas, other, this,
2707
2914
allow_overriding_lca=True):
2708
2915
self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2709
(base, lcas), other, this,
2710
allow_overriding_lca=allow_overriding_lca))
2916
(base, lcas), other, this,
2917
allow_overriding_lca=allow_overriding_lca))
2712
2919
def test_other_equal_equal_lcas(self):
2713
2920
"""Test when OTHER=LCA and all LCAs are identical."""
2714
2921
self.assertLCAMultiWay('this',
2715
'bval', ['bval', 'bval'], 'bval', 'bval')
2716
self.assertLCAMultiWay('this',
2717
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2718
self.assertLCAMultiWay('this',
2719
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2720
self.assertLCAMultiWay('this',
2721
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2722
self.assertLCAMultiWay('this',
2723
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2922
'bval', ['bval', 'bval'], 'bval', 'bval')
2923
self.assertLCAMultiWay('this',
2924
'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2925
self.assertLCAMultiWay('this',
2926
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2927
self.assertLCAMultiWay('this',
2928
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2929
self.assertLCAMultiWay('this',
2930
'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2725
2932
def test_other_equal_this(self):
2726
2933
"""Test when other and this are identical."""
2727
2934
self.assertLCAMultiWay('this',
2728
'bval', ['bval', 'bval'], 'oval', 'oval')
2729
self.assertLCAMultiWay('this',
2730
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2731
self.assertLCAMultiWay('this',
2732
'bval', ['cval', 'dval'], 'oval', 'oval')
2733
self.assertLCAMultiWay('this',
2734
'bval', [None, 'lcaval'], 'oval', 'oval')
2735
self.assertLCAMultiWay('this',
2736
None, [None, 'lcaval'], 'oval', 'oval')
2737
self.assertLCAMultiWay('this',
2738
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2739
self.assertLCAMultiWay('this',
2740
None, ['cval', 'dval'], 'oval', 'oval')
2741
self.assertLCAMultiWay('this',
2742
None, ['cval', 'dval'], None, None)
2743
self.assertLCAMultiWay('this',
2744
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2935
'bval', ['bval', 'bval'], 'oval', 'oval')
2936
self.assertLCAMultiWay('this',
2937
'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2938
self.assertLCAMultiWay('this',
2939
'bval', ['cval', 'dval'], 'oval', 'oval')
2940
self.assertLCAMultiWay('this',
2941
'bval', [None, 'lcaval'], 'oval', 'oval')
2942
self.assertLCAMultiWay('this',
2943
None, [None, 'lcaval'], 'oval', 'oval')
2944
self.assertLCAMultiWay('this',
2945
None, ['lcaval', 'lcaval'], 'oval', 'oval')
2946
self.assertLCAMultiWay('this',
2947
None, ['cval', 'dval'], 'oval', 'oval')
2948
self.assertLCAMultiWay('this',
2949
None, ['cval', 'dval'], None, None)
2950
self.assertLCAMultiWay('this',
2951
None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2746
2953
def test_no_lcas(self):
2747
2954
self.assertLCAMultiWay('this',
2748
'bval', [], 'bval', 'tval')
2955
'bval', [], 'bval', 'tval')
2749
2956
self.assertLCAMultiWay('other',
2750
'bval', [], 'oval', 'bval')
2957
'bval', [], 'oval', 'bval')
2751
2958
self.assertLCAMultiWay('conflict',
2752
'bval', [], 'oval', 'tval')
2959
'bval', [], 'oval', 'tval')
2753
2960
self.assertLCAMultiWay('this',
2754
'bval', [], 'oval', 'oval')
2961
'bval', [], 'oval', 'oval')
2756
2963
def test_lca_supersedes_other_lca(self):
2757
2964
"""If one lca == base, the other lca takes precedence"""
2758
2965
self.assertLCAMultiWay('this',
2759
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2966
'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2760
2967
self.assertLCAMultiWay('this',
2761
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2968
'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2762
2969
# This is actually considered a 'revert' because the 'lcaval' in LCAS
2763
2970
# supersedes the BASE val (in the other LCA) but then OTHER reverts it
2764
2971
# back to bval.
2765
2972
self.assertLCAMultiWay('other',
2766
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2973
'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2767
2974
self.assertLCAMultiWay('conflict',
2768
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2975
'bval', ['bval', 'lcaval'], 'bval', 'tval')
2770
2977
def test_other_and_this_pick_different_lca(self):
2771
2978
# OTHER and THIS resolve the lca conflict in different ways
2772
2979
self.assertLCAMultiWay('conflict',
2773
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2774
self.assertLCAMultiWay('conflict',
2775
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2776
self.assertLCAMultiWay('conflict',
2777
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2980
'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2981
self.assertLCAMultiWay('conflict',
2982
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2983
self.assertLCAMultiWay('conflict',
2984
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2779
2986
def test_other_in_lca(self):
2780
2987
# OTHER takes a value of one of the LCAs, THIS takes a new value, which
2781
2988
# theoretically supersedes both LCA values and 'wins'
2782
self.assertLCAMultiWay('this',
2783
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2784
self.assertLCAMultiWay('this',
2785
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
2786
self.assertLCAMultiWay('conflict',
2787
'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2788
allow_overriding_lca=False)
2789
self.assertLCAMultiWay('conflict',
2790
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2791
allow_overriding_lca=False)
2989
self.assertLCAMultiWay(
2990
'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2991
self.assertLCAMultiWay(
2992
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
2994
self.assertLCAMultiWay('conflict',
2996
'lca2val'], 'lca1val', 'newval',
2997
allow_overriding_lca=False)
2998
self.assertLCAMultiWay('conflict',
2999
'bval', ['lca1val', 'lca2val',
3000
'lca3val'], 'lca1val', 'newval',
3001
allow_overriding_lca=False)
2792
3002
# THIS reverted back to BASE, but that is an explicit supersede of all
2794
self.assertLCAMultiWay('this',
2795
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2796
self.assertLCAMultiWay('this',
2797
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
2798
self.assertLCAMultiWay('conflict',
2799
'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2800
allow_overriding_lca=False)
2801
self.assertLCAMultiWay('conflict',
2802
'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
2803
allow_overriding_lca=False)
3004
self.assertLCAMultiWay(
3005
'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
3007
self.assertLCAMultiWay(
3008
'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
3009
self.assertLCAMultiWay('conflict',
3010
'bval', ['lca1val', 'lca2val',
3011
'lca3val'], 'lca1val', 'bval',
3012
allow_overriding_lca=False)
3013
self.assertLCAMultiWay('conflict',
3014
'bval', ['lca1val', 'lca2val',
3015
'bval'], 'lca1val', 'bval',
3016
allow_overriding_lca=False)
2805
3018
def test_this_in_lca(self):
2806
3019
# THIS takes a value of one of the LCAs, OTHER takes a new value, which
2807
3020
# theoretically supersedes both LCA values and 'wins'
2808
self.assertLCAMultiWay('other',
2809
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
2810
self.assertLCAMultiWay('other',
2811
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
2812
self.assertLCAMultiWay('conflict',
2813
'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
2814
allow_overriding_lca=False)
2815
self.assertLCAMultiWay('conflict',
2816
'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
2817
allow_overriding_lca=False)
3021
self.assertLCAMultiWay(
3022
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3023
self.assertLCAMultiWay(
3024
'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3025
self.assertLCAMultiWay('conflict',
3027
'lca2val'], 'oval', 'lca1val',
3028
allow_overriding_lca=False)
3029
self.assertLCAMultiWay('conflict',
3031
'lca2val'], 'oval', 'lca2val',
3032
allow_overriding_lca=False)
2818
3033
# OTHER reverted back to BASE, but that is an explicit supersede of all
2820
self.assertLCAMultiWay('other',
2821
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
2822
self.assertLCAMultiWay('conflict',
2823
'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
2824
allow_overriding_lca=False)
3035
self.assertLCAMultiWay(
3036
'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
3038
self.assertLCAMultiWay(
3039
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
3040
'bval', 'lca3val', allow_overriding_lca=False)
2826
3042
def test_all_differ(self):
2827
self.assertLCAMultiWay('conflict',
2828
'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
2829
self.assertLCAMultiWay('conflict',
2830
'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
2831
self.assertLCAMultiWay('conflict',
2832
'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
3043
self.assertLCAMultiWay(
3044
'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3045
self.assertLCAMultiWay(
3046
'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
3048
self.assertLCAMultiWay(
3049
'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
2835
3053
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
2903
3122
def test_hook_called_for_text_conflicts(self):
2904
3123
builder = self.make_text_conflict()
2905
conflicts = builder.merge()
2906
3125
# The hook should call the merge_text() method
2907
3126
self.assertEqual(['merge_text'], self.calls)
2909
3128
def test_hook_not_called_for_kind_change(self):
2910
3129
builder = self.make_kind_change()
2911
conflicts = builder.merge()
2912
3131
# The hook should not call the merge_text() method
2913
3132
self.assertEqual([], self.calls)
2915
3134
def test_hook_not_called_for_other_files(self):
2916
3135
builder = self.make_text_conflict('foobar')
2917
conflicts = builder.merge()
2918
3137
# The hook should not call the merge_text() method
2919
3138
self.assertEqual([], self.calls)
3141
class TestMergeIntoBase(tests.TestCaseWithTransport):
3143
def setup_simple_branch(self, relpath, shape=None, root_id=None):
3144
"""One commit, containing tree specified by optional shape.
3146
Default is empty tree (just root entry).
3149
root_id = b'%s-root-id' % (relpath.encode('ascii'),)
3150
wt = self.make_branch_and_tree(relpath)
3151
wt.set_root_id(root_id)
3152
if shape is not None:
3153
adjusted_shape = [relpath + '/' + elem for elem in shape]
3154
self.build_tree(adjusted_shape)
3156
(b'%s-%s-id' % (relpath.encode('utf-8'),
3157
basename(elem.rstrip('/')).encode('ascii')))
3159
wt.add(shape, ids=ids)
3160
rev_id = b'r1-%s' % (relpath.encode('utf-8'),)
3161
wt.commit("Initial commit of %s" % (relpath,), rev_id=rev_id)
3162
self.assertEqual(root_id, wt.path2id(''))
3165
def setup_two_branches(self, custom_root_ids=True):
3166
"""Setup 2 branches, one will be a library, the other a project."""
3170
root_id = inventory.ROOT_ID
3171
project_wt = self.setup_simple_branch(
3172
'project', ['README', 'dir/', 'dir/file.c'],
3174
lib_wt = self.setup_simple_branch(
3175
'lib1', ['README', 'Makefile', 'foo.c'], root_id)
3177
return project_wt, lib_wt
3179
def do_merge_into(self, location, merge_as):
3180
"""Helper for using MergeIntoMerger.
3182
:param location: location of directory to merge from, either the
3183
location of a branch or of a path inside a branch.
3184
:param merge_as: the path in a tree to add the new directory as.
3185
:returns: the conflicts from 'do_merge'.
3187
operation = cleanup.OperationWithCleanups(self._merge_into)
3188
return operation.run(location, merge_as)
3190
def _merge_into(self, op, location, merge_as):
3191
# Open and lock the various tree and branch objects
3192
wt, subdir_relpath = WorkingTree.open_containing(merge_as)
3193
op.add_cleanup(wt.lock_write().unlock)
3194
branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
3196
op.add_cleanup(branch_to_merge.lock_read().unlock)
3197
other_tree = branch_to_merge.basis_tree()
3198
op.add_cleanup(other_tree.lock_read().unlock)
3200
merger = _mod_merge.MergeIntoMerger(
3201
this_tree=wt, other_tree=other_tree, other_branch=branch_to_merge,
3202
target_subdir=subdir_relpath, source_subpath=subdir_to_merge)
3203
merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3204
conflicts = merger.do_merge()
3205
merger.set_pending()
3208
def assertTreeEntriesEqual(self, expected_entries, tree):
3209
"""Assert that 'tree' contains the expected inventory entries.
3211
:param expected_entries: sequence of (path, file-id) pairs.
3213
files = [(path, ie.file_id) for path, ie in tree.iter_entries_by_dir()]
3214
self.assertEqual(expected_entries, files)
3217
class TestMergeInto(TestMergeIntoBase):
3219
def test_newdir_with_unique_roots(self):
3220
"""Merge a branch with a unique root into a new directory."""
3221
project_wt, lib_wt = self.setup_two_branches()
3222
self.do_merge_into('lib1', 'project/lib1')
3223
project_wt.lock_read()
3224
self.addCleanup(project_wt.unlock)
3225
# The r1-lib1 revision should be merged into this one
3226
self.assertEqual([b'r1-project', b'r1-lib1'],
3227
project_wt.get_parent_ids())
3228
self.assertTreeEntriesEqual(
3229
[('', b'project-root-id'),
3230
('README', b'project-README-id'),
3231
('dir', b'project-dir-id'),
3232
('lib1', b'lib1-root-id'),
3233
('dir/file.c', b'project-file.c-id'),
3234
('lib1/Makefile', b'lib1-Makefile-id'),
3235
('lib1/README', b'lib1-README-id'),
3236
('lib1/foo.c', b'lib1-foo.c-id'),
3239
def test_subdir(self):
3240
"""Merge a branch into a subdirectory of an existing directory."""
3241
project_wt, lib_wt = self.setup_two_branches()
3242
self.do_merge_into('lib1', 'project/dir/lib1')
3243
project_wt.lock_read()
3244
self.addCleanup(project_wt.unlock)
3245
# The r1-lib1 revision should be merged into this one
3246
self.assertEqual([b'r1-project', b'r1-lib1'],
3247
project_wt.get_parent_ids())
3248
self.assertTreeEntriesEqual(
3249
[('', b'project-root-id'),
3250
('README', b'project-README-id'),
3251
('dir', b'project-dir-id'),
3252
('dir/file.c', b'project-file.c-id'),
3253
('dir/lib1', b'lib1-root-id'),
3254
('dir/lib1/Makefile', b'lib1-Makefile-id'),
3255
('dir/lib1/README', b'lib1-README-id'),
3256
('dir/lib1/foo.c', b'lib1-foo.c-id'),
3259
def test_newdir_with_repeat_roots(self):
3260
"""If the file-id of the dir to be merged already exists a new ID will
3261
be allocated to let the merge happen.
3263
project_wt, lib_wt = self.setup_two_branches(custom_root_ids=False)
3264
root_id = project_wt.path2id('')
3265
self.do_merge_into('lib1', 'project/lib1')
3266
project_wt.lock_read()
3267
self.addCleanup(project_wt.unlock)
3268
# The r1-lib1 revision should be merged into this one
3269
self.assertEqual([b'r1-project', b'r1-lib1'],
3270
project_wt.get_parent_ids())
3271
new_lib1_id = project_wt.path2id('lib1')
3272
self.assertNotEqual(None, new_lib1_id)
3273
self.assertTreeEntriesEqual(
3275
('README', b'project-README-id'),
3276
('dir', b'project-dir-id'),
3277
('lib1', new_lib1_id),
3278
('dir/file.c', b'project-file.c-id'),
3279
('lib1/Makefile', b'lib1-Makefile-id'),
3280
('lib1/README', b'lib1-README-id'),
3281
('lib1/foo.c', b'lib1-foo.c-id'),
3284
def test_name_conflict(self):
3285
"""When the target directory name already exists a conflict is
3286
generated and the original directory is renamed to foo.moved.
3288
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3289
self.setup_simple_branch('src', ['README'])
3290
conflicts = self.do_merge_into('src', 'dest/dir')
3291
self.assertEqual(1, conflicts)
3293
self.addCleanup(dest_wt.unlock)
3294
# The r1-lib1 revision should be merged into this one
3295
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3296
self.assertTreeEntriesEqual(
3297
[('', b'dest-root-id'),
3298
('dir', b'src-root-id'),
3299
('dir.moved', b'dest-dir-id'),
3300
('dir/README', b'src-README-id'),
3301
('dir.moved/file.txt', b'dest-file.txt-id'),
3304
def test_file_id_conflict(self):
3305
"""A conflict is generated if the merge-into adds a file (or other
3306
inventory entry) with a file-id that already exists in the target tree.
3308
self.setup_simple_branch('dest', ['file.txt'])
3309
# Make a second tree with a file-id that will clash with file.txt in
3311
src_wt = self.make_branch_and_tree('src')
3312
self.build_tree(['src/README'])
3313
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3314
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3315
conflicts = self.do_merge_into('src', 'dest/dir')
3316
# This is an edge case that shouldn't happen to users very often. So
3317
# we don't care really about the exact presentation of the conflict,
3318
# just that there is one.
3319
self.assertEqual(1, conflicts)
3321
def test_only_subdir(self):
3322
"""When the location points to just part of a tree, merge just that
3325
dest_wt = self.setup_simple_branch('dest')
3326
self.setup_simple_branch('src', ['hello.txt', 'dir/', 'dir/foo.c'])
3327
self.do_merge_into('src/dir', 'dest/dir')
3329
self.addCleanup(dest_wt.unlock)
3330
# The r1-lib1 revision should NOT be merged into this one (this is a
3332
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3333
self.assertTreeEntriesEqual(
3334
[('', b'dest-root-id'),
3335
('dir', b'src-dir-id'),
3336
('dir/foo.c', b'src-foo.c-id'),
3339
def test_only_file(self):
3340
"""An edge case: merge just one file, not a whole dir."""
3341
dest_wt = self.setup_simple_branch('dest')
3342
self.setup_simple_branch('two-file', ['file1.txt', 'file2.txt'])
3343
self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3345
self.addCleanup(dest_wt.unlock)
3346
# The r1-lib1 revision should NOT be merged into this one
3347
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3348
self.assertTreeEntriesEqual(
3349
[('', b'dest-root-id'), ('file1.txt', b'two-file-file1.txt-id')],
3352
def test_no_such_source_path(self):
3353
"""PathNotInTree is raised if the specified path in the source tree
3356
dest_wt = self.setup_simple_branch('dest')
3357
self.setup_simple_branch('src', ['dir/'])
3358
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3359
'src/no-such-dir', 'dest/foo')
3361
self.addCleanup(dest_wt.unlock)
3362
# The dest tree is unmodified.
3363
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3364
self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
3366
def test_no_such_target_path(self):
3367
"""PathNotInTree is also raised if the specified path in the target
3368
tree does not exist.
3370
dest_wt = self.setup_simple_branch('dest')
3371
self.setup_simple_branch('src', ['file.txt'])
3372
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3373
'src', 'dest/no-such-dir/foo')
3375
self.addCleanup(dest_wt.unlock)
3376
# The dest tree is unmodified.
3377
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3378
self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
3381
class TestMergeHooks(TestCaseWithTransport):
3384
super(TestMergeHooks, self).setUp()
3385
self.tree_a = self.make_branch_and_tree('tree_a')
3386
self.build_tree_contents([('tree_a/file', b'content_1')])
3387
self.tree_a.add('file', b'file-id')
3388
self.tree_a.commit('added file')
3390
self.tree_b = self.tree_a.controldir.sprout(
3391
'tree_b').open_workingtree()
3392
self.build_tree_contents([('tree_b/file', b'content_2')])
3393
self.tree_b.commit('modify file')
3395
def test_pre_merge_hook_inject_different_tree(self):
3396
tree_c = self.tree_b.controldir.sprout('tree_c').open_workingtree()
3397
self.build_tree_contents([('tree_c/file', b'content_3')])
3398
tree_c.commit("more content")
3401
def factory(merger):
3402
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3403
merger.other_tree = tree_c
3404
calls.append(merger)
3405
_mod_merge.Merger.hooks.install_named_hook('pre_merge',
3406
factory, 'test factory')
3407
self.tree_a.merge_from_branch(self.tree_b.branch)
3409
self.assertFileEqual(b"content_3", 'tree_a/file')
3410
self.assertLength(1, calls)
3412
def test_post_merge_hook_called(self):
3415
def factory(merger):
3416
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3417
calls.append(merger)
3418
_mod_merge.Merger.hooks.install_named_hook('post_merge',
3419
factory, 'test factory')
3421
self.tree_a.merge_from_branch(self.tree_b.branch)
3423
self.assertFileEqual(b"content_2", 'tree_a/file')
3424
self.assertLength(1, calls)