326
360
'c', but not 'b'.
328
362
this_tree = self.make_branch_and_tree('this')
329
self.build_tree_contents([('this/file', "a\n")])
363
self.build_tree_contents([('this/file', b"a\n")])
330
364
this_tree.add('file')
331
365
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')
366
other_tree = this_tree.controldir.sprout('other').open_workingtree()
367
self.build_tree_contents([('other/file', b"a\nb\n")])
368
other_tree.commit('rev2b', rev_id=b'rev2b')
369
self.build_tree_contents([('other/file', b"c\na\nb\n")])
370
other_tree.commit('rev3b', rev_id=b'rev3b')
337
371
this_tree.lock_write()
338
372
self.addCleanup(this_tree.unlock)
339
373
return this_tree, other_tree
341
375
def test_weave_cherrypick(self):
342
376
this_tree, other_tree = self.prepare_cherrypick()
343
merger = _mod_merge.Merger.from_revision_ids(None,
344
this_tree, 'rev3b', 'rev2b', other_tree.branch)
377
merger = _mod_merge.Merger.from_revision_ids(
378
this_tree, b'rev3b', b'rev2b', other_tree.branch)
345
379
merger.merge_type = _mod_merge.WeaveMerger
346
380
merger.do_merge()
347
self.assertFileEqual('c\na\n', 'this/file')
381
self.assertFileEqual(b'c\na\n', 'this/file')
349
383
def test_weave_cannot_reverse_cherrypick(self):
350
384
this_tree, other_tree = self.prepare_cherrypick()
351
merger = _mod_merge.Merger.from_revision_ids(None,
352
this_tree, 'rev2b', 'rev3b', other_tree.branch)
385
merger = _mod_merge.Merger.from_revision_ids(
386
this_tree, b'rev2b', b'rev3b', other_tree.branch)
353
387
merger.merge_type = _mod_merge.WeaveMerger
354
388
self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
356
390
def test_merge3_can_reverse_cherrypick(self):
357
391
this_tree, other_tree = self.prepare_cherrypick()
358
merger = _mod_merge.Merger.from_revision_ids(None,
359
this_tree, 'rev2b', 'rev3b', other_tree.branch)
392
merger = _mod_merge.Merger.from_revision_ids(
393
this_tree, b'rev2b', b'rev3b', other_tree.branch)
360
394
merger.merge_type = _mod_merge.Merge3Merger
361
395
merger.do_merge()
363
397
def test_merge3_will_detect_cherrypick(self):
364
398
this_tree = self.make_branch_and_tree('this')
365
self.build_tree_contents([('this/file', "a\n")])
399
self.build_tree_contents([('this/file', b"a\n")])
366
400
this_tree.add('file')
367
401
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')
402
other_tree = this_tree.controldir.sprout('other').open_workingtree()
403
self.build_tree_contents([('other/file', b"a\nb\n")])
404
other_tree.commit('rev2b', rev_id=b'rev2b')
405
self.build_tree_contents([('other/file', b"a\nb\nc\n")])
406
other_tree.commit('rev3b', rev_id=b'rev3b')
373
407
this_tree.lock_write()
374
408
self.addCleanup(this_tree.unlock)
376
merger = _mod_merge.Merger.from_revision_ids(None,
377
this_tree, 'rev3b', 'rev2b', other_tree.branch)
410
merger = _mod_merge.Merger.from_revision_ids(
411
this_tree, b'rev3b', b'rev2b', other_tree.branch)
378
412
merger.merge_type = _mod_merge.Merge3Merger
379
413
merger.do_merge()
380
self.assertFileEqual('a\n'
384
'>>>>>>> MERGE-SOURCE\n',
414
self.assertFileEqual(b'a\n'
418
b'>>>>>>> MERGE-SOURCE\n',
421
def test_merge_reverse_revision_range(self):
422
tree = self.make_branch_and_tree(".")
424
self.addCleanup(tree.unlock)
425
self.build_tree(['a'])
427
first_rev = tree.commit("added a")
428
merger = _mod_merge.Merger.from_revision_ids(tree,
429
_mod_revision.NULL_REVISION,
431
merger.merge_type = _mod_merge.Merge3Merger
432
merger.interesting_files = 'a'
433
conflict_count = merger.do_merge()
434
self.assertEqual(0, conflict_count)
436
self.assertPathDoesNotExist("a")
438
self.assertPathExists("a")
387
440
def test_make_merger(self):
388
441
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')
442
this_tree.commit('rev1', rev_id=b'rev1')
443
other_tree = this_tree.controldir.sprout('other').open_workingtree()
444
this_tree.commit('rev2', rev_id=b'rev2a')
445
other_tree.commit('rev2', rev_id=b'rev2b')
393
446
this_tree.lock_write()
394
447
self.addCleanup(this_tree.unlock)
395
merger = _mod_merge.Merger.from_revision_ids(None,
396
this_tree, 'rev2b', other_branch=other_tree.branch)
448
merger = _mod_merge.Merger.from_revision_ids(
449
this_tree, b'rev2b', other_branch=other_tree.branch)
397
450
merger.merge_type = _mod_merge.Merge3Merger
398
451
tree_merger = merger.make_merger()
399
452
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())
453
self.assertEqual(b'rev2b',
454
tree_merger.other_tree.get_revision_id())
455
self.assertEqual(b'rev1',
456
tree_merger.base_tree.get_revision_id())
457
self.assertEqual(other_tree.branch, tree_merger.other_branch)
403
459
def test_make_preview_transform(self):
404
460
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')
461
self.build_tree_contents([('this/file', b'1\n')])
462
this_tree.add('file', b'file-id')
463
this_tree.commit('rev1', rev_id=b'rev1')
464
other_tree = this_tree.controldir.sprout('other').open_workingtree()
465
self.build_tree_contents([('this/file', b'1\n2a\n')])
466
this_tree.commit('rev2', rev_id=b'rev2a')
467
self.build_tree_contents([('other/file', b'2b\n1\n')])
468
other_tree.commit('rev2', rev_id=b'rev2b')
413
469
this_tree.lock_write()
414
470
self.addCleanup(this_tree.unlock)
415
merger = _mod_merge.Merger.from_revision_ids(None,
416
this_tree, 'rev2b', other_branch=other_tree.branch)
471
merger = _mod_merge.Merger.from_revision_ids(
472
this_tree, b'rev2b', other_branch=other_tree.branch)
417
473
merger.merge_type = _mod_merge.Merge3Merger
418
474
tree_merger = merger.make_merger()
419
475
tt = tree_merger.make_preview_transform()
420
476
self.addCleanup(tt.finalize)
421
477
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())
478
with this_tree.get_file('file') as tree_file:
479
self.assertEqual(b'1\n2a\n', tree_file.read())
480
with preview_tree.get_file('file') as preview_file:
481
self.assertEqual(b'2b\n1\n2a\n', preview_file.read())
433
483
def test_do_merge(self):
434
484
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')
485
self.build_tree_contents([('this/file', b'1\n')])
486
this_tree.add('file', b'file-id')
487
this_tree.commit('rev1', rev_id=b'rev1')
488
other_tree = this_tree.controldir.sprout('other').open_workingtree()
489
self.build_tree_contents([('this/file', b'1\n2a\n')])
490
this_tree.commit('rev2', rev_id=b'rev2a')
491
self.build_tree_contents([('other/file', b'2b\n1\n')])
492
other_tree.commit('rev2', rev_id=b'rev2b')
443
493
this_tree.lock_write()
444
494
self.addCleanup(this_tree.unlock)
445
merger = _mod_merge.Merger.from_revision_ids(None,
446
this_tree, 'rev2b', other_branch=other_tree.branch)
495
merger = _mod_merge.Merger.from_revision_ids(
496
this_tree, b'rev2b', other_branch=other_tree.branch)
447
497
merger.merge_type = _mod_merge.Merge3Merger
448
498
tree_merger = merger.make_merger()
449
499
tt = tree_merger.do_merge()
450
tree_file = this_tree.get_file('file-id')
452
self.assertEqual('2b\n1\n2a\n', tree_file.read())
500
with this_tree.get_file('file') as tree_file:
501
self.assertEqual(b'2b\n1\n2a\n', tree_file.read())
503
def test_merge_require_tree_root(self):
504
tree = self.make_branch_and_tree(".")
506
self.addCleanup(tree.unlock)
507
self.build_tree(['a'])
509
first_rev = tree.commit("added a")
510
old_root_id = tree.get_root_id()
511
merger = _mod_merge.Merger.from_revision_ids(tree,
512
_mod_revision.NULL_REVISION,
514
merger.merge_type = _mod_merge.Merge3Merger
515
conflict_count = merger.do_merge()
516
self.assertEqual(0, conflict_count)
517
self.assertEqual({''}, set(tree.all_versioned_paths()))
518
tree.set_parent_ids([])
456
520
def test_merge_add_into_deleted_root(self):
457
521
# Yes, people actually do this. And report bugs if it breaks.
458
522
source = self.make_branch_and_tree('source', format='rich-root-pack')
459
523
self.build_tree(['source/foo/'])
460
source.add('foo', 'foo-id')
524
source.add('foo', b'foo-id')
461
525
source.commit('Add foo')
462
target = source.bzrdir.sprout('target').open_workingtree()
463
subtree = target.extract('foo-id')
526
target = source.controldir.sprout('target').open_workingtree()
527
subtree = target.extract('foo', b'foo-id')
464
528
subtree.commit('Delete root')
465
529
self.build_tree(['source/bar'])
466
source.add('bar', 'bar-id')
530
source.add('bar', b'bar-id')
467
531
source.commit('Add bar')
468
532
subtree.merge_from_branch(source.branch)
508
572
[c+'\n' for c in text])
510
574
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',))
575
self.add_rev(b'root', b'A', [], b'abc')
576
self.add_rev(b'root', b'B', [b'A'], b'acehg')
577
self.add_rev(b'root', b'C', [b'A'], b'fabg')
578
return _PlanMerge(b'B', b'C', self.plan_merge_vf, (b'root',))
516
580
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',))
581
self.add_version((b'root', b'A'), [], b'abc')
582
self.add_uncommitted_version((b'root', b'B:'), [(b'root', b'A')], b'acehg')
583
self.add_uncommitted_version((b'root', b'C:'), [(b'root', b'A')], b'fabg')
584
return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
522
586
def test_base_from_plan(self):
523
587
self.setup_plan_merge()
524
plan = self.plan_merge_vf.plan_merge('B', 'C')
588
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
525
589
pwm = versionedfile.PlanWeaveMerge(plan)
526
self.assertEqual(['a\n', 'b\n', 'c\n'], pwm.base_from_plan())
590
self.assertEqual([b'a\n', b'b\n', b'c\n'], pwm.base_from_plan())
528
592
def test_unique_lines(self):
529
593
plan = self.setup_plan_merge()
530
594
self.assertEqual(plan._unique_lines(
531
plan._get_matching_blocks('B', 'C')),
595
plan._get_matching_blocks(b'B', b'C')),
532
596
([1, 2, 3], [0, 2]))
534
598
def test_plan_merge(self):
535
599
self.setup_plan_merge()
536
plan = self.plan_merge_vf.plan_merge('B', 'C')
600
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
537
601
self.assertEqual([
539
('unchanged', 'a\n'),
603
('unchanged', b'a\n'),
604
('killed-a', b'b\n'),
605
('killed-b', b'c\n'),
548
612
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',))
613
self.add_rev(b'root', b'A', [], b'abc')
614
self.add_rev(b'root', b'B', [b'A'], b'abcde')
615
self.add_rev(b'root', b'C', [b'A'], b'abcefg')
616
self.add_rev(b'root', b'D', [b'A', b'B', b'C'], b'abcdegh')
617
my_plan = _PlanMerge(b'B', b'D', self.plan_merge_vf, (b'root',))
554
618
# We shortcut when one text supersedes the other in the per-file graph.
555
619
# We don't actually need to compare the texts at this point.
556
620
self.assertEqual([
564
628
list(my_plan.plan_merge()))
566
630
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',))
631
self.add_rev(b'root', b'A', [], b'abc')
632
self.add_rev(b'root', b'B', [], b'xyz')
633
my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
570
634
self.assertEqual([
577
641
list(my_plan.plan_merge()))
579
643
def test_plan_merge_tail_ancestors(self):
596
660
# are already present in E
598
662
# Introduce the base text
599
self.add_rev('root', 'A', [], 'abc')
663
self.add_rev(b'root', b'A', [], b'abc')
600
664
# Introduces a new line B
601
self.add_rev('root', 'B', ['A'], 'aBbc')
665
self.add_rev(b'root', b'B', [b'A'], b'aBbc')
602
666
# Introduces a new line C
603
self.add_rev('root', 'C', ['A'], 'abCc')
667
self.add_rev(b'root', b'C', [b'A'], b'abCc')
604
668
# Introduce new line D
605
self.add_rev('root', 'D', ['B'], 'DaBbc')
669
self.add_rev(b'root', b'D', [b'B'], b'DaBbc')
606
670
# Merges B and C by just incorporating both
607
self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
671
self.add_rev(b'root', b'E', [b'B', b'C'], b'aBbCc')
608
672
# Introduce new line F
609
self.add_rev('root', 'F', ['C'], 'abCcF')
673
self.add_rev(b'root', b'F', [b'C'], b'abCcF')
610
674
# Merge D & E by just combining the texts
611
self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
675
self.add_rev(b'root', b'G', [b'D', b'E'], b'DaBbCc')
612
676
# Merge F & E by just combining the texts
613
self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
677
self.add_rev(b'root', b'H', [b'F', b'E'], b'aBbCcF')
614
678
# Merge G & H by just combining texts
615
self.add_rev('root', 'I', ['G', 'H'], 'DaBbCcF')
679
self.add_rev(b'root', b'I', [b'G', b'H'], b'DaBbCcF')
616
680
# Merge G & H but supersede an old line in B
617
self.add_rev('root', 'J', ['H', 'G'], 'DaJbCcF')
618
plan = self.plan_merge_vf.plan_merge('I', 'J')
681
self.add_rev(b'root', b'J', [b'H', b'G'], b'DaJbCcF')
682
plan = self.plan_merge_vf.plan_merge(b'I', b'J')
619
683
self.assertEqual([
620
('unchanged', 'D\n'),
621
('unchanged', 'a\n'),
624
('unchanged', 'b\n'),
625
('unchanged', 'C\n'),
626
('unchanged', 'c\n'),
627
('unchanged', 'F\n')],
684
('unchanged', b'D\n'),
685
('unchanged', b'a\n'),
686
('killed-b', b'B\n'),
688
('unchanged', b'b\n'),
689
('unchanged', b'C\n'),
690
('unchanged', b'c\n'),
691
('unchanged', b'F\n')],
630
694
def test_plan_merge_tail_triple_ancestors(self):
645
709
# a third LCA that doesn't add new lines, but will trigger our more
646
710
# involved ancestry logic
648
self.add_rev('root', 'A', [], 'abc')
649
self.add_rev('root', 'B', ['A'], 'aBbc')
650
self.add_rev('root', 'C', ['A'], 'abCc')
651
self.add_rev('root', 'D', ['B'], 'DaBbc')
652
self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
653
self.add_rev('root', 'F', ['C'], 'abCcF')
654
self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
655
self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
656
self.add_rev('root', 'Q', ['E'], 'aBbCc')
657
self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DaBbCcF')
712
self.add_rev(b'root', b'A', [], b'abc')
713
self.add_rev(b'root', b'B', [b'A'], b'aBbc')
714
self.add_rev(b'root', b'C', [b'A'], b'abCc')
715
self.add_rev(b'root', b'D', [b'B'], b'DaBbc')
716
self.add_rev(b'root', b'E', [b'B', b'C'], b'aBbCc')
717
self.add_rev(b'root', b'F', [b'C'], 'abCcF')
718
self.add_rev(b'root', b'G', [b'D', b'E'], b'DaBbCc')
719
self.add_rev(b'root', b'H', [b'F', b'E'], b'aBbCcF')
720
self.add_rev(b'root', b'Q', [b'E'], 'aBbCc')
721
self.add_rev(b'root', b'I', [b'G', b'Q', b'H'], b'DaBbCcF')
658
722
# Merge G & H but supersede an old line in B
659
self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
660
plan = self.plan_merge_vf.plan_merge('I', 'J')
723
self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DaJbCcF')
724
plan = self.plan_merge_vf.plan_merge(b'I', b'J')
661
725
self.assertEqual([
662
('unchanged', 'D\n'),
663
('unchanged', 'a\n'),
666
('unchanged', 'b\n'),
667
('unchanged', 'C\n'),
668
('unchanged', 'c\n'),
669
('unchanged', 'F\n')],
726
('unchanged', b'D\n'),
727
('unchanged', b'a\n'),
728
('killed-b', b'B\n'),
730
('unchanged', b'b\n'),
731
('unchanged', b'C\n'),
732
('unchanged', b'c\n'),
733
('unchanged', b'F\n')],
672
736
def test_plan_merge_2_tail_triple_ancestors(self):
685
749
# This is meant to test after hitting a 3-way LCA, and multiple tail
686
750
# ancestors (only have NULL_REVISION in common)
688
self.add_rev('root', 'A', [], 'abc')
689
self.add_rev('root', 'B', [], 'def')
690
self.add_rev('root', 'D', ['A'], 'Dabc')
691
self.add_rev('root', 'E', ['A', 'B'], 'abcdef')
692
self.add_rev('root', 'F', ['B'], 'defF')
693
self.add_rev('root', 'G', ['D', 'E'], 'Dabcdef')
694
self.add_rev('root', 'H', ['F', 'E'], 'abcdefF')
695
self.add_rev('root', 'Q', ['E'], 'abcdef')
696
self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DabcdefF')
752
self.add_rev(b'root', b'A', [], b'abc')
753
self.add_rev(b'root', b'B', [], b'def')
754
self.add_rev(b'root', b'D', [b'A'], b'Dabc')
755
self.add_rev(b'root', b'E', [b'A', b'B'], b'abcdef')
756
self.add_rev(b'root', b'F', [b'B'], b'defF')
757
self.add_rev(b'root', b'G', [b'D', b'E'], b'Dabcdef')
758
self.add_rev(b'root', b'H', [b'F', b'E'], b'abcdefF')
759
self.add_rev(b'root', b'Q', [b'E'], b'abcdef')
760
self.add_rev(b'root', b'I', [b'G', b'Q', b'H'], b'DabcdefF')
697
761
# Merge G & H but supersede an old line in B
698
self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DabcdJfF')
699
plan = self.plan_merge_vf.plan_merge('I', 'J')
762
self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DabcdJfF')
763
plan = self.plan_merge_vf.plan_merge(b'I', b'J')
700
764
self.assertEqual([
701
('unchanged', 'D\n'),
702
('unchanged', 'a\n'),
703
('unchanged', 'b\n'),
704
('unchanged', 'c\n'),
705
('unchanged', 'd\n'),
708
('unchanged', 'f\n'),
709
('unchanged', 'F\n')],
765
('unchanged', b'D\n'),
766
('unchanged', b'a\n'),
767
('unchanged', b'b\n'),
768
('unchanged', b'c\n'),
769
('unchanged', b'd\n'),
770
('killed-b', b'e\n'),
772
('unchanged', b'f\n'),
773
('unchanged', b'F\n')],
712
776
def test_plan_merge_uncommitted_files(self):
713
777
self.setup_plan_merge_uncommitted()
714
plan = self.plan_merge_vf.plan_merge('B:', 'C:')
778
plan = self.plan_merge_vf.plan_merge(b'B:', b'C:')
715
779
self.assertEqual([
717
('unchanged', 'a\n'),
781
('unchanged', b'a\n'),
782
('killed-a', b'b\n'),
783
('killed-b', b'c\n'),
726
790
def test_plan_merge_insert_order(self):
804
868
# 'foo', it should appear as superseding the value in F (since it
805
869
# came from B), rather than conflict because of the resolution during
807
self.add_rev('root', 'XX', [], 'qrs')
808
self.add_rev('root', 'A', ['XX'], 'abcdef')
809
self.add_rev('root', 'B', ['A'], 'axcdef')
810
self.add_rev('root', 'C', ['B'], 'axcdefg')
811
self.add_rev('root', 'D', ['B'], 'haxcdef')
812
self.add_rev('root', 'E', ['A'], 'abcdyf')
871
self.add_rev(b'root', b'XX', [], b'qrs')
872
self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
873
self.add_rev(b'root', b'B', [b'A'], b'axcdef')
874
self.add_rev(b'root', b'C', [b'B'], b'axcdefg')
875
self.add_rev(b'root', b'D', [b'B'], b'haxcdef')
876
self.add_rev(b'root', b'E', [b'A'], b'abcdyf')
813
877
# Simple combining of all texts
814
self.add_rev('root', 'F', ['C', 'D', 'E'], 'haxcdyfg')
878
self.add_rev(b'root', b'F', [b'C', b'D', b'E'], b'haxcdyfg')
815
879
# combine and supersede 'x'
816
self.add_rev('root', 'G', ['C', 'D', 'E'], 'hazcdyfg')
817
plan = self.plan_merge_vf.plan_merge('F', 'G')
880
self.add_rev(b'root', b'G', [b'C', b'D', b'E'], b'hazcdyfg')
881
plan = self.plan_merge_vf.plan_merge(b'F', b'G')
818
882
self.assertEqual([
819
('unchanged', 'h\n'),
820
('unchanged', 'a\n'),
821
('killed-base', 'b\n'),
824
('unchanged', 'c\n'),
825
('unchanged', 'd\n'),
826
('killed-base', 'e\n'),
827
('unchanged', 'y\n'),
828
('unchanged', 'f\n'),
829
('unchanged', 'g\n')],
883
('unchanged', b'h\n'),
884
('unchanged', b'a\n'),
885
('killed-base', b'b\n'),
886
('killed-b', b'x\n'),
888
('unchanged', b'c\n'),
889
('unchanged', b'd\n'),
890
('killed-base', b'e\n'),
891
('unchanged', b'y\n'),
892
('unchanged', b'f\n'),
893
('unchanged', b'g\n')],
831
plan = self.plan_merge_vf.plan_lca_merge('F', 'G')
895
plan = self.plan_merge_vf.plan_lca_merge(b'F', b'G')
832
896
# This is one of the main differences between plan_merge and
833
897
# plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
834
898
# because 'x' was not present in one of the bases. However, in this
835
899
# case it is spurious because 'x' does not exist in the global base A.
836
900
self.assertEqual([
837
('unchanged', 'h\n'),
838
('unchanged', 'a\n'),
839
('conflicted-a', 'x\n'),
841
('unchanged', 'c\n'),
842
('unchanged', 'd\n'),
843
('unchanged', 'y\n'),
844
('unchanged', 'f\n'),
845
('unchanged', 'g\n')],
901
('unchanged', b'h\n'),
902
('unchanged', b'a\n'),
903
('conflicted-a', b'x\n'),
905
('unchanged', b'c\n'),
906
('unchanged', b'd\n'),
907
('unchanged', b'y\n'),
908
('unchanged', b'f\n'),
909
('unchanged', b'g\n')],
848
912
def test_criss_cross_flip_flop(self):
851
915
# XX unused ancestor, should not show up in the weave
855
919
# B C B & C both introduce a new line
859
923
# D E B & C are both merged, so both are common ancestors
860
924
# In the process of merging, both sides order the new
861
925
# 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'))
927
self.add_rev(b'root', b'XX', [], b'qrs')
928
self.add_rev(b'root', b'A', [b'XX'], b'abcdef')
929
self.add_rev(b'root', b'B', [b'A'], b'abcdgef')
930
self.add_rev(b'root', b'C', [b'A'], b'abcdhef')
931
self.add_rev(b'root', b'D', [b'B', b'C'], b'abcdghef')
932
self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
933
plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
870
934
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'),
935
('unchanged', b'a\n'),
936
('unchanged', b'b\n'),
937
('unchanged', b'c\n'),
938
('unchanged', b'd\n'),
940
('unchanged', b'g\n'),
941
('killed-b', b'h\n'),
942
('unchanged', b'e\n'),
943
('unchanged', b'f\n'),
881
945
pwm = versionedfile.PlanWeaveMerge(plan)
882
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
883
''.join(pwm.base_from_plan()))
946
self.assertEqualDiff(b'\n'.join(b'abcdghef') + b'\n',
947
b''.join(pwm.base_from_plan()))
884
948
# Reversing the order reverses the merge plan, and final order of 'hg'
886
plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
950
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
887
951
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'),
952
('unchanged', b'a\n'),
953
('unchanged', b'b\n'),
954
('unchanged', b'c\n'),
955
('unchanged', b'd\n'),
957
('unchanged', b'h\n'),
958
('killed-b', b'g\n'),
959
('unchanged', b'e\n'),
960
('unchanged', b'f\n'),
898
962
pwm = versionedfile.PlanWeaveMerge(plan)
899
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
900
''.join(pwm.base_from_plan()))
963
self.assertEqualDiff(b'\n'.join(b'abcdhgef') + b'\n',
964
b''.join(pwm.base_from_plan()))
901
965
# This is where lca differs, in that it (fairly correctly) determines
902
966
# that there is a conflict because both sides resolved the merge
904
plan = list(self.plan_merge_vf.plan_lca_merge('D', 'E'))
968
plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
905
969
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'),
970
('unchanged', b'a\n'),
971
('unchanged', b'b\n'),
972
('unchanged', b'c\n'),
973
('unchanged', b'd\n'),
974
('conflicted-b', b'h\n'),
975
('unchanged', b'g\n'),
976
('conflicted-a', b'h\n'),
977
('unchanged', b'e\n'),
978
('unchanged', b'f\n'),
916
980
pwm = versionedfile.PlanWeaveMerge(plan)
917
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
918
''.join(pwm.base_from_plan()))
981
self.assertEqualDiff(b'\n'.join(b'abcdgef') + b'\n',
982
b''.join(pwm.base_from_plan()))
919
983
# Reversing it changes what line is doubled, but still gives a
920
984
# double-conflict
921
plan = list(self.plan_merge_vf.plan_lca_merge('E', 'D'))
985
plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
922
986
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'),
987
('unchanged', b'a\n'),
988
('unchanged', b'b\n'),
989
('unchanged', b'c\n'),
990
('unchanged', b'd\n'),
991
('conflicted-b', b'g\n'),
992
('unchanged', b'h\n'),
993
('conflicted-a', b'g\n'),
994
('unchanged', b'e\n'),
995
('unchanged', b'f\n'),
933
997
pwm = versionedfile.PlanWeaveMerge(plan)
934
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
935
''.join(pwm.base_from_plan()))
998
self.assertEqualDiff(b'\n'.join(b'abcdhef') + b'\n',
999
b''.join(pwm.base_from_plan()))
937
1001
def assertRemoveExternalReferences(self, filtered_parent_map,
938
1002
child_map, tails, parent_map):
1008
1072
def test_subtract_plans(self):
1010
('unchanged', 'a\n'),
1012
('killed-a', 'c\n'),
1015
('killed-b', 'f\n'),
1016
('killed-b', 'g\n'),
1074
('unchanged', b'a\n'),
1076
('killed-a', b'c\n'),
1079
('killed-b', b'f\n'),
1080
('killed-b', b'g\n'),
1019
('unchanged', 'a\n'),
1021
('killed-a', 'c\n'),
1024
('killed-b', 'f\n'),
1025
('killed-b', 'i\n'),
1083
('unchanged', b'a\n'),
1085
('killed-a', b'c\n'),
1088
('killed-b', b'f\n'),
1089
('killed-b', b'i\n'),
1027
1091
subtracted_plan = [
1028
('unchanged', 'a\n'),
1030
('killed-a', 'c\n'),
1032
('unchanged', 'f\n'),
1033
('killed-b', 'i\n'),
1092
('unchanged', b'a\n'),
1094
('killed-a', b'c\n'),
1096
('unchanged', b'f\n'),
1097
('killed-b', b'i\n'),
1035
1099
self.assertEqual(subtracted_plan,
1036
1100
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1038
1102
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')
1103
self.add_rev(b'root', b'COMMON', [], b'abc')
1104
self.add_rev(b'root', b'THIS', [b'COMMON'], b'abcd')
1105
self.add_rev(b'root', b'BASE', [b'COMMON'], b'eabc')
1106
self.add_rev(b'root', b'OTHER', [b'BASE'], b'eafb')
1044
1108
def test_plan_merge_with_base(self):
1045
1109
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'),
1110
plan = self.plan_merge_vf.plan_merge(b'THIS', b'OTHER', b'BASE')
1111
self.assertEqual([('unchanged', b'a\n'),
1113
('unchanged', b'b\n'),
1114
('killed-b', b'c\n'),
1054
1118
def test_plan_lca_merge(self):
1055
1119
self.setup_plan_merge()
1056
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1120
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1057
1121
self.assertEqual([
1059
('unchanged', 'a\n'),
1060
('killed-b', 'c\n'),
1063
('killed-a', 'b\n'),
1064
('unchanged', 'g\n')],
1123
('unchanged', b'a\n'),
1124
('killed-b', b'c\n'),
1127
('killed-a', b'b\n'),
1128
('unchanged', b'g\n')],
1067
1131
def test_plan_lca_merge_uncommitted_files(self):
1068
1132
self.setup_plan_merge_uncommitted()
1069
plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1133
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1070
1134
self.assertEqual([
1072
('unchanged', 'a\n'),
1073
('killed-b', 'c\n'),
1076
('killed-a', 'b\n'),
1077
('unchanged', 'g\n')],
1136
('unchanged', b'a\n'),
1137
('killed-b', b'c\n'),
1140
('killed-a', b'b\n'),
1141
('unchanged', b'g\n')],
1080
1144
def test_plan_lca_merge_with_base(self):
1081
1145
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'),
1146
plan = self.plan_merge_vf.plan_lca_merge(b'THIS', b'OTHER', b'BASE')
1147
self.assertEqual([('unchanged', b'a\n'),
1149
('unchanged', b'b\n'),
1150
('killed-b', b'c\n'),
1090
1154
def test_plan_lca_merge_with_criss_cross(self):
1091
self.add_version(('root', 'ROOT'), [], 'abc')
1155
self.add_version((b'root', b'ROOT'), [], b'abc')
1092
1156
# each side makes a change
1093
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1094
self.add_version(('root', 'REV2'), [('root', 'ROOT')], 'abce')
1157
self.add_version((b'root', b'REV1'), [(b'root', b'ROOT')], b'abcd')
1158
self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
1095
1159
# 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'),
1160
self.add_version((b'root', b'LCA1'),
1161
[(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1162
self.add_version((b'root', b'LCA2'),
1163
[(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1164
plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1165
self.assertEqual([('new-b', b'f\n'),
1166
('unchanged', b'a\n'),
1167
('unchanged', b'b\n'),
1168
('unchanged', b'c\n'),
1169
('conflicted-a', b'd\n'),
1170
('conflicted-b', b'e\n'),
1109
1173
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'),
1174
self.add_version((b'root', b'A'), [], b'ab')
1175
self.add_version((b'root', b'B'), [], b'bc')
1176
plan = self.plan_merge_vf.plan_lca_merge(b'A', b'B')
1177
self.assertEqual([('new-a', b'a\n'),
1178
('unchanged', b'b\n'),
1118
1182
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'),
1183
self.add_rev(b'root', b'C', [], b'a')
1184
self.add_rev(b'root', b'A', [b'C'], b'b')
1185
self.add_rev(b'root', b'B', [b'C'], b'')
1186
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1187
self.assertEqual([('killed-both', b'a\n'),
1127
1191
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'),
1192
self.add_rev(b'root', b'C', [], b'abcd')
1193
self.add_rev(b'root', b'A', [b'C'], b'acbd')
1194
self.add_rev(b'root', b'B', [b'C'], b'aBcd')
1195
plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1196
self.assertEqual([('unchanged', b'a\n'),
1198
('killed-b', b'b\n'),
1200
('killed-a', b'c\n'),
1201
('unchanged', b'd\n'),
1210
1272
class TestMergerInMemory(TestMergerBase):
1212
1274
def test_cache_trees_with_revision_ids_None(self):
1213
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1275
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1214
1276
original_cache = dict(merger._cached_trees)
1215
1277
merger.cache_trees_with_revision_ids([None])
1216
1278
self.assertEqual(original_cache, merger._cached_trees)
1218
1280
def test_cache_trees_with_revision_ids_no_revision_id(self):
1219
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1281
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1220
1282
original_cache = dict(merger._cached_trees)
1221
1283
tree = self.make_branch_and_memory_tree('tree')
1222
1284
merger.cache_trees_with_revision_ids([tree])
1223
1285
self.assertEqual(original_cache, merger._cached_trees)
1225
1287
def test_cache_trees_with_revision_ids_having_revision_id(self):
1226
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1288
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1227
1289
original_cache = dict(merger._cached_trees)
1228
tree = merger.this_branch.repository.revision_tree('B-id')
1229
original_cache['B-id'] = tree
1290
tree = merger.this_branch.repository.revision_tree(b'B-id')
1291
original_cache[b'B-id'] = tree
1230
1292
merger.cache_trees_with_revision_ids([tree])
1231
1293
self.assertEqual(original_cache, merger._cached_trees)
1233
1295
def test_find_base(self):
1234
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1235
self.assertEqual('A-id', merger.base_rev_id)
1296
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1297
self.assertEqual(b'A-id', merger.base_rev_id)
1236
1298
self.assertFalse(merger._is_criss_cross)
1237
1299
self.assertIs(None, merger._lca_trees)
1239
1301
def test_find_base_criss_cross(self):
1240
1302
builder = self.setup_criss_cross_graph()
1241
merger = self.make_Merger(builder, 'E-id')
1242
self.assertEqual('A-id', merger.base_rev_id)
1303
merger = self.make_Merger(builder, b'E-id')
1304
self.assertEqual(b'A-id', merger.base_rev_id)
1243
1305
self.assertTrue(merger._is_criss_cross)
1244
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1306
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1245
1307
for t in merger._lca_trees])
1246
1308
# If we swap the order, we should get a different lca order
1247
builder.build_snapshot('F-id', ['E-id'], [])
1248
merger = self.make_Merger(builder, 'D-id')
1249
self.assertEqual(['C-id', 'B-id'], [t.get_revision_id()
1309
builder.build_snapshot([b'E-id'], [], revision_id=b'F-id')
1310
merger = self.make_Merger(builder, b'D-id')
1311
self.assertEqual([b'C-id', b'B-id'], [t.get_revision_id()
1250
1312
for t in merger._lca_trees])
1252
1314
def test_find_base_triple_criss_cross(self):
1262
1324
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'],
1325
builder.build_snapshot([b'A-id'], [], revision_id=b'F-id')
1326
builder.build_snapshot([b'E-id', b'F-id'], [], revision_id=b'H-id')
1327
builder.build_snapshot([b'D-id', b'F-id'], [], revision_id=b'G-id')
1328
merger = self.make_Merger(builder, b'H-id')
1329
self.assertEqual([b'B-id', b'C-id', b'F-id'],
1268
1330
[t.get_revision_id() for t in merger._lca_trees])
1332
def test_find_base_new_root_criss_cross(self):
1338
builder = self.get_builder()
1339
builder.build_snapshot(None,
1340
[('add', ('', None, 'directory', None))],
1341
revision_id=b'A-id')
1342
builder.build_snapshot([],
1343
[('add', ('', None, 'directory', None))],
1344
revision_id=b'B-id')
1345
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'D-id')
1346
builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'C-id')
1347
merger = self.make_Merger(builder, b'D-id')
1348
self.assertEqual(b'A-id', merger.base_rev_id)
1349
self.assertTrue(merger._is_criss_cross)
1350
self.assertEqual([b'A-id', b'B-id'], [t.get_revision_id()
1351
for t in merger._lca_trees])
1270
1353
def test_no_criss_cross_passed_to_merge_type(self):
1271
1354
class LCATreesMerger(LoggingMerger):
1272
1355
supports_lca_trees = True
1274
merger = self.make_Merger(self.setup_simple_graph(), 'C-id')
1357
merger = self.make_Merger(self.setup_simple_graph(), b'C-id')
1275
1358
merger.merge_type = LCATreesMerger
1276
1359
merge_obj = merger.make_merger()
1277
1360
self.assertIsInstance(merge_obj, LCATreesMerger)
1278
1361
self.assertFalse('lca_trees' in merge_obj.kwargs)
1280
1363
def test_criss_cross_passed_to_merge_type(self):
1281
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1364
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1282
1365
merger.merge_type = _mod_merge.Merge3Merger
1283
1366
merge_obj = merger.make_merger()
1284
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1367
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1285
1368
for t in merger._lca_trees])
1287
1370
def test_criss_cross_not_supported_merge_type(self):
1288
merger = self.make_Merger(self.setup_criss_cross_graph(), 'E-id')
1371
merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1289
1372
# We explicitly do not define supports_lca_trees
1290
1373
merger.merge_type = LoggingMerger
1291
1374
merge_obj = merger.make_merger()
1306
1389
class TestMergerEntriesLCA(TestMergerBase):
1308
1391
def make_merge_obj(self, builder, other_revision_id,
1309
interesting_files=None, interesting_ids=None):
1392
interesting_files=None):
1310
1393
merger = self.make_Merger(builder, other_revision_id,
1311
interesting_files=interesting_files,
1312
interesting_ids=interesting_ids)
1394
interesting_files=interesting_files)
1313
1395
return merger.make_merger()
1315
1397
def test_simple(self):
1316
1398
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')
1399
builder.build_snapshot(None,
1400
[('add', (u'', b'a-root-id', 'directory', None)),
1401
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1402
revision_id=b'A-id')
1403
builder.build_snapshot([b'A-id'],
1404
[('modify', ('a', b'a\nb\nC\nc\n'))],
1405
revision_id=b'C-id')
1406
builder.build_snapshot([b'A-id'],
1407
[('modify', ('a', b'a\nB\nb\nc\n'))],
1408
revision_id=b'B-id')
1409
builder.build_snapshot([b'C-id', b'B-id'],
1410
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1411
revision_id=b'E-id')
1412
builder.build_snapshot([b'B-id', b'C-id'],
1413
[('modify', ('a', b'a\nB\nb\nC\nc\n'))],
1414
revision_id=b'D-id', )
1415
merge_obj = self.make_merge_obj(builder, b'E-id')
1330
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1417
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1331
1418
for t in merge_obj._lca_trees])
1332
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1419
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1333
1420
entries = list(merge_obj._entries_lca())
1335
1422
# (file_id, changed, parents, names, executable)
1336
1423
# BASE, lca1, lca2, OTHER, THIS
1337
root_id = 'a-root-id'
1338
self.assertEqual([('a-id', True,
1424
root_id = b'a-root-id'
1425
self.assertEqual([(b'a-id', True,
1426
((u'a', [u'a', u'a']), u'a', u'a'),
1339
1427
((root_id, [root_id, root_id]), root_id, root_id),
1340
1428
((u'a', [u'a', u'a']), u'a', u'a'),
1341
1429
((False, [False, False]), False, False)),
1354
1442
# G modifies 'bar'
1356
1444
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')
1445
builder.build_snapshot(None,
1446
[('add', (u'', b'a-root-id', 'directory', None))],
1447
revision_id=b'A-id')
1448
builder.build_snapshot([b'A-id'],
1449
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1450
revision_id=b'B-id')
1451
builder.build_snapshot([b'A-id'],
1452
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1453
revision_id=b'C-id')
1454
builder.build_snapshot([b'B-id', b'C-id'],
1455
[('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
1456
revision_id=b'D-id')
1457
builder.build_snapshot([b'C-id', b'B-id'],
1458
[('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
1459
revision_id=b'E-id')
1460
builder.build_snapshot([b'E-id', b'D-id'],
1461
[('modify', (u'bar', b'd\ne\nf\nG\n'))],
1462
revision_id=b'G-id')
1463
builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1464
merge_obj = self.make_merge_obj(builder, b'G-id')
1372
self.assertEqual(['D-id', 'E-id'], [t.get_revision_id()
1466
self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1373
1467
for t in merge_obj._lca_trees])
1374
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1468
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1375
1469
entries = list(merge_obj._entries_lca())
1376
root_id = 'a-root-id'
1377
self.assertEqual([('bar-id', True,
1470
root_id = b'a-root-id'
1471
self.assertEqual([(b'bar-id', True,
1472
((None, [u'bar', u'bar']), u'bar', u'bar'),
1378
1473
((None, [root_id, root_id]), root_id, root_id),
1379
1474
((None, [u'bar', u'bar']), u'bar', u'bar'),
1380
1475
((None, [False, False]), False, False)),
1383
1478
def test_not_in_this(self):
1384
1479
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')
1480
builder.build_snapshot(None,
1481
[('add', (u'', b'a-root-id', 'directory', None)),
1482
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1483
revision_id=b'A-id')
1484
builder.build_snapshot([b'A-id'],
1485
[('modify', ('a', b'a\nB\nb\nc\n'))],
1486
revision_id=b'B-id')
1487
builder.build_snapshot([b'A-id'],
1488
[('modify', ('a', b'a\nb\nC\nc\n'))],
1489
revision_id=b'C-id')
1490
builder.build_snapshot([b'C-id', b'B-id'],
1491
[('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
1492
revision_id=b'E-id')
1493
builder.build_snapshot([b'B-id', b'C-id'],
1494
[('unversion', 'a')],
1495
revision_id=b'D-id')
1496
merge_obj = self.make_merge_obj(builder, b'E-id')
1398
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1498
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1399
1499
for t in merge_obj._lca_trees])
1400
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1500
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1402
1502
entries = list(merge_obj._entries_lca())
1403
root_id = 'a-root-id'
1404
self.assertEqual([('a-id', True,
1503
root_id = b'a-root-id'
1504
self.assertEqual([(b'a-id', True,
1505
((u'a', [u'a', u'a']), u'a', None),
1405
1506
((root_id, [root_id, root_id]), root_id, None),
1406
1507
((u'a', [u'a', u'a']), u'a', None),
1407
1508
((False, [False, False]), False, None)),
1415
1516
# D E # D and E both have the file, unchanged from C
1416
1517
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')
1518
builder.build_snapshot(None,
1519
[('add', (u'', b'a-root-id', 'directory', None))],
1520
revision_id=b'A-id')
1521
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1522
builder.build_snapshot([b'A-id'],
1523
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1524
revision_id=b'C-id')
1525
builder.build_snapshot([b'C-id', b'B-id'],
1526
[], revision_id=b'E-id') # Inherited from C
1527
builder.build_snapshot([b'B-id', b'C-id'], # Merged from C
1528
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1529
revision_id=b'D-id')
1530
merge_obj = self.make_merge_obj(builder, b'E-id')
1427
self.assertEqual(['B-id', 'C-id'], [t.get_revision_id()
1532
self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1428
1533
for t in merge_obj._lca_trees])
1429
self.assertEqual('A-id', merge_obj.base_tree.get_revision_id())
1534
self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1431
1536
entries = list(merge_obj._entries_lca())
1432
1537
self.assertEqual([], entries)
1434
1539
def test_not_in_other(self):
1435
1540
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')
1541
builder.build_snapshot(None,
1542
[('add', (u'', b'a-root-id', 'directory', None)),
1543
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1544
revision_id=b'A-id')
1545
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1546
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1547
builder.build_snapshot(
1549
[('unversion', 'a')], revision_id=b'E-id')
1550
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1551
merge_obj = self.make_merge_obj(builder, b'E-id')
1446
1553
entries = list(merge_obj._entries_lca())
1447
root_id = 'a-root-id'
1448
self.assertEqual([('a-id', True,
1554
root_id = b'a-root-id'
1555
self.assertEqual([(b'a-id', True,
1556
((u'a', [u'a', u'a']), None, u'a'),
1449
1557
((root_id, [root_id, root_id]), None, root_id),
1450
1558
((u'a', [u'a', u'a']), None, u'a'),
1451
1559
((False, [False, False]), None, False)),
1493
1602
# In this case, we have a conflict of how the changes were resolved. E
1494
1603
# picked C and D picked B, so we should issue a conflict
1495
1604
builder = self.get_builder()
1496
builder.build_snapshot('A-id', None,
1497
[('add', (u'', 'a-root-id', 'directory', None)),
1498
('add', (u'foo', 'foo-id', 'file', 'content\n'))])
1499
builder.build_snapshot('B-id', ['A-id'], [
1500
('modify', ('foo-id', 'new-content\n'))])
1501
builder.build_snapshot('C-id', ['A-id'],
1502
[('unversion', 'foo-id')])
1503
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1504
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1505
merge_obj = self.make_merge_obj(builder, 'E-id')
1605
builder.build_snapshot(None,
1606
[('add', (u'', b'a-root-id', 'directory', None)),
1607
('add', (u'foo', b'foo-id', 'file', b'content\n'))],
1608
revision_id=b'A-id')
1609
builder.build_snapshot([b'A-id'], [
1610
('modify', ('foo', b'new-content\n'))],
1611
revision_id=b'B-id')
1612
builder.build_snapshot([b'A-id'],
1613
[('unversion', 'foo')],
1614
revision_id=b'C-id')
1615
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1616
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1617
merge_obj = self.make_merge_obj(builder, b'E-id')
1507
1619
entries = list(merge_obj._entries_lca())
1508
root_id = 'a-root-id'
1509
self.assertEqual([('foo-id', True,
1620
root_id = b'a-root-id'
1621
self.assertEqual([(b'foo-id', True,
1622
((u'foo', [u'foo', None]), None, u'foo'),
1510
1623
((root_id, [root_id, None]), None, root_id),
1511
1624
((u'foo', [u'foo', None]), None, 'foo'),
1512
1625
((False, [False, None]), None, False)),
1529
1642
# A => C, add file, thus C supersedes B
1530
1643
# w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1531
1644
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')
1645
builder.build_snapshot(None,
1646
[('add', (u'', b'a-root-id', 'directory', None))],
1647
revision_id=b'A-id')
1648
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1649
builder.build_snapshot([b'A-id'],
1650
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1651
revision_id=b'C-id')
1652
builder.build_snapshot([b'C-id', b'B-id'],
1653
[('unversion', 'a')],
1654
revision_id=b'E-id')
1655
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1656
merge_obj = self.make_merge_obj(builder, b'E-id')
1542
1658
entries = list(merge_obj._entries_lca())
1543
1659
self.assertEqual([], entries)
1545
1661
def test_only_in_other(self):
1546
1662
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')
1663
builder.build_snapshot(None,
1664
[('add', (u'', b'a-root-id', 'directory', None))],
1665
revision_id=b'A-id')
1666
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1667
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1668
builder.build_snapshot([b'C-id', b'B-id'],
1669
[('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
1670
revision_id=b'E-id')
1671
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1672
merge_obj = self.make_merge_obj(builder, b'E-id')
1556
1674
entries = list(merge_obj._entries_lca())
1557
root_id = 'a-root-id'
1558
self.assertEqual([('a-id', True,
1675
root_id = b'a-root-id'
1676
self.assertEqual([(b'a-id', True,
1677
((None, [None, None]), u'a', None),
1559
1678
((None, [None, None]), root_id, None),
1560
1679
((None, [None, None]), u'a', None),
1561
1680
((None, [None, None]), False, None)),
1576
1695
# though its LCAs disagree. This is because the modification in E
1577
1696
# completely supersedes the value in D.
1578
1697
builder = self.get_builder()
1579
builder.build_snapshot('A-id', None,
1580
[('add', (u'', 'a-root-id', 'directory', None)),
1581
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1582
builder.build_snapshot('C-id', ['A-id'], [])
1583
builder.build_snapshot('B-id', ['A-id'],
1584
[('modify', ('foo-id', 'B content\n'))])
1585
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1586
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1587
[('modify', ('foo-id', 'E content\n'))])
1588
builder.build_snapshot('G-id', ['E-id', 'D-id'], [])
1589
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1590
[('modify', ('foo-id', 'F content\n'))])
1591
merge_obj = self.make_merge_obj(builder, 'G-id')
1698
builder.build_snapshot(None,
1699
[('add', (u'', b'a-root-id', 'directory', None)),
1700
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1701
revision_id=b'A-id')
1702
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1703
builder.build_snapshot([b'A-id'],
1704
[('modify', ('foo', b'B content\n'))],
1705
revision_id=b'B-id')
1706
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1707
builder.build_snapshot([b'C-id', b'B-id'],
1708
[('modify', ('foo', b'E content\n'))],
1709
revision_id=b'E-id')
1710
builder.build_snapshot([b'E-id', b'D-id'], [], revision_id=b'G-id')
1711
builder.build_snapshot([b'D-id', b'E-id'],
1712
[('modify', ('foo', b'F content\n'))],
1713
revision_id=b'F-id')
1714
merge_obj = self.make_merge_obj(builder, b'G-id')
1593
1716
self.assertEqual([], list(merge_obj._entries_lca()))
1622
1745
# aren't supporting it yet.
1624
1747
builder = self.get_builder()
1625
builder.build_snapshot('A-id', None,
1626
[('add', (u'', 'a-root-id', 'directory', None)),
1627
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1628
builder.build_snapshot('C-id', ['A-id'], [])
1629
builder.build_snapshot('B-id', ['A-id'],
1630
[('rename', ('foo', 'bar'))])
1631
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1632
builder.build_snapshot('E-id', ['C-id', 'B-id'],
1633
[('rename', ('foo', 'bing'))]) # override to bing
1634
builder.build_snapshot('G-id', ['E-id', 'D-id'],
1635
[('rename', ('bing', 'barry'))]) # override to barry
1636
builder.build_snapshot('F-id', ['D-id', 'E-id'],
1637
[('rename', ('bar', 'bing'))]) # Merge in E's change
1638
merge_obj = self.make_merge_obj(builder, 'G-id')
1748
builder.build_snapshot(None,
1749
[('add', (u'', b'a-root-id', 'directory', None)),
1750
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1751
revision_id=b'A-id')
1752
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1753
builder.build_snapshot([b'A-id'],
1754
[('rename', ('foo', 'bar'))],
1755
revision_id=b'B-id')
1756
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1757
builder.build_snapshot([b'C-id', b'B-id'],
1758
[('rename', ('foo', 'bing'))],
1759
revision_id=b'E-id') # override to bing
1760
builder.build_snapshot([b'E-id', b'D-id'],
1761
[('rename', ('bing', 'barry'))],
1762
revision_id=b'G-id') # override to barry
1763
builder.build_snapshot([b'D-id', b'E-id'],
1764
[('rename', ('bar', 'bing'))],
1765
revision_id=b'F-id') # Merge in E's change
1766
merge_obj = self.make_merge_obj(builder, b'G-id')
1640
1768
self.expectFailure("We don't do an actual heads() check on lca values,"
1641
1769
" or use the per-attribute graph",
1655
1783
# be pruned from the LCAs, even though it was newly introduced by E
1656
1784
# (superseding B).
1657
1785
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')
1786
builder.build_snapshot(None,
1787
[('add', (u'', b'a-root-id', 'directory', None)),
1788
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1789
revision_id=b'A-id')
1790
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1791
builder.build_snapshot([b'A-id'],
1792
[('rename', ('foo', 'bar'))],
1793
revision_id=b'B-id')
1794
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1795
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1796
builder.build_snapshot([b'E-id', b'D-id'],
1797
[('rename', ('foo', 'bar'))],
1798
revision_id=b'G-id')
1799
builder.build_snapshot([b'D-id', b'E-id'],
1800
[('rename', ('bar', 'bing'))],
1801
revision_id=b'F-id') # should end up conflicting
1802
merge_obj = self.make_merge_obj(builder, b'G-id')
1672
1804
entries = list(merge_obj._entries_lca())
1673
root_id = 'a-root-id'
1805
root_id = b'a-root-id'
1674
1806
self.expectFailure("We prune values from BASE even when relevant.",
1675
1807
self.assertEqual,
1677
1809
((root_id, [root_id, root_id]), root_id, root_id),
1678
1810
((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1679
1811
((False, [False, False]), False, False)),
1688
1820
# D E D reverts to B, E reverts to C
1689
1821
# This should conflict
1690
1822
builder = self.get_builder()
1691
builder.build_snapshot('A-id', None,
1692
[('add', (u'', 'a-root-id', 'directory', None)),
1693
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1694
builder.build_snapshot('B-id', ['A-id'],
1695
[('modify', ('foo-id', 'B content\n'))])
1696
builder.build_snapshot('C-id', ['A-id'],
1697
[('modify', ('foo-id', 'C content\n'))])
1698
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1699
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1700
merge_obj = self.make_merge_obj(builder, 'E-id')
1823
builder.build_snapshot(None,
1824
[('add', (u'', b'a-root-id', 'directory', None)),
1825
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1826
revision_id=b'A-id')
1827
builder.build_snapshot([b'A-id'],
1828
[('modify', ('foo', b'B content\n'))],
1829
revision_id=b'B-id')
1830
builder.build_snapshot([b'A-id'],
1831
[('modify', ('foo', b'C content\n'))],
1832
revision_id=b'C-id')
1833
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1834
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1835
merge_obj = self.make_merge_obj(builder, b'E-id')
1702
1837
entries = list(merge_obj._entries_lca())
1703
root_id = 'a-root-id'
1704
self.assertEqual([('foo-id', True,
1838
root_id = b'a-root-id'
1839
self.assertEqual([(b'foo-id', True,
1840
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1705
1841
((root_id, [root_id, root_id]), root_id, root_id),
1706
1842
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1707
1843
((False, [False, False]), False, False)),
1719
1855
# We need to emit an entry for 'foo', because D & E differed on the
1720
1856
# merge resolution
1721
1857
builder = self.get_builder()
1722
builder.build_snapshot('A-id', None,
1723
[('add', (u'', 'a-root-id', 'directory', None)),
1724
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
1725
builder.build_snapshot('B-id', ['A-id'],
1726
[('modify', ('foo-id', 'B content\n'))])
1727
builder.build_snapshot('C-id', ['A-id'],
1728
[('modify', ('foo-id', 'C content\n'))])
1729
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1730
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
1731
builder.build_snapshot('F-id', ['D-id'],
1732
[('modify', ('foo-id', 'F content\n'))])
1733
merge_obj = self.make_merge_obj(builder, 'E-id')
1858
builder.build_snapshot(None,
1859
[('add', (u'', b'a-root-id', 'directory', None)),
1860
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1861
revision_id=b'A-id')
1862
builder.build_snapshot([b'A-id'],
1863
[('modify', ('foo', b'B content\n'))],
1864
revision_id=b'B-id')
1865
builder.build_snapshot([b'A-id'],
1866
[('modify', ('foo', b'C content\n'))],
1867
revision_id=b'C-id', )
1868
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1869
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1870
builder.build_snapshot([b'D-id'],
1871
[('modify', ('foo', b'F content\n'))],
1872
revision_id=b'F-id')
1873
merge_obj = self.make_merge_obj(builder, b'E-id')
1735
1875
entries = list(merge_obj._entries_lca())
1736
root_id = 'a-root-id'
1737
self.assertEqual([('foo-id', True,
1876
root_id = b'a-root-id'
1877
self.assertEqual([(b'foo-id', True,
1878
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1738
1879
((root_id, [root_id, root_id]), root_id, root_id),
1739
1880
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1740
1881
((False, [False, False]), False, False)),
1755
1896
# We need to conflict.
1757
1898
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')
1899
builder.build_snapshot(None,
1900
[('add', (u'', b'a-root-id', 'directory', None)),
1901
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
1902
revision_id=b'A-id')
1903
builder.build_snapshot([b'A-id'],
1904
[('modify', ('foo', b'B content\n'))],
1905
revision_id=b'B-id')
1906
builder.build_snapshot([b'A-id'],
1907
[('modify', ('foo', b'C content\n'))],
1908
revision_id=b'C-id')
1909
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1910
builder.build_snapshot([b'B-id', b'C-id'],
1911
[('modify', ('foo', b'C content\n'))],
1912
revision_id=b'D-id') # Same as E
1913
builder.build_snapshot([b'D-id'],
1914
[('modify', ('foo', b'F content\n'))],
1915
revision_id=b'F-id')
1916
merge_obj = self.make_merge_obj(builder, b'E-id')
1772
1918
entries = list(merge_obj._entries_lca())
1773
1919
self.expectFailure("We don't detect that LCA resolution was the"
1777
1923
def test_only_path_changed(self):
1778
1924
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')
1925
builder.build_snapshot(None,
1926
[('add', (u'', b'a-root-id', 'directory', None)),
1927
('add', (u'a', b'a-id', 'file', b'content\n'))],
1928
revision_id=b'A-id')
1929
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1930
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1931
builder.build_snapshot([b'C-id', b'B-id'],
1932
[('rename', (u'a', u'b'))],
1933
revision_id=b'E-id')
1934
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1935
merge_obj = self.make_merge_obj(builder, b'E-id')
1788
1936
entries = list(merge_obj._entries_lca())
1789
root_id = 'a-root-id'
1937
root_id = b'a-root-id'
1790
1938
# The content was not changed, only the path
1791
self.assertEqual([('a-id', False,
1939
self.assertEqual([(b'a-id', False,
1940
((u'a', [u'a', u'a']), u'b', u'a'),
1792
1941
((root_id, [root_id, root_id]), root_id, root_id),
1793
1942
((u'a', [u'a', u'a']), u'b', u'a'),
1794
1943
((False, [False, False]), False, False)),
1797
1946
def test_kind_changed(self):
1798
1947
# Identical content, except 'D' changes a-id into a directory
1799
1948
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')
1949
builder.build_snapshot(None,
1950
[('add', (u'', b'a-root-id', 'directory', None)),
1951
('add', (u'a', b'a-id', 'file', b'content\n'))],
1952
revision_id=b'A-id')
1953
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1954
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1955
builder.build_snapshot([b'C-id', b'B-id'],
1956
[('unversion', 'a'),
1958
('add', (u'a', b'a-id', 'directory', None))],
1959
revision_id=b'E-id')
1960
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1961
merge_obj = self.make_merge_obj(builder, b'E-id')
1810
1962
entries = list(merge_obj._entries_lca())
1811
root_id = 'a-root-id'
1963
root_id = b'a-root-id'
1812
1964
# Only the kind was changed (content)
1813
self.assertEqual([('a-id', True,
1965
self.assertEqual([(b'a-id', True,
1966
((u'a', [u'a', u'a']), u'a', u'a'),
1814
1967
((root_id, [root_id, root_id]), root_id, root_id),
1815
1968
((u'a', [u'a', u'a']), u'a', u'a'),
1816
1969
((False, [False, False]), False, False)),
1836
1992
def test_interesting_files(self):
1837
1993
# Two files modified, but we should filter one of them
1838
1994
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',
1995
builder.build_snapshot(None,
1996
[('add', (u'', b'a-root-id', 'directory', None)),
1997
('add', (u'a', b'a-id', 'file', b'content\n')),
1998
('add', (u'b', b'b-id', 'file', b'content\n'))],
1999
revision_id=b'A-id')
2000
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2001
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2002
builder.build_snapshot([b'C-id', b'B-id'],
2003
[('modify', ('a', b'new-content\n')),
2004
('modify', ('b', b'new-content\n'))],
2005
revision_id=b'E-id')
2006
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2007
merge_obj = self.make_merge_obj(builder, b'E-id',
1850
2008
interesting_files=['b'])
1851
2009
entries = list(merge_obj._entries_lca())
1852
root_id = 'a-root-id'
1853
self.assertEqual([('b-id', True,
2010
root_id = b'a-root-id'
2011
self.assertEqual([(b'b-id', True,
2012
((u'b', [u'b', u'b']), u'b', u'b'),
1854
2013
((root_id, [root_id, root_id]), root_id, root_id),
1855
2014
((u'b', [u'b', u'b']), u'b', u'b'),
1856
2015
((False, [False, False]), False, False)),
1859
2018
def test_interesting_file_in_this(self):
1860
2019
# This renamed the file, but it should still match the entry in other
1861
2020
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',
2021
builder.build_snapshot(None,
2022
[('add', (u'', b'a-root-id', 'directory', None)),
2023
('add', (u'a', b'a-id', 'file', b'content\n')),
2024
('add', (u'b', b'b-id', 'file', b'content\n'))],
2025
revision_id=b'A-id')
2026
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2027
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2028
builder.build_snapshot([b'C-id', b'B-id'],
2029
[('modify', ('a', b'new-content\n')),
2030
('modify', ('b', b'new-content\n'))],
2031
revision_id=b'E-id')
2032
builder.build_snapshot([b'B-id', b'C-id'],
2033
[('rename', ('b', 'c'))],
2034
revision_id=b'D-id')
2035
merge_obj = self.make_merge_obj(builder, b'E-id',
1874
2036
interesting_files=['c'])
1875
2037
entries = list(merge_obj._entries_lca())
1876
root_id = 'a-root-id'
1877
self.assertEqual([('b-id', True,
2038
root_id = b'a-root-id'
2039
self.assertEqual([(b'b-id', True,
2040
((u'b', [u'b', u'b']), u'b', u'c'),
1878
2041
((root_id, [root_id, root_id]), root_id, root_id),
1879
2042
((u'b', [u'b', u'b']), u'b', u'c'),
1880
2043
((False, [False, False]), False, False)),
1883
2046
def test_interesting_file_in_base(self):
1884
2047
# This renamed the file, but it should still match the entry in BASE
1885
2048
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',
2049
builder.build_snapshot(None,
2050
[('add', (u'', b'a-root-id', 'directory', None)),
2051
('add', (u'a', b'a-id', 'file', b'content\n')),
2052
('add', (u'c', b'c-id', 'file', b'content\n'))],
2053
revision_id=b'A-id')
2054
builder.build_snapshot([b'A-id'],
2055
[('rename', ('c', 'b'))],
2056
revision_id=b'B-id')
2057
builder.build_snapshot([b'A-id'],
2058
[('rename', ('c', 'b'))],
2059
revision_id=b'C-id')
2060
builder.build_snapshot([b'C-id', b'B-id'],
2061
[('modify', ('a', b'new-content\n')),
2062
('modify', ('b', b'new-content\n'))],
2063
revision_id=b'E-id')
2064
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2065
merge_obj = self.make_merge_obj(builder, b'E-id',
1899
2066
interesting_files=['c'])
1900
2067
entries = list(merge_obj._entries_lca())
1901
root_id = 'a-root-id'
1902
self.assertEqual([('c-id', True,
2068
root_id = b'a-root-id'
2069
self.assertEqual([(b'c-id', True,
2070
((u'c', [u'b', u'b']), u'b', u'b'),
1903
2071
((root_id, [root_id, root_id]), root_id, root_id),
1904
2072
((u'c', [u'b', u'b']), u'b', u'b'),
1905
2073
((False, [False, False]), False, False)),
1908
2076
def test_interesting_file_in_lca(self):
1909
2077
# This renamed the file, but it should still match the entry in LCA
1910
2078
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',
2079
builder.build_snapshot(None,
2080
[('add', (u'', b'a-root-id', 'directory', None)),
2081
('add', (u'a', b'a-id', 'file', b'content\n')),
2082
('add', (u'b', b'b-id', 'file', b'content\n'))],
2083
revision_id=b'A-id')
2084
builder.build_snapshot([b'A-id'],
2085
[('rename', ('b', 'c'))], revision_id=b'B-id')
2086
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2087
builder.build_snapshot([b'C-id', b'B-id'],
2088
[('modify', ('a', b'new-content\n')),
2089
('modify', ('b', b'new-content\n'))],
2090
revision_id=b'E-id')
2091
builder.build_snapshot([b'B-id', b'C-id'],
2092
[('rename', ('c', 'b'))], revision_id=b'D-id')
2093
merge_obj = self.make_merge_obj(builder, b'E-id',
1924
2094
interesting_files=['c'])
1925
2095
entries = list(merge_obj._entries_lca())
1926
root_id = 'a-root-id'
1927
self.assertEqual([('b-id', True,
2096
root_id = b'a-root-id'
2097
self.assertEqual([(b'b-id', True,
2098
((u'b', [u'c', u'b']), u'b', u'b'),
1928
2099
((root_id, [root_id, root_id]), root_id, root_id),
1929
2100
((u'b', [u'c', u'b']), u'b', u'b'),
1930
2101
((False, [False, False]), False, False)),
1933
def test_interesting_ids(self):
2104
def test_interesting_files(self):
1934
2105
# Two files modified, but we should filter one of them
1935
2106
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'])
2107
builder.build_snapshot(None,
2108
[('add', (u'', b'a-root-id', 'directory', None)),
2109
('add', (u'a', b'a-id', 'file', b'content\n')),
2110
('add', (u'b', b'b-id', 'file', b'content\n'))],
2111
revision_id=b'A-id')
2112
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2113
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2114
builder.build_snapshot([b'C-id', b'B-id'],
2115
[('modify', ('a', b'new-content\n')),
2116
('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2117
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2118
merge_obj = self.make_merge_obj(builder, b'E-id',
2119
interesting_files=['b'])
1948
2120
entries = list(merge_obj._entries_lca())
1949
root_id = 'a-root-id'
1950
self.assertEqual([('b-id', True,
2121
root_id = b'a-root-id'
2122
self.assertEqual([(b'b-id', True,
2123
((u'b', [u'b', u'b']), u'b', u'b'),
1951
2124
((root_id, [root_id, root_id]), root_id, root_id),
1952
2125
((u'b', [u'b', u'b']), u'b', u'b'),
1953
2126
((False, [False, False]), False, False)),
1979
2152
def do_merge(self, builder, other_revision_id):
1980
2153
wt = self.get_wt_from_builder(builder)
1981
merger = _mod_merge.Merger.from_revision_ids(None,
2154
merger = _mod_merge.Merger.from_revision_ids(
1982
2155
wt, other_revision_id)
1983
2156
merger.merge_type = _mod_merge.Merge3Merger
1984
2157
return wt, merger.do_merge()
1986
2159
def test_simple_lca(self):
1987
2160
builder = self.get_builder()
1988
builder.build_snapshot('A-id', None,
1989
[('add', (u'', 'a-root-id', 'directory', None)),
1990
('add', (u'a', 'a-id', 'file', 'a\nb\nc\n'))])
1991
builder.build_snapshot('C-id', ['A-id'], [])
1992
builder.build_snapshot('B-id', ['A-id'], [])
1993
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
1994
builder.build_snapshot('D-id', ['B-id', 'C-id'],
1995
[('modify', ('a-id', 'a\nb\nc\nd\ne\nf\n'))])
1996
wt, conflicts = self.do_merge(builder, 'E-id')
2161
builder.build_snapshot(None,
2162
[('add', (u'', b'a-root-id', 'directory', None)),
2163
('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
2164
revision_id=b'A-id')
2165
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2166
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2167
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2168
builder.build_snapshot([b'B-id', b'C-id'],
2169
[('modify', ('a', b'a\nb\nc\nd\ne\nf\n'))],
2170
revision_id=b'D-id')
2171
wt, conflicts = self.do_merge(builder, b'E-id')
1997
2172
self.assertEqual(0, conflicts)
1998
2173
# The merge should have simply update the contents of 'a'
1999
self.assertEqual('a\nb\nc\nd\ne\nf\n', wt.get_file_text('a-id'))
2174
self.assertEqual(b'a\nb\nc\nd\ne\nf\n', wt.get_file_text('a'))
2001
2176
def test_conflict_without_lca(self):
2002
2177
# This test would cause a merge conflict, unless we use the lca trees
2012
2187
# F Path at 'baz' in F, which supersedes 'bar' and 'foo'
2013
2188
builder = self.get_builder()
2014
builder.build_snapshot('A-id', None,
2015
[('add', (u'', 'a-root-id', 'directory', None)),
2016
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2017
builder.build_snapshot('C-id', ['A-id'], [])
2018
builder.build_snapshot('B-id', ['A-id'],
2019
[('rename', ('foo', 'bar'))])
2020
builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2021
[('rename', ('foo', 'bar'))])
2022
builder.build_snapshot('F-id', ['E-id'],
2023
[('rename', ('bar', 'baz'))])
2024
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2025
wt, conflicts = self.do_merge(builder, 'F-id')
2189
builder.build_snapshot(None,
2190
[('add', (u'', b'a-root-id', 'directory', None)),
2191
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2192
revision_id=b'A-id')
2193
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2194
builder.build_snapshot([b'A-id'],
2195
[('rename', ('foo', 'bar'))], revision_id=b'B-id', )
2196
builder.build_snapshot([b'C-id', b'B-id'], # merge the rename
2197
[('rename', ('foo', 'bar'))], revision_id=b'E-id')
2198
builder.build_snapshot([b'E-id'],
2199
[('rename', ('bar', 'baz'))], revision_id=b'F-id')
2200
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2201
wt, conflicts = self.do_merge(builder, b'F-id')
2026
2202
self.assertEqual(0, conflicts)
2027
2203
# The merge should simply recognize that the final rename takes
2029
self.assertEqual('baz', wt.id2path('foo-id'))
2205
self.assertEqual('baz', wt.id2path(b'foo-id'))
2031
2207
def test_other_deletes_lca_renames(self):
2032
2208
# This test would cause a merge conflict, unless we use the lca trees
2042
2218
# F F deletes 'bar'
2043
2219
builder = self.get_builder()
2044
builder.build_snapshot('A-id', None,
2045
[('add', (u'', 'a-root-id', 'directory', None)),
2046
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2047
builder.build_snapshot('C-id', ['A-id'], [])
2048
builder.build_snapshot('B-id', ['A-id'],
2049
[('rename', ('foo', 'bar'))])
2050
builder.build_snapshot('E-id', ['C-id', 'B-id'], # merge the rename
2051
[('rename', ('foo', 'bar'))])
2052
builder.build_snapshot('F-id', ['E-id'],
2053
[('unversion', 'foo-id')])
2054
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2055
wt, conflicts = self.do_merge(builder, 'F-id')
2220
builder.build_snapshot(None,
2221
[('add', (u'', b'a-root-id', 'directory', None)),
2222
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2223
revision_id=b'A-id')
2224
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2225
builder.build_snapshot([b'A-id'],
2226
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2227
builder.build_snapshot([b'C-id', b'B-id'], # merge the rename
2228
[('rename', ('foo', 'bar'))], revision_id=b'E-id')
2229
builder.build_snapshot([b'E-id'],
2230
[('unversion', 'bar')], revision_id=b'F-id')
2231
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2232
wt, conflicts = self.do_merge(builder, b'F-id')
2056
2233
self.assertEqual(0, conflicts)
2057
self.assertRaises(errors.NoSuchId, wt.id2path, 'foo-id')
2234
self.assertRaises(errors.NoSuchId, wt.id2path, b'foo-id')
2059
2236
def test_executable_changes(self):
2060
2237
# A Path at 'foo'
2068
2245
# F Executable bit changed
2069
2246
builder = self.get_builder()
2070
builder.build_snapshot('A-id', None,
2071
[('add', (u'', 'a-root-id', 'directory', None)),
2072
('add', (u'foo', 'foo-id', 'file', 'a\nb\nc\n'))])
2073
builder.build_snapshot('C-id', ['A-id'], [])
2074
builder.build_snapshot('B-id', ['A-id'], [])
2075
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2076
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2247
builder.build_snapshot(None,
2248
[('add', (u'', b'a-root-id', 'directory', None)),
2249
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2250
revision_id=b'A-id')
2251
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2252
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2253
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2254
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2077
2255
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2078
2256
wt = self.get_wt_from_builder(builder)
2079
2257
tt = transform.TreeTransform(wt)
2081
tt.set_executability(True, tt.trans_id_tree_file_id('foo-id'))
2259
tt.set_executability(True, tt.trans_id_tree_path('foo'))
2086
self.assertTrue(wt.is_executable('foo-id'))
2087
wt.commit('F-id', rev_id='F-id')
2264
self.assertTrue(wt.is_executable('foo'))
2265
wt.commit('F-id', rev_id=b'F-id')
2088
2266
# Reset to D, so that we can merge F
2089
wt.set_parent_ids(['D-id'])
2090
wt.branch.set_last_revision_info(3, 'D-id')
2267
wt.set_parent_ids([b'D-id'])
2268
wt.branch.set_last_revision_info(3, b'D-id')
2092
self.assertFalse(wt.is_executable('foo-id'))
2093
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2270
self.assertFalse(wt.is_executable('foo'))
2271
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2094
2272
self.assertEqual(0, conflicts)
2095
self.assertTrue(wt.is_executable('foo-id'))
2273
self.assertTrue(wt.is_executable('foo'))
2097
2275
def test_create_symlink(self):
2098
self.requireFeature(tests.SymlinkFeature)
2276
self.requireFeature(features.SymlinkFeature)
2108
2286
# Have to use a real WT, because BranchBuilder and MemoryTree don't
2109
2287
# have symlink support
2110
2288
builder = self.get_builder()
2111
builder.build_snapshot('A-id', None,
2112
[('add', (u'', 'a-root-id', 'directory', None))])
2113
builder.build_snapshot('C-id', ['A-id'], [])
2114
builder.build_snapshot('B-id', ['A-id'], [])
2115
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2116
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2289
builder.build_snapshot(None,
2290
[('add', (u'', b'a-root-id', 'directory', None))],
2291
revision_id=b'A-id')
2292
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2293
builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2294
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2295
builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2117
2296
# Have to use a real WT, because BranchBuilder doesn't support exec bit
2118
2297
wt = self.get_wt_from_builder(builder)
2119
2298
os.symlink('bar', 'path/foo')
2120
wt.add(['foo'], ['foo-id'])
2121
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2122
wt.commit('add symlink', rev_id='F-id')
2299
wt.add(['foo'], [b'foo-id'])
2300
self.assertEqual('bar', wt.get_symlink_target('foo'))
2301
wt.commit('add symlink', rev_id=b'F-id')
2123
2302
# Reset to D, so that we can merge F
2124
wt.set_parent_ids(['D-id'])
2125
wt.branch.set_last_revision_info(3, 'D-id')
2303
wt.set_parent_ids([b'D-id'])
2304
wt.branch.set_last_revision_info(3, b'D-id')
2127
self.assertIs(None, wt.path2id('foo'))
2128
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2306
self.assertFalse(wt.is_versioned('foo'))
2307
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2129
2308
self.assertEqual(0, conflicts)
2130
self.assertEqual('foo-id', wt.path2id('foo'))
2131
self.assertEqual('bar', wt.get_symlink_target('foo-id'))
2309
self.assertEqual(b'foo-id', wt.path2id('foo'))
2310
self.assertEqual('bar', wt.get_symlink_target('foo'))
2133
2312
def test_both_sides_revert(self):
2134
2313
# Both sides of a criss-cross revert the text to the lca
2141
2320
# This must be done with a real WorkingTree, because normally their
2142
2321
# inventory contains "None" rather than a real sha1
2143
2322
builder = self.get_builder()
2144
builder.build_snapshot('A-id', None,
2145
[('add', (u'', 'a-root-id', 'directory', None)),
2146
('add', (u'foo', 'foo-id', 'file', 'A content\n'))])
2147
builder.build_snapshot('B-id', ['A-id'],
2148
[('modify', ('foo-id', 'B content\n'))])
2149
builder.build_snapshot('C-id', ['A-id'],
2150
[('modify', ('foo-id', 'C content\n'))])
2151
builder.build_snapshot('E-id', ['C-id', 'B-id'], [])
2152
builder.build_snapshot('D-id', ['B-id', 'C-id'], [])
2153
wt, conflicts = self.do_merge(builder, 'E-id')
2323
builder.build_snapshot(None,
2324
[('add', (u'', b'a-root-id', 'directory', None)),
2325
('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
2326
revision_id=b'A-id')
2327
builder.build_snapshot([b'A-id'],
2328
[('modify', ('foo', b'B content\n'))],
2329
revision_id=b'B-id')
2330
builder.build_snapshot([b'A-id'],
2331
[('modify', ('foo', b'C content\n'))],
2332
revision_id=b'C-id')
2333
builder.build_snapshot([b'C-id', b'B-id'], [],
2334
revision_id=b'E-id')
2335
builder.build_snapshot([b'B-id', b'C-id'], [],
2336
revision_id=b'D-id')
2337
wt, conflicts = self.do_merge(builder, b'E-id')
2154
2338
self.assertEqual(1, conflicts)
2155
self.assertEqualDiff('<<<<<<< TREE\n'
2159
'>>>>>>> MERGE-SOURCE\n',
2160
wt.get_file_text('foo-id'))
2339
self.assertEqualDiff(b'<<<<<<< TREE\n'
2343
b'>>>>>>> MERGE-SOURCE\n',
2344
wt.get_file_text('foo'))
2162
2346
def test_modified_symlink(self):
2163
self.requireFeature(tests.SymlinkFeature)
2347
self.requireFeature(features.SymlinkFeature)
2164
2348
# A Create symlink foo => bar
2166
2350
# B C B relinks foo => baz
2180
2364
wt.lock_write()
2181
2365
self.addCleanup(wt.unlock)
2182
2366
os.symlink('bar', 'path/foo')
2183
wt.add(['foo'], ['foo-id'])
2184
wt.commit('add symlink', rev_id='A-id')
2367
wt.add(['foo'], [b'foo-id'])
2368
wt.commit('add symlink', rev_id=b'A-id')
2185
2369
os.remove('path/foo')
2186
2370
os.symlink('baz', 'path/foo')
2187
wt.commit('foo => baz', rev_id='B-id')
2188
wt.set_last_revision('A-id')
2189
wt.branch.set_last_revision_info(1, 'A-id')
2371
wt.commit('foo => baz', rev_id=b'B-id')
2372
wt.set_last_revision(b'A-id')
2373
wt.branch.set_last_revision_info(1, b'A-id')
2191
wt.commit('C', rev_id='C-id')
2192
wt.merge_from_branch(wt.branch, 'B-id')
2193
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2194
wt.commit('E merges C & B', rev_id='E-id')
2375
wt.commit('C', rev_id=b'C-id')
2376
wt.merge_from_branch(wt.branch, b'B-id')
2377
self.assertEqual('baz', wt.get_symlink_target('foo'))
2378
wt.commit('E merges C & B', rev_id=b'E-id')
2195
2379
os.remove('path/foo')
2196
2380
os.symlink('bing', 'path/foo')
2197
wt.commit('F foo => bing', rev_id='F-id')
2198
wt.set_last_revision('B-id')
2199
wt.branch.set_last_revision_info(2, 'B-id')
2381
wt.commit('F foo => bing', rev_id=b'F-id')
2382
wt.set_last_revision(b'B-id')
2383
wt.branch.set_last_revision_info(2, b'B-id')
2201
wt.merge_from_branch(wt.branch, 'C-id')
2202
wt.commit('D merges B & C', rev_id='D-id')
2203
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2385
wt.merge_from_branch(wt.branch, b'C-id')
2386
wt.commit('D merges B & C', rev_id=b'D-id')
2387
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2204
2388
self.assertEqual(0, conflicts)
2205
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2389
self.assertEqual('bing', wt.get_symlink_target('foo'))
2207
2391
def test_renamed_symlink(self):
2208
self.requireFeature(tests.SymlinkFeature)
2392
self.requireFeature(features.SymlinkFeature)
2209
2393
# A Create symlink foo => bar
2211
2395
# B C B renames foo => barry
2223
2407
wt.lock_write()
2224
2408
self.addCleanup(wt.unlock)
2225
2409
os.symlink('bar', 'path/foo')
2226
wt.add(['foo'], ['foo-id'])
2227
wt.commit('A add symlink', rev_id='A-id')
2410
wt.add(['foo'], [b'foo-id'])
2411
wt.commit('A add symlink', rev_id=b'A-id')
2228
2412
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')
2413
wt.commit('B foo => barry', rev_id=b'B-id')
2414
wt.set_last_revision(b'A-id')
2415
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')
2417
wt.commit('C', rev_id=b'C-id')
2418
wt.merge_from_branch(wt.branch, b'B-id')
2419
self.assertEqual('barry', wt.id2path(b'foo-id'))
2420
self.assertEqual('bar', wt.get_symlink_target('barry'))
2421
wt.commit('E merges C & B', rev_id=b'E-id')
2238
2422
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')
2423
wt.commit('F barry => blah', rev_id=b'F-id')
2424
wt.set_last_revision(b'B-id')
2425
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'))
2427
wt.merge_from_branch(wt.branch, b'C-id')
2428
wt.commit('D merges B & C', rev_id=b'D-id')
2429
self.assertEqual('barry', wt.id2path(b'foo-id'))
2246
2430
# Check the output of the Merger object directly
2247
merger = _mod_merge.Merger.from_revision_ids(None,
2431
merger = _mod_merge.Merger.from_revision_ids(wt, b'F-id')
2249
2432
merger.merge_type = _mod_merge.Merge3Merger
2250
2433
merge_obj = merger.make_merger()
2251
2434
root_id = wt.path2id('')
2252
2435
entries = list(merge_obj._entries_lca())
2253
2436
# No content change, just a path change
2254
self.assertEqual([('foo-id', False,
2437
self.assertEqual([(b'foo-id', False,
2438
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2255
2439
((root_id, [root_id, root_id]), root_id, root_id),
2256
2440
((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2257
2441
((False, [False, False]), False, False)),
2259
conflicts = wt.merge_from_branch(wt.branch, to_revision='F-id')
2443
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2260
2444
self.assertEqual(0, conflicts)
2261
self.assertEqual('blah', wt.id2path('foo-id'))
2445
self.assertEqual('blah', wt.id2path(b'foo-id'))
2263
2447
def test_symlink_no_content_change(self):
2264
self.requireFeature(tests.SymlinkFeature)
2448
self.requireFeature(features.SymlinkFeature)
2265
2449
# A Create symlink foo => bar
2267
2451
# B C B relinks foo => baz
2278
2462
wt.lock_write()
2279
2463
self.addCleanup(wt.unlock)
2280
2464
os.symlink('bar', 'path/foo')
2281
wt.add(['foo'], ['foo-id'])
2282
wt.commit('add symlink', rev_id='A-id')
2465
wt.add(['foo'], [b'foo-id'])
2466
wt.commit('add symlink', rev_id=b'A-id')
2283
2467
os.remove('path/foo')
2284
2468
os.symlink('baz', 'path/foo')
2285
wt.commit('foo => baz', rev_id='B-id')
2286
wt.set_last_revision('A-id')
2287
wt.branch.set_last_revision_info(1, 'A-id')
2289
wt.commit('C', rev_id='C-id')
2290
wt.merge_from_branch(wt.branch, 'B-id')
2291
self.assertEqual('baz', wt.get_symlink_target('foo-id'))
2292
wt.commit('E merges C & B', rev_id='E-id')
2293
wt.set_last_revision('B-id')
2294
wt.branch.set_last_revision_info(2, 'B-id')
2296
wt.merge_from_branch(wt.branch, 'C-id')
2297
wt.commit('D merges B & C', rev_id='D-id')
2469
wt.commit('foo => baz', rev_id=b'B-id')
2470
wt.set_last_revision(b'A-id')
2471
wt.branch.set_last_revision_info(1, b'A-id')
2473
wt.commit('C', rev_id=b'C-id')
2474
wt.merge_from_branch(wt.branch, b'B-id')
2475
self.assertEqual('baz', wt.get_symlink_target('foo'))
2476
wt.commit('E merges C & B', rev_id=b'E-id')
2477
wt.set_last_revision(b'B-id')
2478
wt.branch.set_last_revision_info(2, b'B-id')
2480
wt.merge_from_branch(wt.branch, b'C-id')
2481
wt.commit('D merges B & C', rev_id=b'D-id')
2298
2482
os.remove('path/foo')
2299
2483
os.symlink('bing', 'path/foo')
2300
wt.commit('F foo => bing', rev_id='F-id')
2484
wt.commit('F foo => bing', rev_id=b'F-id')
2302
2486
# Check the output of the Merger object directly
2303
merger = _mod_merge.Merger.from_revision_ids(None,
2487
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2305
2488
merger.merge_type = _mod_merge.Merge3Merger
2306
2489
merge_obj = merger.make_merger()
2307
2490
# Nothing interesting happened in OTHER relative to BASE
2308
2491
self.assertEqual([], list(merge_obj._entries_lca()))
2309
2492
# Now do a real merge, just to test the rest of the stack
2310
conflicts = wt.merge_from_branch(wt.branch, to_revision='E-id')
2493
conflicts = wt.merge_from_branch(wt.branch, to_revision=b'E-id')
2311
2494
self.assertEqual(0, conflicts)
2312
self.assertEqual('bing', wt.get_symlink_target('foo-id'))
2495
self.assertEqual('bing', wt.get_symlink_target('foo'))
2314
2497
def test_symlink_this_changed_kind(self):
2315
self.requireFeature(tests.SymlinkFeature)
2498
self.requireFeature(features.SymlinkFeature)
2318
2501
# B C B creates symlink foo => bar
2326
2509
wt = self.make_branch_and_tree('path')
2327
2510
wt.lock_write()
2328
2511
self.addCleanup(wt.unlock)
2329
wt.commit('base', rev_id='A-id')
2512
wt.commit('base', rev_id=b'A-id')
2330
2513
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')
2514
wt.add(['foo'], [b'foo-id'])
2515
wt.commit('add symlink foo => bar', rev_id=b'B-id')
2516
wt.set_last_revision(b'A-id')
2517
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'))
2519
wt.commit('C', rev_id=b'C-id')
2520
wt.merge_from_branch(wt.branch, b'B-id')
2521
self.assertEqual('bar', wt.get_symlink_target('foo'))
2339
2522
os.remove('path/foo')
2340
2523
# We have to change the link in E, or it won't try to do a comparison
2341
2524
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')
2525
wt.commit('E merges C & B, overrides to bing', rev_id=b'E-id')
2526
wt.set_last_revision(b'B-id')
2527
wt.branch.set_last_revision_info(2, b'B-id')
2346
wt.merge_from_branch(wt.branch, 'C-id')
2529
wt.merge_from_branch(wt.branch, b'C-id')
2347
2530
os.remove('path/foo')
2348
self.build_tree_contents([('path/foo', 'file content\n')])
2531
self.build_tree_contents([('path/foo', b'file content\n')])
2349
2532
# XXX: workaround, WT doesn't detect kind changes unless you do
2350
2533
# iter_changes()
2351
2534
list(wt.iter_changes(wt.basis_tree()))
2352
wt.commit('D merges B & C, makes it a file', rev_id='D-id')
2535
wt.commit('D merges B & C, makes it a file', rev_id=b'D-id')
2354
merger = _mod_merge.Merger.from_revision_ids(None,
2537
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2356
2538
merger.merge_type = _mod_merge.Merge3Merger
2357
2539
merge_obj = merger.make_merger()
2358
2540
entries = list(merge_obj._entries_lca())
2359
2541
root_id = wt.path2id('')
2360
self.assertEqual([('foo-id', True,
2542
self.assertEqual([(b'foo-id', True,
2543
((None, [u'foo', None]), u'foo', u'foo'),
2361
2544
((None, [root_id, None]), root_id, root_id),
2362
2545
((None, [u'foo', None]), u'foo', u'foo'),
2363
2546
((None, [False, None]), False, False)),
2382
2565
wt.lock_write()
2383
2566
self.addCleanup(wt.unlock)
2384
2567
os.symlink('bar', 'path/foo')
2385
wt.add(['foo'], ['foo-id'])
2386
wt.commit('add symlink', rev_id='A-id')
2568
wt.add(['foo'], [b'foo-id'])
2569
wt.commit('add symlink', rev_id=b'A-id')
2387
2570
os.remove('path/foo')
2388
2571
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')
2572
wt.commit('foo => baz', rev_id=b'B-id')
2573
wt.set_last_revision(b'A-id')
2574
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')
2576
wt.commit('C', rev_id=b'C-id')
2577
wt.merge_from_branch(wt.branch, b'B-id')
2578
self.assertEqual('baz', wt.get_symlink_target('foo'))
2579
wt.commit('E merges C & B', rev_id=b'E-id')
2397
2580
os.remove('path/foo')
2398
2581
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')
2582
wt.commit('F foo => bing', rev_id=b'F-id')
2583
wt.set_last_revision(b'B-id')
2584
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()
2586
wt.merge_from_branch(wt.branch, b'C-id')
2587
wt.commit('D merges B & C', rev_id=b'D-id')
2588
wt_base = wt.controldir.sprout('base', b'A-id').open_workingtree()
2406
2589
wt_base.lock_read()
2407
2590
self.addCleanup(wt_base.unlock)
2408
wt_lca1 = wt.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2591
wt_lca1 = wt.controldir.sprout('b-tree', b'B-id').open_workingtree()
2409
2592
wt_lca1.lock_read()
2410
2593
self.addCleanup(wt_lca1.unlock)
2411
wt_lca2 = wt.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2594
wt_lca2 = wt.controldir.sprout('c-tree', b'C-id').open_workingtree()
2412
2595
wt_lca2.lock_read()
2413
2596
self.addCleanup(wt_lca2.unlock)
2414
wt_other = wt.bzrdir.sprout('other', 'F-id').open_workingtree()
2597
wt_other = wt.controldir.sprout('other', b'F-id').open_workingtree()
2415
2598
wt_other.lock_read()
2416
2599
self.addCleanup(wt_other.unlock)
2417
2600
merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2418
2601
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2419
2602
entries = list(merge_obj._entries_lca())
2420
2603
root_id = wt.path2id('')
2421
self.assertEqual([('foo-id', True,
2604
self.assertEqual([(b'foo-id', True,
2605
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2422
2606
((root_id, [root_id, root_id]), root_id, root_id),
2423
2607
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2424
2608
((False, [False, False]), False, False)),
2436
2620
# F Path at 'foo'
2437
2621
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')
2622
builder.build_snapshot(None,
2623
[('add', (u'', b'a-root-id', 'directory', None)),
2624
('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
2625
revision_id=b'A-id')
2626
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2627
builder.build_snapshot([b'A-id'],
2628
[('rename', ('foo', 'bar'))], revision_id=b'B-id')
2629
builder.build_snapshot([b'C-id', b'B-id'],
2630
[('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
2631
builder.build_snapshot([b'E-id'],
2632
[('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
2633
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2634
wt, conflicts = self.do_merge(builder, b'F-id')
2450
2635
self.assertEqual(0, conflicts)
2451
self.assertEqual('foo', wt.id2path('foo-id'))
2636
self.assertEqual('foo', wt.id2path(b'foo-id'))
2453
2638
def test_other_reverted_content_to_base(self):
2454
2639
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')
2640
builder.build_snapshot(None,
2641
[('add', (u'', b'a-root-id', 'directory', None)),
2642
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2643
revision_id=b'A-id')
2644
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2645
builder.build_snapshot([b'A-id'],
2646
[('modify', ('foo', b'B content\n'))],
2647
revision_id=b'B-id')
2648
builder.build_snapshot([b'C-id', b'B-id'],
2649
[('modify', ('foo', b'B content\n'))],
2650
revision_id=b'E-id') # merge the content
2651
builder.build_snapshot([b'E-id'],
2652
[('modify', ('foo', b'base content\n'))],
2653
revision_id=b'F-id') # Revert back to BASE
2654
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2655
wt, conflicts = self.do_merge(builder, b'F-id')
2467
2656
self.assertEqual(0, conflicts)
2468
2657
# TODO: We need to use the per-file graph to properly select a BASE
2469
2658
# before this will work. Or at least use the LCA trees to find
2470
2659
# the appropriate content base. (which is B, not A).
2471
self.assertEqual('base content\n', wt.get_file_text('foo-id'))
2660
self.assertEqual(b'base content\n', wt.get_file_text('foo'))
2473
2662
def test_other_modified_content(self):
2474
2663
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')
2664
builder.build_snapshot(None,
2665
[('add', (u'', b'a-root-id', 'directory', None)),
2666
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2667
revision_id=b'A-id')
2668
builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2669
builder.build_snapshot([b'A-id'],
2670
[('modify', ('foo', b'B content\n'))],
2671
revision_id=b'B-id')
2672
builder.build_snapshot([b'C-id', b'B-id'],
2673
[('modify', ('foo', b'B content\n'))],
2674
revision_id=b'E-id') # merge the content
2675
builder.build_snapshot([b'E-id'],
2676
[('modify', ('foo', b'F content\n'))],
2677
revision_id=b'F-id') # Override B content
2678
builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2679
wt, conflicts = self.do_merge(builder, b'F-id')
2487
2680
self.assertEqual(0, conflicts)
2488
self.assertEqual('F content\n', wt.get_file_text('foo-id'))
2681
self.assertEqual(b'F content\n', wt.get_file_text('foo'))
2490
2683
def test_all_wt(self):
2491
2684
"""Check behavior if all trees are Working Trees."""
2499
2692
# D E E updates content, renames 'b' => 'c'
2500
2693
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'],
2694
builder.build_snapshot(None,
2695
[('add', (u'', b'a-root-id', 'directory', None)),
2696
('add', (u'a', b'a-id', 'file', b'base content\n')),
2697
('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
2698
revision_id=b'A-id')
2699
builder.build_snapshot([b'A-id'],
2700
[('modify', ('foo', b'B content\n'))],
2701
revision_id=b'B-id')
2702
builder.build_snapshot([b'A-id'],
2703
[('rename', ('a', 'b'))],
2704
revision_id=b'C-id')
2705
builder.build_snapshot([b'C-id', b'B-id'],
2510
2706
[('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
2707
('modify', ('foo', b'E content\n'))],
2708
revision_id=b'E-id')
2709
builder.build_snapshot([b'B-id', b'C-id'],
2710
[('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
2514
2711
wt_this = self.get_wt_from_builder(builder)
2515
wt_base = wt_this.bzrdir.sprout('base', 'A-id').open_workingtree()
2712
wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2516
2713
wt_base.lock_read()
2517
2714
self.addCleanup(wt_base.unlock)
2518
wt_lca1 = wt_this.bzrdir.sprout('b-tree', 'B-id').open_workingtree()
2715
wt_lca1 = wt_this.controldir.sprout('b-tree', b'B-id').open_workingtree()
2519
2716
wt_lca1.lock_read()
2520
2717
self.addCleanup(wt_lca1.unlock)
2521
wt_lca2 = wt_this.bzrdir.sprout('c-tree', 'C-id').open_workingtree()
2718
wt_lca2 = wt_this.controldir.sprout('c-tree', b'C-id').open_workingtree()
2522
2719
wt_lca2.lock_read()
2523
2720
self.addCleanup(wt_lca2.unlock)
2524
wt_other = wt_this.bzrdir.sprout('other', 'E-id').open_workingtree()
2721
wt_other = wt_this.controldir.sprout('other', b'E-id').open_workingtree()
2525
2722
wt_other.lock_read()
2526
2723
self.addCleanup(wt_other.unlock)
2527
2724
merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2528
2725
wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2529
2726
entries = list(merge_obj._entries_lca())
2530
root_id = 'a-root-id'
2531
self.assertEqual([('a-id', False,
2727
root_id = b'a-root-id'
2728
self.assertEqual([(b'a-id', False,
2729
((u'a', [u'a', u'b']), u'c', u'b'),
2532
2730
((root_id, [root_id, root_id]), root_id, root_id),
2533
2731
((u'a', [u'a', u'b']), u'c', u'b'),
2534
2732
((False, [False, False]), False, False)),
2734
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2536
2735
((root_id, [root_id, root_id]), root_id, root_id),
2537
2736
((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2538
2737
((False, [False, False]), False, False)),
2542
2741
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2543
2742
# 'tree-reference'
2544
2743
wt = self.make_branch_and_tree('tree',
2545
format='dirstate-with-subtree')
2744
format='development-subtree')
2546
2745
wt.lock_write()
2547
2746
self.addCleanup(wt.unlock)
2548
2747
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')])
2748
format='development-subtree')
2749
wt.set_root_id(b'a-root-id')
2750
sub_tree.set_root_id(b'sub-tree-root')
2751
self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2553
2752
sub_tree.add('file')
2554
sub_tree.commit('foo', rev_id='sub-A-id')
2753
sub_tree.commit('foo', rev_id=b'sub-A-id')
2555
2754
wt.add_reference(sub_tree)
2556
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2755
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2557
2756
# 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)
2758
wt.commit('B', rev_id=b'B-id', recursive=None)
2759
wt.set_last_revision(b'A-id')
2760
wt.branch.set_last_revision_info(1, b'A-id')
2761
wt.commit('C', rev_id=b'C-id', recursive=None)
2762
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2763
wt.commit('E', rev_id=b'E-id', recursive=None)
2764
wt.set_parent_ids([b'B-id', b'C-id'])
2765
wt.branch.set_last_revision_info(2, b'B-id')
2766
wt.commit('D', rev_id=b'D-id', recursive=None)
2569
merger = _mod_merge.Merger.from_revision_ids(None,
2768
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2571
2769
merger.merge_type = _mod_merge.Merge3Merger
2572
2770
merge_obj = merger.make_merger()
2573
2771
entries = list(merge_obj._entries_lca())
2577
2775
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2578
2776
# 'tree-reference'
2579
2777
wt = self.make_branch_and_tree('tree',
2580
format='dirstate-with-subtree')
2778
format='development-subtree')
2581
2779
wt.lock_write()
2582
2780
self.addCleanup(wt.unlock)
2583
2781
sub_tree = self.make_branch_and_tree('tree/sub',
2584
format='dirstate-with-subtree')
2585
wt.set_root_id('a-root-id')
2586
sub_tree.set_root_id('sub-tree-root')
2587
self.build_tree_contents([('tree/sub/file', 'text1')])
2782
format='development-subtree')
2783
wt.set_root_id(b'a-root-id')
2784
sub_tree.set_root_id(b'sub-tree-root')
2785
self.build_tree_contents([('tree/sub/file', b'text1')])
2588
2786
sub_tree.add('file')
2589
sub_tree.commit('foo', rev_id='sub-A-id')
2787
sub_tree.commit('foo', rev_id=b'sub-A-id')
2590
2788
wt.add_reference(sub_tree)
2591
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2789
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2592
2790
# Now create a criss-cross merge in the parent, without modifying the
2594
wt.commit('B', rev_id='B-id', recursive=None)
2595
wt.set_last_revision('A-id')
2596
wt.branch.set_last_revision_info(1, 'A-id')
2597
wt.commit('C', rev_id='C-id', recursive=None)
2598
wt.merge_from_branch(wt.branch, to_revision='B-id')
2599
self.build_tree_contents([('tree/sub/file', 'text2')])
2600
sub_tree.commit('modify contents', rev_id='sub-B-id')
2601
wt.commit('E', rev_id='E-id', recursive=None)
2602
wt.set_parent_ids(['B-id', 'C-id'])
2603
wt.branch.set_last_revision_info(2, 'B-id')
2604
wt.commit('D', rev_id='D-id', recursive=None)
2792
wt.commit('B', rev_id=b'B-id', recursive=None)
2793
wt.set_last_revision(b'A-id')
2794
wt.branch.set_last_revision_info(1, b'A-id')
2795
wt.commit('C', rev_id=b'C-id', recursive=None)
2796
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2797
self.build_tree_contents([('tree/sub/file', b'text2')])
2798
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2799
wt.commit('E', rev_id=b'E-id', recursive=None)
2800
wt.set_parent_ids([b'B-id', b'C-id'])
2801
wt.branch.set_last_revision_info(2, b'B-id')
2802
wt.commit('D', rev_id=b'D-id', recursive=None)
2606
merger = _mod_merge.Merger.from_revision_ids(None,
2804
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2608
2805
merger.merge_type = _mod_merge.Merge3Merger
2609
2806
merge_obj = merger.make_merger()
2610
2807
entries = list(merge_obj._entries_lca())
2616
2813
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2617
2814
# 'tree-reference'
2618
2815
wt = self.make_branch_and_tree('tree',
2619
format='dirstate-with-subtree')
2816
format='development-subtree')
2620
2817
wt.lock_write()
2621
2818
self.addCleanup(wt.unlock)
2622
2819
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')])
2820
format='development-subtree')
2821
wt.set_root_id(b'a-root-id')
2822
sub_tree.set_root_id(b'sub-tree-root')
2823
self.build_tree_contents([('tree/sub/file', b'text1')])
2627
2824
sub_tree.add('file')
2628
sub_tree.commit('foo', rev_id='sub-A-id')
2825
sub_tree.commit('foo', rev_id=b'sub-A-id')
2629
2826
wt.add_reference(sub_tree)
2630
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2827
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2631
2828
# 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')
2830
wt.commit('B', rev_id=b'B-id', recursive=None)
2831
wt.set_last_revision(b'A-id')
2832
wt.branch.set_last_revision_info(1, b'A-id')
2833
wt.commit('C', rev_id=b'C-id', recursive=None)
2834
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2638
2835
wt.rename_one('sub', 'alt_sub')
2639
wt.commit('E', rev_id='E-id', recursive=None)
2640
wt.set_last_revision('B-id')
2836
wt.commit('E', rev_id=b'E-id', recursive=None)
2837
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)
2839
wt.set_parent_ids([b'B-id', b'C-id'])
2840
wt.branch.set_last_revision_info(2, b'B-id')
2841
wt.commit('D', rev_id=b'D-id', recursive=None)
2646
merger = _mod_merge.Merger.from_revision_ids(None,
2843
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2648
2844
merger.merge_type = _mod_merge.Merge3Merger
2649
2845
merge_obj = merger.make_merger()
2650
2846
entries = list(merge_obj._entries_lca())
2651
root_id = 'a-root-id'
2652
self.assertEqual([('sub-tree-root', False,
2847
root_id = b'a-root-id'
2848
self.assertEqual([(b'sub-tree-root', False,
2849
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2653
2850
((root_id, [root_id, root_id]), root_id, root_id),
2654
2851
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2655
2852
((False, [False, False]), False, False)),
2659
2856
# Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2660
2857
# 'tree-reference'
2661
2858
wt = self.make_branch_and_tree('tree',
2662
format='dirstate-with-subtree')
2859
format='development-subtree')
2663
2860
wt.lock_write()
2664
2861
self.addCleanup(wt.unlock)
2665
2862
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')])
2863
format='development-subtree')
2864
wt.set_root_id(b'a-root-id')
2865
sub_tree.set_root_id(b'sub-tree-root')
2866
self.build_tree_contents([('tree/sub/file', b'text1')])
2670
2867
sub_tree.add('file')
2671
sub_tree.commit('foo', rev_id='sub-A-id')
2868
sub_tree.commit('foo', rev_id=b'sub-A-id')
2672
2869
wt.add_reference(sub_tree)
2673
wt.commit('set text to 1', rev_id='A-id', recursive=None)
2870
wt.commit('set text to 1', rev_id=b'A-id', recursive=None)
2674
2871
# 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')
2873
wt.commit('B', rev_id=b'B-id', recursive=None)
2874
wt.set_last_revision(b'A-id')
2875
wt.branch.set_last_revision_info(1, b'A-id')
2876
wt.commit('C', rev_id=b'C-id', recursive=None)
2877
wt.merge_from_branch(wt.branch, to_revision=b'B-id')
2878
self.build_tree_contents([('tree/sub/file', b'text2')])
2879
sub_tree.commit('modify contents', rev_id=b'sub-B-id')
2683
2880
wt.rename_one('sub', 'alt_sub')
2684
wt.commit('E', rev_id='E-id', recursive=None)
2685
wt.set_last_revision('B-id')
2881
wt.commit('E', rev_id=b'E-id', recursive=None)
2882
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)
2884
wt.set_parent_ids([b'B-id', b'C-id'])
2885
wt.branch.set_last_revision_info(2, b'B-id')
2886
wt.commit('D', rev_id=b'D-id', recursive=None)
2691
merger = _mod_merge.Merger.from_revision_ids(None,
2888
merger = _mod_merge.Merger.from_revision_ids(wt, b'E-id')
2693
2889
merger.merge_type = _mod_merge.Merge3Merger
2694
2890
merge_obj = merger.make_merger()
2695
2891
entries = list(merge_obj._entries_lca())
2696
root_id = 'a-root-id'
2892
root_id = b'a-root-id'
2697
2893
self.assertEqual([('sub-tree-root', False,
2894
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2698
2895
((root_id, [root_id, root_id]), root_id, root_id),
2699
2896
((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2700
2897
((False, [False, False]), False, False)),
2917
3114
conflicts = builder.merge()
2918
3115
# The hook should not call the merge_text() method
2919
3116
self.assertEqual([], self.calls)
3119
class TestMergeIntoBase(tests.TestCaseWithTransport):
3121
def setup_simple_branch(self, relpath, shape=None, root_id=None):
3122
"""One commit, containing tree specified by optional shape.
3124
Default is empty tree (just root entry).
3127
root_id = b'%s-root-id' % (relpath.encode('ascii'),)
3128
wt = self.make_branch_and_tree(relpath)
3129
wt.set_root_id(root_id)
3130
if shape is not None:
3131
adjusted_shape = [relpath + '/' + elem for elem in shape]
3132
self.build_tree(adjusted_shape)
3134
(b'%s-%s-id' % (relpath.encode('utf-8'), basename(elem.rstrip('/')).encode('ascii')))
3136
wt.add(shape, ids=ids)
3137
rev_id = b'r1-%s' % (relpath.encode('utf-8'),)
3138
wt.commit("Initial commit of %s" % (relpath,), rev_id=rev_id)
3139
self.assertEqual(root_id, wt.path2id(''))
3142
def setup_two_branches(self, custom_root_ids=True):
3143
"""Setup 2 branches, one will be a library, the other a project."""
3147
root_id = inventory.ROOT_ID
3148
project_wt = self.setup_simple_branch(
3149
'project', ['README', 'dir/', 'dir/file.c'],
3151
lib_wt = self.setup_simple_branch(
3152
'lib1', ['README', 'Makefile', 'foo.c'], root_id)
3154
return project_wt, lib_wt
3156
def do_merge_into(self, location, merge_as):
3157
"""Helper for using MergeIntoMerger.
3159
:param location: location of directory to merge from, either the
3160
location of a branch or of a path inside a branch.
3161
:param merge_as: the path in a tree to add the new directory as.
3162
:returns: the conflicts from 'do_merge'.
3164
operation = cleanup.OperationWithCleanups(self._merge_into)
3165
return operation.run(location, merge_as)
3167
def _merge_into(self, op, location, merge_as):
3168
# Open and lock the various tree and branch objects
3169
wt, subdir_relpath = WorkingTree.open_containing(merge_as)
3170
op.add_cleanup(wt.lock_write().unlock)
3171
branch_to_merge, subdir_to_merge = _mod_branch.Branch.open_containing(
3173
op.add_cleanup(branch_to_merge.lock_read().unlock)
3174
other_tree = branch_to_merge.basis_tree()
3175
op.add_cleanup(other_tree.lock_read().unlock)
3177
merger = _mod_merge.MergeIntoMerger(this_tree=wt, other_tree=other_tree,
3178
other_branch=branch_to_merge, target_subdir=subdir_relpath,
3179
source_subpath=subdir_to_merge)
3180
merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3181
conflicts = merger.do_merge()
3182
merger.set_pending()
3185
def assertTreeEntriesEqual(self, expected_entries, tree):
3186
"""Assert that 'tree' contains the expected inventory entries.
3188
:param expected_entries: sequence of (path, file-id) pairs.
3190
files = [(path, ie.file_id) for path, ie in tree.iter_entries_by_dir()]
3191
self.assertEqual(expected_entries, files)
3194
class TestMergeInto(TestMergeIntoBase):
3196
def test_newdir_with_unique_roots(self):
3197
"""Merge a branch with a unique root into a new directory."""
3198
project_wt, lib_wt = self.setup_two_branches()
3199
self.do_merge_into('lib1', 'project/lib1')
3200
project_wt.lock_read()
3201
self.addCleanup(project_wt.unlock)
3202
# The r1-lib1 revision should be merged into this one
3203
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3204
self.assertTreeEntriesEqual(
3205
[('', b'project-root-id'),
3206
('README', b'project-README-id'),
3207
('dir', b'project-dir-id'),
3208
('lib1', b'lib1-root-id'),
3209
('dir/file.c', b'project-file.c-id'),
3210
('lib1/Makefile', b'lib1-Makefile-id'),
3211
('lib1/README', b'lib1-README-id'),
3212
('lib1/foo.c', b'lib1-foo.c-id'),
3215
def test_subdir(self):
3216
"""Merge a branch into a subdirectory of an existing directory."""
3217
project_wt, lib_wt = self.setup_two_branches()
3218
self.do_merge_into('lib1', 'project/dir/lib1')
3219
project_wt.lock_read()
3220
self.addCleanup(project_wt.unlock)
3221
# The r1-lib1 revision should be merged into this one
3222
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3223
self.assertTreeEntriesEqual(
3224
[('', b'project-root-id'),
3225
('README', b'project-README-id'),
3226
('dir', b'project-dir-id'),
3227
('dir/file.c', b'project-file.c-id'),
3228
('dir/lib1', b'lib1-root-id'),
3229
('dir/lib1/Makefile', b'lib1-Makefile-id'),
3230
('dir/lib1/README', b'lib1-README-id'),
3231
('dir/lib1/foo.c', b'lib1-foo.c-id'),
3234
def test_newdir_with_repeat_roots(self):
3235
"""If the file-id of the dir to be merged already exists a new ID will
3236
be allocated to let the merge happen.
3238
project_wt, lib_wt = self.setup_two_branches(custom_root_ids=False)
3239
root_id = project_wt.path2id('')
3240
self.do_merge_into('lib1', 'project/lib1')
3241
project_wt.lock_read()
3242
self.addCleanup(project_wt.unlock)
3243
# The r1-lib1 revision should be merged into this one
3244
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3245
new_lib1_id = project_wt.path2id('lib1')
3246
self.assertNotEqual(None, new_lib1_id)
3247
self.assertTreeEntriesEqual(
3249
('README', b'project-README-id'),
3250
('dir', b'project-dir-id'),
3251
('lib1', new_lib1_id),
3252
('dir/file.c', b'project-file.c-id'),
3253
('lib1/Makefile', b'lib1-Makefile-id'),
3254
('lib1/README', b'lib1-README-id'),
3255
('lib1/foo.c', b'lib1-foo.c-id'),
3258
def test_name_conflict(self):
3259
"""When the target directory name already exists a conflict is
3260
generated and the original directory is renamed to foo.moved.
3262
dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3263
src_wt = self.setup_simple_branch('src', ['README'])
3264
conflicts = self.do_merge_into('src', 'dest/dir')
3265
self.assertEqual(1, conflicts)
3267
self.addCleanup(dest_wt.unlock)
3268
# The r1-lib1 revision should be merged into this one
3269
self.assertEqual([b'r1-dest', b'r1-src'], dest_wt.get_parent_ids())
3270
self.assertTreeEntriesEqual(
3271
[('', b'dest-root-id'),
3272
('dir', b'src-root-id'),
3273
('dir.moved', b'dest-dir-id'),
3274
('dir/README', b'src-README-id'),
3275
('dir.moved/file.txt', b'dest-file.txt-id'),
3278
def test_file_id_conflict(self):
3279
"""A conflict is generated if the merge-into adds a file (or other
3280
inventory entry) with a file-id that already exists in the target tree.
3282
dest_wt = self.setup_simple_branch('dest', ['file.txt'])
3283
# Make a second tree with a file-id that will clash with file.txt in
3285
src_wt = self.make_branch_and_tree('src')
3286
self.build_tree(['src/README'])
3287
src_wt.add(['README'], ids=[b'dest-file.txt-id'])
3288
src_wt.commit("Rev 1 of src.", rev_id=b'r1-src')
3289
conflicts = self.do_merge_into('src', 'dest/dir')
3290
# This is an edge case that shouldn't happen to users very often. So
3291
# we don't care really about the exact presentation of the conflict,
3292
# just that there is one.
3293
self.assertEqual(1, conflicts)
3295
def test_only_subdir(self):
3296
"""When the location points to just part of a tree, merge just that
3299
dest_wt = self.setup_simple_branch('dest')
3300
src_wt = self.setup_simple_branch(
3301
'src', ['hello.txt', 'dir/', 'dir/foo.c'])
3302
conflicts = self.do_merge_into('src/dir', 'dest/dir')
3304
self.addCleanup(dest_wt.unlock)
3305
# The r1-lib1 revision should NOT be merged into this one (this is a
3307
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3308
self.assertTreeEntriesEqual(
3309
[('', b'dest-root-id'),
3310
('dir', b'src-dir-id'),
3311
('dir/foo.c', b'src-foo.c-id'),
3314
def test_only_file(self):
3315
"""An edge case: merge just one file, not a whole dir."""
3316
dest_wt = self.setup_simple_branch('dest')
3317
two_file_wt = self.setup_simple_branch(
3318
'two-file', ['file1.txt', 'file2.txt'])
3319
conflicts = self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3321
self.addCleanup(dest_wt.unlock)
3322
# The r1-lib1 revision should NOT be merged into this one
3323
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3324
self.assertTreeEntriesEqual(
3325
[('', b'dest-root-id'), ('file1.txt', b'two-file-file1.txt-id')],
3328
def test_no_such_source_path(self):
3329
"""PathNotInTree is raised if the specified path in the source tree
3332
dest_wt = self.setup_simple_branch('dest')
3333
two_file_wt = self.setup_simple_branch('src', ['dir/'])
3334
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3335
'src/no-such-dir', 'dest/foo')
3337
self.addCleanup(dest_wt.unlock)
3338
# The dest tree is unmodified.
3339
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3340
self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
3342
def test_no_such_target_path(self):
3343
"""PathNotInTree is also raised if the specified path in the target
3344
tree does not exist.
3346
dest_wt = self.setup_simple_branch('dest')
3347
two_file_wt = self.setup_simple_branch('src', ['file.txt'])
3348
self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3349
'src', 'dest/no-such-dir/foo')
3351
self.addCleanup(dest_wt.unlock)
3352
# The dest tree is unmodified.
3353
self.assertEqual([b'r1-dest'], dest_wt.get_parent_ids())
3354
self.assertTreeEntriesEqual([('', b'dest-root-id')], dest_wt)
3357
class TestMergeHooks(TestCaseWithTransport):
3360
super(TestMergeHooks, self).setUp()
3361
self.tree_a = self.make_branch_and_tree('tree_a')
3362
self.build_tree_contents([('tree_a/file', b'content_1')])
3363
self.tree_a.add('file', b'file-id')
3364
self.tree_a.commit('added file')
3366
self.tree_b = self.tree_a.controldir.sprout('tree_b').open_workingtree()
3367
self.build_tree_contents([('tree_b/file', b'content_2')])
3368
self.tree_b.commit('modify file')
3370
def test_pre_merge_hook_inject_different_tree(self):
3371
tree_c = self.tree_b.controldir.sprout('tree_c').open_workingtree()
3372
self.build_tree_contents([('tree_c/file', b'content_3')])
3373
tree_c.commit("more content")
3375
def factory(merger):
3376
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3377
merger.other_tree = tree_c
3378
calls.append(merger)
3379
_mod_merge.Merger.hooks.install_named_hook('pre_merge',
3380
factory, 'test factory')
3381
self.tree_a.merge_from_branch(self.tree_b.branch)
3383
self.assertFileEqual(b"content_3", 'tree_a/file')
3384
self.assertLength(1, calls)
3386
def test_post_merge_hook_called(self):
3388
def factory(merger):
3389
self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3390
calls.append(merger)
3391
_mod_merge.Merger.hooks.install_named_hook('post_merge',
3392
factory, 'test factory')
3394
self.tree_a.merge_from_branch(self.tree_b.branch)
3396
self.assertFileEqual(b"content_2", 'tree_a/file')
3397
self.assertLength(1, calls)