374
375
def test_weave_cherrypick(self):
375
376
this_tree, other_tree = self.prepare_cherrypick()
376
377
merger = _mod_merge.Merger.from_revision_ids(
377
this_tree, 'rev3b', 'rev2b', other_tree.branch)
378
this_tree, b'rev3b', b'rev2b', other_tree.branch)
378
379
merger.merge_type = _mod_merge.WeaveMerger
379
380
merger.do_merge()
380
self.assertFileEqual('c\na\n', 'this/file')
381
self.assertFileEqual(b'c\na\n', 'this/file')
382
383
def test_weave_cannot_reverse_cherrypick(self):
383
384
this_tree, other_tree = self.prepare_cherrypick()
384
385
merger = _mod_merge.Merger.from_revision_ids(
385
this_tree, 'rev2b', 'rev3b', other_tree.branch)
386
this_tree, b'rev2b', b'rev3b', other_tree.branch)
386
387
merger.merge_type = _mod_merge.WeaveMerger
387
388
self.assertRaises(errors.CannotReverseCherrypick, merger.do_merge)
389
390
def test_merge3_can_reverse_cherrypick(self):
390
391
this_tree, other_tree = self.prepare_cherrypick()
391
392
merger = _mod_merge.Merger.from_revision_ids(
392
this_tree, 'rev2b', 'rev3b', other_tree.branch)
393
this_tree, b'rev2b', b'rev3b', other_tree.branch)
393
394
merger.merge_type = _mod_merge.Merge3Merger
394
395
merger.do_merge()
577
572
[c+'\n' for c in text])
579
574
def setup_plan_merge(self):
580
self.add_rev('root', 'A', [], 'abc')
581
self.add_rev('root', 'B', ['A'], 'acehg')
582
self.add_rev('root', 'C', ['A'], 'fabg')
583
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',))
585
580
def setup_plan_merge_uncommitted(self):
586
self.add_version(('root', 'A'), [], 'abc')
587
self.add_uncommitted_version(('root', 'B:'), [('root', 'A')], 'acehg')
588
self.add_uncommitted_version(('root', 'C:'), [('root', 'A')], 'fabg')
589
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',))
591
586
def test_base_from_plan(self):
592
587
self.setup_plan_merge()
593
plan = self.plan_merge_vf.plan_merge('B', 'C')
588
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
594
589
pwm = versionedfile.PlanWeaveMerge(plan)
595
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())
597
592
def test_unique_lines(self):
598
593
plan = self.setup_plan_merge()
599
594
self.assertEqual(plan._unique_lines(
600
plan._get_matching_blocks('B', 'C')),
595
plan._get_matching_blocks(b'B', b'C')),
601
596
([1, 2, 3], [0, 2]))
603
598
def test_plan_merge(self):
604
599
self.setup_plan_merge()
605
plan = self.plan_merge_vf.plan_merge('B', 'C')
600
plan = self.plan_merge_vf.plan_merge(b'B', b'C')
606
601
self.assertEqual([
608
('unchanged', 'a\n'),
603
('unchanged', b'a\n'),
604
('killed-a', b'b\n'),
605
('killed-b', b'c\n'),
617
612
def test_plan_merge_cherrypick(self):
618
self.add_rev('root', 'A', [], 'abc')
619
self.add_rev('root', 'B', ['A'], 'abcde')
620
self.add_rev('root', 'C', ['A'], 'abcefg')
621
self.add_rev('root', 'D', ['A', 'B', 'C'], 'abcdegh')
622
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',))
623
618
# We shortcut when one text supersedes the other in the per-file graph.
624
619
# We don't actually need to compare the texts at this point.
625
620
self.assertEqual([
633
628
list(my_plan.plan_merge()))
635
630
def test_plan_merge_no_common_ancestor(self):
636
self.add_rev('root', 'A', [], 'abc')
637
self.add_rev('root', 'B', [], 'xyz')
638
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',))
639
634
self.assertEqual([
646
641
list(my_plan.plan_merge()))
648
643
def test_plan_merge_tail_ancestors(self):
665
660
# are already present in E
667
662
# Introduce the base text
668
self.add_rev('root', 'A', [], 'abc')
663
self.add_rev(b'root', b'A', [], b'abc')
669
664
# Introduces a new line B
670
self.add_rev('root', 'B', ['A'], 'aBbc')
665
self.add_rev(b'root', b'B', [b'A'], b'aBbc')
671
666
# Introduces a new line C
672
self.add_rev('root', 'C', ['A'], 'abCc')
667
self.add_rev(b'root', b'C', [b'A'], b'abCc')
673
668
# Introduce new line D
674
self.add_rev('root', 'D', ['B'], 'DaBbc')
669
self.add_rev(b'root', b'D', [b'B'], b'DaBbc')
675
670
# Merges B and C by just incorporating both
676
self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
671
self.add_rev(b'root', b'E', [b'B', b'C'], b'aBbCc')
677
672
# Introduce new line F
678
self.add_rev('root', 'F', ['C'], 'abCcF')
673
self.add_rev(b'root', b'F', [b'C'], b'abCcF')
679
674
# Merge D & E by just combining the texts
680
self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
675
self.add_rev(b'root', b'G', [b'D', b'E'], b'DaBbCc')
681
676
# Merge F & E by just combining the texts
682
self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
677
self.add_rev(b'root', b'H', [b'F', b'E'], b'aBbCcF')
683
678
# Merge G & H by just combining texts
684
self.add_rev('root', 'I', ['G', 'H'], 'DaBbCcF')
679
self.add_rev(b'root', b'I', [b'G', b'H'], b'DaBbCcF')
685
680
# Merge G & H but supersede an old line in B
686
self.add_rev('root', 'J', ['H', 'G'], 'DaJbCcF')
687
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')
688
683
self.assertEqual([
689
('unchanged', 'D\n'),
690
('unchanged', 'a\n'),
693
('unchanged', 'b\n'),
694
('unchanged', 'C\n'),
695
('unchanged', 'c\n'),
696
('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')],
699
694
def test_plan_merge_tail_triple_ancestors(self):
714
709
# a third LCA that doesn't add new lines, but will trigger our more
715
710
# involved ancestry logic
717
self.add_rev('root', 'A', [], 'abc')
718
self.add_rev('root', 'B', ['A'], 'aBbc')
719
self.add_rev('root', 'C', ['A'], 'abCc')
720
self.add_rev('root', 'D', ['B'], 'DaBbc')
721
self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
722
self.add_rev('root', 'F', ['C'], 'abCcF')
723
self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
724
self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
725
self.add_rev('root', 'Q', ['E'], 'aBbCc')
726
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')
727
722
# Merge G & H but supersede an old line in B
728
self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
729
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')
730
725
self.assertEqual([
731
('unchanged', 'D\n'),
732
('unchanged', 'a\n'),
735
('unchanged', 'b\n'),
736
('unchanged', 'C\n'),
737
('unchanged', 'c\n'),
738
('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')],
741
736
def test_plan_merge_2_tail_triple_ancestors(self):
754
749
# This is meant to test after hitting a 3-way LCA, and multiple tail
755
750
# ancestors (only have NULL_REVISION in common)
757
self.add_rev('root', 'A', [], 'abc')
758
self.add_rev('root', 'B', [], 'def')
759
self.add_rev('root', 'D', ['A'], 'Dabc')
760
self.add_rev('root', 'E', ['A', 'B'], 'abcdef')
761
self.add_rev('root', 'F', ['B'], 'defF')
762
self.add_rev('root', 'G', ['D', 'E'], 'Dabcdef')
763
self.add_rev('root', 'H', ['F', 'E'], 'abcdefF')
764
self.add_rev('root', 'Q', ['E'], 'abcdef')
765
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')
766
761
# Merge G & H but supersede an old line in B
767
self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DabcdJfF')
768
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')
769
764
self.assertEqual([
770
('unchanged', 'D\n'),
771
('unchanged', 'a\n'),
772
('unchanged', 'b\n'),
773
('unchanged', 'c\n'),
774
('unchanged', 'd\n'),
777
('unchanged', 'f\n'),
778
('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')],
781
776
def test_plan_merge_uncommitted_files(self):
782
777
self.setup_plan_merge_uncommitted()
783
plan = self.plan_merge_vf.plan_merge('B:', 'C:')
778
plan = self.plan_merge_vf.plan_merge(b'B:', b'C:')
784
779
self.assertEqual([
786
('unchanged', 'a\n'),
781
('unchanged', b'a\n'),
782
('killed-a', b'b\n'),
783
('killed-b', b'c\n'),
795
790
def test_plan_merge_insert_order(self):
800
795
same ordering, then the lines match the parents, if they don't only
801
796
*some* of the lines match.
803
self.add_rev('root', 'A', [], 'abcdef')
804
self.add_rev('root', 'B', ['A'], 'abwxcdef')
805
self.add_rev('root', 'C', ['A'], 'abyzcdef')
798
self.add_rev(b'root', b'A', [], b'abcdef')
799
self.add_rev(b'root', b'B', [b'A'], b'abwxcdef')
800
self.add_rev(b'root', b'C', [b'A'], b'abyzcdef')
806
801
# Merge, and resolve the conflict by adding *both* sets of lines
807
802
# If we get the ordering wrong, these will look like new lines in D,
808
803
# rather than carried over from B, C
809
self.add_rev('root', 'D', ['B', 'C'],
804
self.add_rev(b'root', b'D', [b'B', b'C'],
811
806
# Supersede the lines in B and delete the lines in C, which will
812
807
# conflict if they are treated as being in D
813
self.add_rev('root', 'E', ['C', 'B'],
808
self.add_rev(b'root', b'E', [b'C', b'B'],
815
810
# Same thing for the lines in C
816
self.add_rev('root', 'F', ['C'], 'abpqcdef')
817
plan = self.plan_merge_vf.plan_merge('D', 'E')
811
self.add_rev(b'root', b'F', [b'C'], b'abpqcdef')
812
plan = self.plan_merge_vf.plan_merge(b'D', b'E')
818
813
self.assertEqual([
819
('unchanged', 'a\n'),
820
('unchanged', 'b\n'),
827
('unchanged', 'c\n'),
828
('unchanged', 'd\n'),
829
('unchanged', 'e\n'),
830
('unchanged', 'f\n')],
814
('unchanged', b'a\n'),
815
('unchanged', b'b\n'),
816
('killed-b', b'w\n'),
817
('killed-b', b'x\n'),
818
('killed-b', b'y\n'),
819
('killed-b', b'z\n'),
822
('unchanged', b'c\n'),
823
('unchanged', b'd\n'),
824
('unchanged', b'e\n'),
825
('unchanged', b'f\n')],
832
plan = self.plan_merge_vf.plan_merge('E', 'D')
827
plan = self.plan_merge_vf.plan_merge(b'E', b'D')
833
828
# Going in the opposite direction shows the effect of the opposite plan
834
829
self.assertEqual([
835
('unchanged', 'a\n'),
836
('unchanged', 'b\n'),
841
('killed-both', 'w\n'),
842
('killed-both', 'x\n'),
845
('unchanged', 'c\n'),
846
('unchanged', 'd\n'),
847
('unchanged', 'e\n'),
848
('unchanged', 'f\n')],
830
('unchanged', b'a\n'),
831
('unchanged', b'b\n'),
834
('killed-a', b'y\n'),
835
('killed-a', b'z\n'),
836
('killed-both', b'w\n'),
837
('killed-both', b'x\n'),
840
('unchanged', b'c\n'),
841
('unchanged', b'd\n'),
842
('unchanged', b'e\n'),
843
('unchanged', b'f\n')],
851
846
def test_plan_merge_criss_cross(self):
873
868
# 'foo', it should appear as superseding the value in F (since it
874
869
# came from B), rather than conflict because of the resolution during
876
self.add_rev('root', 'XX', [], 'qrs')
877
self.add_rev('root', 'A', ['XX'], 'abcdef')
878
self.add_rev('root', 'B', ['A'], 'axcdef')
879
self.add_rev('root', 'C', ['B'], 'axcdefg')
880
self.add_rev('root', 'D', ['B'], 'haxcdef')
881
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')
882
877
# Simple combining of all texts
883
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')
884
879
# combine and supersede 'x'
885
self.add_rev('root', 'G', ['C', 'D', 'E'], 'hazcdyfg')
886
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')
887
882
self.assertEqual([
888
('unchanged', 'h\n'),
889
('unchanged', 'a\n'),
890
('killed-base', 'b\n'),
893
('unchanged', 'c\n'),
894
('unchanged', 'd\n'),
895
('killed-base', 'e\n'),
896
('unchanged', 'y\n'),
897
('unchanged', 'f\n'),
898
('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')],
900
plan = self.plan_merge_vf.plan_lca_merge('F', 'G')
895
plan = self.plan_merge_vf.plan_lca_merge(b'F', b'G')
901
896
# This is one of the main differences between plan_merge and
902
897
# plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
903
898
# because 'x' was not present in one of the bases. However, in this
904
899
# case it is spurious because 'x' does not exist in the global base A.
905
900
self.assertEqual([
906
('unchanged', 'h\n'),
907
('unchanged', 'a\n'),
908
('conflicted-a', 'x\n'),
910
('unchanged', 'c\n'),
911
('unchanged', 'd\n'),
912
('unchanged', 'y\n'),
913
('unchanged', 'f\n'),
914
('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')],
917
912
def test_criss_cross_flip_flop(self):
920
915
# XX unused ancestor, should not show up in the weave
924
919
# B C B & C both introduce a new line
928
923
# D E B & C are both merged, so both are common ancestors
929
924
# In the process of merging, both sides order the new
930
925
# lines differently
932
self.add_rev('root', 'XX', [], 'qrs')
933
self.add_rev('root', 'A', ['XX'], 'abcdef')
934
self.add_rev('root', 'B', ['A'], 'abcdgef')
935
self.add_rev('root', 'C', ['A'], 'abcdhef')
936
self.add_rev('root', 'D', ['B', 'C'], 'abcdghef')
937
self.add_rev('root', 'E', ['C', 'B'], 'abcdhgef')
938
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'))
939
934
self.assertEqual([
940
('unchanged', 'a\n'),
941
('unchanged', 'b\n'),
942
('unchanged', 'c\n'),
943
('unchanged', 'd\n'),
945
('unchanged', 'g\n'),
947
('unchanged', 'e\n'),
948
('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'),
950
945
pwm = versionedfile.PlanWeaveMerge(plan)
951
self.assertEqualDiff('\n'.join('abcdghef') + '\n',
952
''.join(pwm.base_from_plan()))
946
self.assertEqualDiff(b'\n'.join(b'abcdghef') + b'\n',
947
b''.join(pwm.base_from_plan()))
953
948
# Reversing the order reverses the merge plan, and final order of 'hg'
955
plan = list(self.plan_merge_vf.plan_merge('E', 'D'))
950
plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
956
951
self.assertEqual([
957
('unchanged', 'a\n'),
958
('unchanged', 'b\n'),
959
('unchanged', 'c\n'),
960
('unchanged', 'd\n'),
962
('unchanged', 'h\n'),
964
('unchanged', 'e\n'),
965
('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'),
967
962
pwm = versionedfile.PlanWeaveMerge(plan)
968
self.assertEqualDiff('\n'.join('abcdhgef') + '\n',
969
''.join(pwm.base_from_plan()))
963
self.assertEqualDiff(b'\n'.join(b'abcdhgef') + b'\n',
964
b''.join(pwm.base_from_plan()))
970
965
# This is where lca differs, in that it (fairly correctly) determines
971
966
# that there is a conflict because both sides resolved the merge
973
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'))
974
969
self.assertEqual([
975
('unchanged', 'a\n'),
976
('unchanged', 'b\n'),
977
('unchanged', 'c\n'),
978
('unchanged', 'd\n'),
979
('conflicted-b', 'h\n'),
980
('unchanged', 'g\n'),
981
('conflicted-a', 'h\n'),
982
('unchanged', 'e\n'),
983
('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'),
985
980
pwm = versionedfile.PlanWeaveMerge(plan)
986
self.assertEqualDiff('\n'.join('abcdgef') + '\n',
987
''.join(pwm.base_from_plan()))
981
self.assertEqualDiff(b'\n'.join(b'abcdgef') + b'\n',
982
b''.join(pwm.base_from_plan()))
988
983
# Reversing it changes what line is doubled, but still gives a
989
984
# double-conflict
990
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'))
991
986
self.assertEqual([
992
('unchanged', 'a\n'),
993
('unchanged', 'b\n'),
994
('unchanged', 'c\n'),
995
('unchanged', 'd\n'),
996
('conflicted-b', 'g\n'),
997
('unchanged', 'h\n'),
998
('conflicted-a', 'g\n'),
999
('unchanged', 'e\n'),
1000
('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'),
1002
997
pwm = versionedfile.PlanWeaveMerge(plan)
1003
self.assertEqualDiff('\n'.join('abcdhef') + '\n',
1004
''.join(pwm.base_from_plan()))
998
self.assertEqualDiff(b'\n'.join(b'abcdhef') + b'\n',
999
b''.join(pwm.base_from_plan()))
1006
1001
def assertRemoveExternalReferences(self, filtered_parent_map,
1007
1002
child_map, tails, parent_map):
1077
1072
def test_subtract_plans(self):
1079
('unchanged', 'a\n'),
1081
('killed-a', 'c\n'),
1084
('killed-b', 'f\n'),
1085
('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'),
1088
('unchanged', 'a\n'),
1090
('killed-a', 'c\n'),
1093
('killed-b', 'f\n'),
1094
('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'),
1096
1091
subtracted_plan = [
1097
('unchanged', 'a\n'),
1099
('killed-a', 'c\n'),
1101
('unchanged', 'f\n'),
1102
('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'),
1104
1099
self.assertEqual(subtracted_plan,
1105
1100
list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1107
1102
def setup_merge_with_base(self):
1108
self.add_rev('root', 'COMMON', [], 'abc')
1109
self.add_rev('root', 'THIS', ['COMMON'], 'abcd')
1110
self.add_rev('root', 'BASE', ['COMMON'], 'eabc')
1111
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')
1113
1108
def test_plan_merge_with_base(self):
1114
1109
self.setup_merge_with_base()
1115
plan = self.plan_merge_vf.plan_merge('THIS', 'OTHER', 'BASE')
1116
self.assertEqual([('unchanged', 'a\n'),
1118
('unchanged', 'b\n'),
1119
('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'),
1123
1118
def test_plan_lca_merge(self):
1124
1119
self.setup_plan_merge()
1125
plan = self.plan_merge_vf.plan_lca_merge('B', 'C')
1120
plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1126
1121
self.assertEqual([
1128
('unchanged', 'a\n'),
1129
('killed-b', 'c\n'),
1132
('killed-a', 'b\n'),
1133
('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')],
1136
1131
def test_plan_lca_merge_uncommitted_files(self):
1137
1132
self.setup_plan_merge_uncommitted()
1138
plan = self.plan_merge_vf.plan_lca_merge('B:', 'C:')
1133
plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1139
1134
self.assertEqual([
1141
('unchanged', 'a\n'),
1142
('killed-b', 'c\n'),
1145
('killed-a', 'b\n'),
1146
('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')],
1149
1144
def test_plan_lca_merge_with_base(self):
1150
1145
self.setup_merge_with_base()
1151
plan = self.plan_merge_vf.plan_lca_merge('THIS', 'OTHER', 'BASE')
1152
self.assertEqual([('unchanged', 'a\n'),
1154
('unchanged', 'b\n'),
1155
('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'),
1159
1154
def test_plan_lca_merge_with_criss_cross(self):
1160
self.add_version(('root', 'ROOT'), [], 'abc')
1155
self.add_version((b'root', b'ROOT'), [], b'abc')
1161
1156
# each side makes a change
1162
self.add_version(('root', 'REV1'), [('root', 'ROOT')], 'abcd')
1163
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')
1164
1159
# both sides merge, discarding others' changes
1165
self.add_version(('root', 'LCA1'),
1166
[('root', 'REV1'), ('root', 'REV2')], 'abcd')
1167
self.add_version(('root', 'LCA2'),
1168
[('root', 'REV1'), ('root', 'REV2')], 'fabce')
1169
plan = self.plan_merge_vf.plan_lca_merge('LCA1', 'LCA2')
1170
self.assertEqual([('new-b', 'f\n'),
1171
('unchanged', 'a\n'),
1172
('unchanged', 'b\n'),
1173
('unchanged', 'c\n'),
1174
('conflicted-a', 'd\n'),
1175
('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'),
1178
1173
def test_plan_lca_merge_with_null(self):
1179
self.add_version(('root', 'A'), [], 'ab')
1180
self.add_version(('root', 'B'), [], 'bc')
1181
plan = self.plan_merge_vf.plan_lca_merge('A', 'B')
1182
self.assertEqual([('new-a', 'a\n'),
1183
('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'),
1187
1182
def test_plan_merge_with_delete_and_change(self):
1188
self.add_rev('root', 'C', [], 'a')
1189
self.add_rev('root', 'A', ['C'], 'b')
1190
self.add_rev('root', 'B', ['C'], '')
1191
plan = self.plan_merge_vf.plan_merge('A', 'B')
1192
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'),
1196
1191
def test_plan_merge_with_move_and_change(self):
1197
self.add_rev('root', 'C', [], 'abcd')
1198
self.add_rev('root', 'A', ['C'], 'acbd')
1199
self.add_rev('root', 'B', ['C'], 'aBcd')
1200
plan = self.plan_merge_vf.plan_merge('A', 'B')
1201
self.assertEqual([('unchanged', 'a\n'),
1203
('killed-b', 'b\n'),
1205
('killed-a', 'c\n'),
1206
('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'),
3205
3200
project_wt.lock_read()
3206
3201
self.addCleanup(project_wt.unlock)
3207
3202
# The r1-lib1 revision should be merged into this one
3208
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3203
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3209
3204
self.assertTreeEntriesEqual(
3210
[('', 'project-root-id'),
3211
('README', 'project-README-id'),
3212
('dir', 'project-dir-id'),
3213
('lib1', 'lib1-root-id'),
3214
('dir/file.c', 'project-file.c-id'),
3215
('lib1/Makefile', 'lib1-Makefile-id'),
3216
('lib1/README', 'lib1-README-id'),
3217
('lib1/foo.c', 'lib1-foo.c-id'),
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'),
3220
3215
def test_subdir(self):
3224
3219
project_wt.lock_read()
3225
3220
self.addCleanup(project_wt.unlock)
3226
3221
# The r1-lib1 revision should be merged into this one
3227
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3222
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3228
3223
self.assertTreeEntriesEqual(
3229
[('', 'project-root-id'),
3230
('README', 'project-README-id'),
3231
('dir', 'project-dir-id'),
3232
('dir/file.c', 'project-file.c-id'),
3233
('dir/lib1', 'lib1-root-id'),
3234
('dir/lib1/Makefile', 'lib1-Makefile-id'),
3235
('dir/lib1/README', 'lib1-README-id'),
3236
('dir/lib1/foo.c', 'lib1-foo.c-id'),
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'),
3239
3234
def test_newdir_with_repeat_roots(self):
3246
3241
project_wt.lock_read()
3247
3242
self.addCleanup(project_wt.unlock)
3248
3243
# The r1-lib1 revision should be merged into this one
3249
self.assertEqual(['r1-project', 'r1-lib1'], project_wt.get_parent_ids())
3244
self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
3250
3245
new_lib1_id = project_wt.path2id('lib1')
3251
3246
self.assertNotEqual(None, new_lib1_id)
3252
3247
self.assertTreeEntriesEqual(
3253
3248
[('', root_id),
3254
('README', 'project-README-id'),
3255
('dir', 'project-dir-id'),
3249
('README', b'project-README-id'),
3250
('dir', b'project-dir-id'),
3256
3251
('lib1', new_lib1_id),
3257
('dir/file.c', 'project-file.c-id'),
3258
('lib1/Makefile', 'lib1-Makefile-id'),
3259
('lib1/README', 'lib1-README-id'),
3260
('lib1/foo.c', 'lib1-foo.c-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'),
3263
3258
def test_name_conflict(self):