/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_merge.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-16 19:47:19 UTC
  • mfrom: (7178 work)
  • mto: This revision was merged to the branch mainline in revision 7179.
  • Revision ID: jelmer@jelmer.uk-20181116194719-m5ut2wfuze5x9s1p
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
109
109
        # are not related.
110
110
        wt1.merge_from_branch(wt2.branch, wt2.last_revision(), b'null:')
111
111
        self.assertEqual([br1.last_revision(), wt2.branch.last_revision()],
112
 
            wt1.get_parent_ids())
 
112
                         wt1.get_parent_ids())
113
113
        return (wt1, wt2.branch)
114
114
 
115
115
    def test_two_roots(self):
153
153
 
154
154
    def test_create_rename(self):
155
155
        """Rename an inventory entry while creating the file"""
156
 
        tree =self.make_branch_and_tree('.')
157
 
        with open('name1', 'wb') as f: f.write(b'Hello')
 
156
        tree = self.make_branch_and_tree('.')
 
157
        with open('name1', 'wb') as f:
 
158
            f.write(b'Hello')
158
159
        tree.add('name1')
159
160
        tree.commit(message="hello")
160
161
        tree.rename_one('name1', 'name2')
163
164
 
164
165
    def test_layered_rename(self):
165
166
        """Rename both child and parent at same time"""
166
 
        tree =self.make_branch_and_tree('.')
 
167
        tree = self.make_branch_and_tree('.')
167
168
        os.mkdir('dirname1')
168
169
        tree.add('dirname1')
169
170
        filename = pathjoin('dirname1', 'name1')
170
 
        with open(filename, 'wb') as f: f.write(b'Hello')
 
171
        with open(filename, 'wb') as f:
 
172
            f.write(b'Hello')
171
173
        tree.add(filename)
172
174
        tree.commit(message="hello")
173
175
        filename2 = pathjoin('dirname1', 'name2')
177
179
 
178
180
    def test_ignore_zero_merge_inner(self):
179
181
        # Test that merge_inner's ignore zero parameter is effective
180
 
        tree_a =self.make_branch_and_tree('a')
 
182
        tree_a = self.make_branch_and_tree('a')
181
183
        tree_a.commit(message="hello")
182
184
        dir_b = tree_a.controldir.sprout('b')
183
185
        tree_b = dir_b.open_workingtree()
187
189
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
188
190
                    this_tree=tree_b, ignore_zero=True)
189
191
        self.assertTrue('All changes applied successfully.\n' not in
190
 
            self.get_log())
 
192
                        self.get_log())
191
193
        tree_b.revert()
192
194
        merge_inner(tree_b.branch, tree_a, tree_b.basis_tree(),
193
195
                    this_tree=tree_b, ignore_zero=False)
194
 
        self.assertTrue('All changes applied successfully.\n' in self.get_log())
 
196
        self.assertTrue(
 
197
            'All changes applied successfully.\n' in self.get_log())
195
198
 
196
199
    def test_merge_inner_conflicts(self):
197
200
        tree_a = self.make_branch_and_tree('a')
207
210
        # basis_tree() is only guaranteed to be valid as long as it is actually
208
211
        # the basis tree. This mutates the tree after grabbing basis, so go to
209
212
        # the repository.
210
 
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
 
213
        base_tree = tree_a.branch.repository.revision_tree(
 
214
            tree_a.last_revision())
211
215
        tree_z = tree_a.controldir.sprout('z').open_workingtree()
212
216
        self.build_tree(['a/b/c'])
213
217
        tree_a.add('b/c')
230
234
        self.knownFailure(
231
235
            'iter_changes doesn\'t work with changes in nested trees')
232
236
        tree = self.make_branch_and_tree('tree',
233
 
            format='development-subtree')
 
237
                                         format='development-subtree')
234
238
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
235
 
            format='development-subtree')
 
239
                                             format='development-subtree')
236
240
        sub_tree.set_root_id(b'sub-tree-root')
237
241
        self.build_tree_contents([('tree/sub-tree/file', b'text1')])
238
242
        sub_tree.add('file')
256
260
        # basis_tree() is only guaranteed to be valid as long as it is actually
257
261
        # the basis tree. This test commits to the tree after grabbing basis,
258
262
        # so we go to the repository.
259
 
        base_tree = tree_a.branch.repository.revision_tree(tree_a.last_revision())
 
263
        base_tree = tree_a.branch.repository.revision_tree(
 
264
            tree_a.last_revision())
260
265
        tree_b = tree_a.controldir.sprout('tree_b').open_workingtree()
261
266
        self.build_tree_contents([('tree_a/file', b'content_2')])
262
267
        tree_a.commit('commit other')
284
289
        tree_b.merge_from_branch(tree_a.branch)
285
290
        self.assertEqual(tree_b.conflicts(),
286
291
                         [conflicts.ContentsConflict('file',
287
 
                          file_id=b'file-id')])
 
292
                                                     file_id=b'file-id')])
288
293
 
289
294
    def test_merge_type_registry(self):
290
295
        merge_type_option = option.Option.OPTIONS['merge-type']
291
296
        self.assertFalse('merge4' in [x[0] for x in
292
 
                        merge_type_option.iter_switches()])
 
297
                                      merge_type_option.iter_switches()])
293
298
        registry = _mod_merge.get_merge_type_registry()
294
299
        registry.register_lazy('merge4', 'breezy.merge', 'Merge4Merger',
295
300
                               'time-travelling merge')
296
301
        self.assertTrue('merge4' in [x[0] for x in
297
 
                        merge_type_option.iter_switches()])
 
302
                                     merge_type_option.iter_switches()])
298
303
        registry.remove('merge4')
299
304
        self.assertFalse('merge4' in [x[0] for x in
300
 
                        merge_type_option.iter_switches()])
 
305
                                      merge_type_option.iter_switches()])
301
306
 
302
307
    def test_merge_other_moves_we_deleted(self):
303
308
        tree_a = self.make_branch_and_tree('A')
426
431
        tree.add('a')
427
432
        first_rev = tree.commit("added a")
428
433
        merger = _mod_merge.Merger.from_revision_ids(tree,
429
 
                                          _mod_revision.NULL_REVISION,
430
 
                                          first_rev)
 
434
                                                     _mod_revision.NULL_REVISION,
 
435
                                                     first_rev)
431
436
        merger.merge_type = _mod_merge.Merge3Merger
432
437
        merger.interesting_files = 'a'
433
438
        conflict_count = merger.do_merge()
451
456
        tree_merger = merger.make_merger()
452
457
        self.assertIs(_mod_merge.Merge3Merger, tree_merger.__class__)
453
458
        self.assertEqual(b'rev2b',
454
 
            tree_merger.other_tree.get_revision_id())
 
459
                         tree_merger.other_tree.get_revision_id())
455
460
        self.assertEqual(b'rev1',
456
 
            tree_merger.base_tree.get_revision_id())
 
461
                         tree_merger.base_tree.get_revision_id())
457
462
        self.assertEqual(other_tree.branch, tree_merger.other_branch)
458
463
 
459
464
    def test_make_preview_transform(self):
509
514
        first_rev = tree.commit("added a")
510
515
        old_root_id = tree.get_root_id()
511
516
        merger = _mod_merge.Merger.from_revision_ids(tree,
512
 
                                          _mod_revision.NULL_REVISION,
513
 
                                          first_rev)
 
517
                                                     _mod_revision.NULL_REVISION,
 
518
                                                     first_rev)
514
519
        merger.merge_type = _mod_merge.Merge3Merger
515
520
        conflict_count = merger.do_merge()
516
521
        self.assertEqual(0, conflict_count)
524
529
        source.add('foo', b'foo-id')
525
530
        source.commit('Add foo')
526
531
        target = source.controldir.sprout('target').open_workingtree()
527
 
        subtree = target.extract('foo', b'foo-id')
 
532
        subtree = target.extract('foo')
528
533
        subtree.commit('Delete root')
529
534
        self.build_tree(['source/bar'])
530
535
        source.add('bar', b'bar-id')
561
566
        self.plan_merge_vf.fallback_versionedfiles.append(self.vf)
562
567
 
563
568
    def add_version(self, key, parents, text):
564
 
        self.vf.add_lines(key, parents, [int2byte(c)+b'\n' for c in bytearray(text)])
 
569
        self.vf.add_lines(key, parents, [int2byte(
 
570
            c) + b'\n' for c in bytearray(text)])
565
571
 
566
572
    def add_rev(self, prefix, revision_id, parents, text):
567
573
        self.add_version((prefix, revision_id), [(prefix, p) for p in parents],
569
575
 
570
576
    def add_uncommitted_version(self, key, parents, text):
571
577
        self.plan_merge_vf.add_lines(key, parents,
572
 
                                     [int2byte(c)+b'\n' for c in bytearray(text)])
 
578
                                     [int2byte(c) + b'\n' for c in bytearray(text)])
573
579
 
574
580
    def setup_plan_merge(self):
575
581
        self.add_rev(b'root', b'A', [], b'abc')
579
585
 
580
586
    def setup_plan_merge_uncommitted(self):
581
587
        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')
 
588
        self.add_uncommitted_version(
 
589
            (b'root', b'B:'), [(b'root', b'A')], b'acehg')
 
590
        self.add_uncommitted_version(
 
591
            (b'root', b'C:'), [(b'root', b'A')], b'fabg')
584
592
        return _PlanMerge(b'B:', b'C:', self.plan_merge_vf, (b'root',))
585
593
 
586
594
    def test_base_from_plan(self):
599
607
        self.setup_plan_merge()
600
608
        plan = self.plan_merge_vf.plan_merge(b'B', b'C')
601
609
        self.assertEqual([
602
 
                          ('new-b', b'f\n'),
603
 
                          ('unchanged', b'a\n'),
604
 
                          ('killed-a', b'b\n'),
605
 
                          ('killed-b', b'c\n'),
606
 
                          ('new-a', b'e\n'),
607
 
                          ('new-a', b'h\n'),
608
 
                          ('new-a', b'g\n'),
609
 
                          ('new-b', b'g\n')],
610
 
                         list(plan))
 
610
            ('new-b', b'f\n'),
 
611
            ('unchanged', b'a\n'),
 
612
            ('killed-a', b'b\n'),
 
613
            ('killed-b', b'c\n'),
 
614
            ('new-a', b'e\n'),
 
615
            ('new-a', b'h\n'),
 
616
            ('new-a', b'g\n'),
 
617
            ('new-b', b'g\n')],
 
618
            list(plan))
611
619
 
612
620
    def test_plan_merge_cherrypick(self):
613
621
        self.add_rev(b'root', b'A', [], b'abc')
618
626
        # We shortcut when one text supersedes the other in the per-file graph.
619
627
        # We don't actually need to compare the texts at this point.
620
628
        self.assertEqual([
621
 
                          ('new-b', b'a\n'),
622
 
                          ('new-b', b'b\n'),
623
 
                          ('new-b', b'c\n'),
624
 
                          ('new-b', b'd\n'),
625
 
                          ('new-b', b'e\n'),
626
 
                          ('new-b', b'g\n'),
627
 
                          ('new-b', b'h\n')],
628
 
                          list(my_plan.plan_merge()))
 
629
            ('new-b', b'a\n'),
 
630
            ('new-b', b'b\n'),
 
631
            ('new-b', b'c\n'),
 
632
            ('new-b', b'd\n'),
 
633
            ('new-b', b'e\n'),
 
634
            ('new-b', b'g\n'),
 
635
            ('new-b', b'h\n')],
 
636
            list(my_plan.plan_merge()))
629
637
 
630
638
    def test_plan_merge_no_common_ancestor(self):
631
639
        self.add_rev(b'root', b'A', [], b'abc')
632
640
        self.add_rev(b'root', b'B', [], b'xyz')
633
641
        my_plan = _PlanMerge(b'A', b'B', self.plan_merge_vf, (b'root',))
634
642
        self.assertEqual([
635
 
                          ('new-a', b'a\n'),
636
 
                          ('new-a', b'b\n'),
637
 
                          ('new-a', b'c\n'),
638
 
                          ('new-b', b'x\n'),
639
 
                          ('new-b', b'y\n'),
640
 
                          ('new-b', b'z\n')],
641
 
                          list(my_plan.plan_merge()))
 
643
            ('new-a', b'a\n'),
 
644
            ('new-a', b'b\n'),
 
645
            ('new-a', b'c\n'),
 
646
            ('new-b', b'x\n'),
 
647
            ('new-b', b'y\n'),
 
648
            ('new-b', b'z\n')],
 
649
            list(my_plan.plan_merge()))
642
650
 
643
651
    def test_plan_merge_tail_ancestors(self):
644
652
        # The graph looks like this:
681
689
        self.add_rev(b'root', b'J', [b'H', b'G'], b'DaJbCcF')
682
690
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
683
691
        self.assertEqual([
684
 
                          ('unchanged', b'D\n'),
685
 
                          ('unchanged', b'a\n'),
686
 
                          ('killed-b', b'B\n'),
687
 
                          ('new-b', b'J\n'),
688
 
                          ('unchanged', b'b\n'),
689
 
                          ('unchanged', b'C\n'),
690
 
                          ('unchanged', b'c\n'),
691
 
                          ('unchanged', b'F\n')],
692
 
                         list(plan))
 
692
            ('unchanged', b'D\n'),
 
693
            ('unchanged', b'a\n'),
 
694
            ('killed-b', b'B\n'),
 
695
            ('new-b', b'J\n'),
 
696
            ('unchanged', b'b\n'),
 
697
            ('unchanged', b'C\n'),
 
698
            ('unchanged', b'c\n'),
 
699
            ('unchanged', b'F\n')],
 
700
            list(plan))
693
701
 
694
702
    def test_plan_merge_tail_triple_ancestors(self):
695
703
        # The graph looks like this:
723
731
        self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DaJbCcF')
724
732
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
725
733
        self.assertEqual([
726
 
                          ('unchanged', b'D\n'),
727
 
                          ('unchanged', b'a\n'),
728
 
                          ('killed-b', b'B\n'),
729
 
                          ('new-b', b'J\n'),
730
 
                          ('unchanged', b'b\n'),
731
 
                          ('unchanged', b'C\n'),
732
 
                          ('unchanged', b'c\n'),
733
 
                          ('unchanged', b'F\n')],
734
 
                         list(plan))
 
734
            ('unchanged', b'D\n'),
 
735
            ('unchanged', b'a\n'),
 
736
            ('killed-b', b'B\n'),
 
737
            ('new-b', b'J\n'),
 
738
            ('unchanged', b'b\n'),
 
739
            ('unchanged', b'C\n'),
 
740
            ('unchanged', b'c\n'),
 
741
            ('unchanged', b'F\n')],
 
742
            list(plan))
735
743
 
736
744
    def test_plan_merge_2_tail_triple_ancestors(self):
737
745
        # The graph looks like this:
762
770
        self.add_rev(b'root', b'J', [b'H', b'Q', b'G'], b'DabcdJfF')
763
771
        plan = self.plan_merge_vf.plan_merge(b'I', b'J')
764
772
        self.assertEqual([
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'),
771
 
                          ('new-b', b'J\n'),
772
 
                          ('unchanged', b'f\n'),
773
 
                          ('unchanged', b'F\n')],
774
 
                         list(plan))
 
773
            ('unchanged', b'D\n'),
 
774
            ('unchanged', b'a\n'),
 
775
            ('unchanged', b'b\n'),
 
776
            ('unchanged', b'c\n'),
 
777
            ('unchanged', b'd\n'),
 
778
            ('killed-b', b'e\n'),
 
779
            ('new-b', b'J\n'),
 
780
            ('unchanged', b'f\n'),
 
781
            ('unchanged', b'F\n')],
 
782
            list(plan))
775
783
 
776
784
    def test_plan_merge_uncommitted_files(self):
777
785
        self.setup_plan_merge_uncommitted()
778
786
        plan = self.plan_merge_vf.plan_merge(b'B:', b'C:')
779
787
        self.assertEqual([
780
 
                          ('new-b', b'f\n'),
781
 
                          ('unchanged', b'a\n'),
782
 
                          ('killed-a', b'b\n'),
783
 
                          ('killed-b', b'c\n'),
784
 
                          ('new-a', b'e\n'),
785
 
                          ('new-a', b'h\n'),
786
 
                          ('new-a', b'g\n'),
787
 
                          ('new-b', b'g\n')],
788
 
                         list(plan))
 
788
            ('new-b', b'f\n'),
 
789
            ('unchanged', b'a\n'),
 
790
            ('killed-a', b'b\n'),
 
791
            ('killed-b', b'c\n'),
 
792
            ('new-a', b'e\n'),
 
793
            ('new-a', b'h\n'),
 
794
            ('new-a', b'g\n'),
 
795
            ('new-b', b'g\n')],
 
796
            list(plan))
789
797
 
790
798
    def test_plan_merge_insert_order(self):
791
799
        """Weave merges are sensitive to the order of insertion.
802
810
        # If we get the ordering wrong, these will look like new lines in D,
803
811
        # rather than carried over from B, C
804
812
        self.add_rev(b'root', b'D', [b'B', b'C'],
805
 
                         b'abwxyzcdef')
 
813
                     b'abwxyzcdef')
806
814
        # Supersede the lines in B and delete the lines in C, which will
807
815
        # conflict if they are treated as being in D
808
816
        self.add_rev(b'root', b'E', [b'C', b'B'],
809
 
                         b'abnocdef')
 
817
                     b'abnocdef')
810
818
        # Same thing for the lines in C
811
819
        self.add_rev(b'root', b'F', [b'C'], b'abpqcdef')
812
820
        plan = self.plan_merge_vf.plan_merge(b'D', b'E')
813
821
        self.assertEqual([
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'),
820
 
                          ('new-b', b'n\n'),
821
 
                          ('new-b', b'o\n'),
822
 
                          ('unchanged', b'c\n'),
823
 
                          ('unchanged', b'd\n'),
824
 
                          ('unchanged', b'e\n'),
825
 
                          ('unchanged', b'f\n')],
826
 
                         list(plan))
 
822
            ('unchanged', b'a\n'),
 
823
            ('unchanged', b'b\n'),
 
824
            ('killed-b', b'w\n'),
 
825
            ('killed-b', b'x\n'),
 
826
            ('killed-b', b'y\n'),
 
827
            ('killed-b', b'z\n'),
 
828
            ('new-b', b'n\n'),
 
829
            ('new-b', b'o\n'),
 
830
            ('unchanged', b'c\n'),
 
831
            ('unchanged', b'd\n'),
 
832
            ('unchanged', b'e\n'),
 
833
            ('unchanged', b'f\n')],
 
834
            list(plan))
827
835
        plan = self.plan_merge_vf.plan_merge(b'E', b'D')
828
836
        # Going in the opposite direction shows the effect of the opposite plan
829
837
        self.assertEqual([
830
 
                          ('unchanged', b'a\n'),
831
 
                          ('unchanged', b'b\n'),
832
 
                          ('new-b', b'w\n'),
833
 
                          ('new-b', b'x\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'),
838
 
                          ('new-a', b'n\n'),
839
 
                          ('new-a', b'o\n'),
840
 
                          ('unchanged', b'c\n'),
841
 
                          ('unchanged', b'd\n'),
842
 
                          ('unchanged', b'e\n'),
843
 
                          ('unchanged', b'f\n')],
844
 
                         list(plan))
 
838
            ('unchanged', b'a\n'),
 
839
            ('unchanged', b'b\n'),
 
840
            ('new-b', b'w\n'),
 
841
            ('new-b', b'x\n'),
 
842
            ('killed-a', b'y\n'),
 
843
            ('killed-a', b'z\n'),
 
844
            ('killed-both', b'w\n'),
 
845
            ('killed-both', b'x\n'),
 
846
            ('new-a', b'n\n'),
 
847
            ('new-a', b'o\n'),
 
848
            ('unchanged', b'c\n'),
 
849
            ('unchanged', b'd\n'),
 
850
            ('unchanged', b'e\n'),
 
851
            ('unchanged', b'f\n')],
 
852
            list(plan))
845
853
 
846
854
    def test_plan_merge_criss_cross(self):
847
855
        # This is specificly trying to trigger problems when using limited
880
888
        self.add_rev(b'root', b'G', [b'C', b'D', b'E'], b'hazcdyfg')
881
889
        plan = self.plan_merge_vf.plan_merge(b'F', b'G')
882
890
        self.assertEqual([
883
 
                          ('unchanged', b'h\n'),
884
 
                          ('unchanged', b'a\n'),
885
 
                          ('killed-base', b'b\n'),
886
 
                          ('killed-b', b'x\n'),
887
 
                          ('new-b', b'z\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')],
894
 
                         list(plan))
 
891
            ('unchanged', b'h\n'),
 
892
            ('unchanged', b'a\n'),
 
893
            ('killed-base', b'b\n'),
 
894
            ('killed-b', b'x\n'),
 
895
            ('new-b', b'z\n'),
 
896
            ('unchanged', b'c\n'),
 
897
            ('unchanged', b'd\n'),
 
898
            ('killed-base', b'e\n'),
 
899
            ('unchanged', b'y\n'),
 
900
            ('unchanged', b'f\n'),
 
901
            ('unchanged', b'g\n')],
 
902
            list(plan))
895
903
        plan = self.plan_merge_vf.plan_lca_merge(b'F', b'G')
896
904
        # This is one of the main differences between plan_merge and
897
905
        # plan_lca_merge. plan_lca_merge generates a conflict for 'x => z',
898
906
        # because 'x' was not present in one of the bases. However, in this
899
907
        # case it is spurious because 'x' does not exist in the global base A.
900
908
        self.assertEqual([
901
 
                          ('unchanged', b'h\n'),
902
 
                          ('unchanged', b'a\n'),
903
 
                          ('conflicted-a', b'x\n'),
904
 
                          ('new-b', b'z\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')],
910
 
                         list(plan))
 
909
            ('unchanged', b'h\n'),
 
910
            ('unchanged', b'a\n'),
 
911
            ('conflicted-a', b'x\n'),
 
912
            ('new-b', b'z\n'),
 
913
            ('unchanged', b'c\n'),
 
914
            ('unchanged', b'd\n'),
 
915
            ('unchanged', b'y\n'),
 
916
            ('unchanged', b'f\n'),
 
917
            ('unchanged', b'g\n')],
 
918
            list(plan))
911
919
 
912
920
    def test_criss_cross_flip_flop(self):
913
921
        # This is specificly trying to trigger problems when using limited
932
940
        self.add_rev(b'root', b'E', [b'C', b'B'], b'abcdhgef')
933
941
        plan = list(self.plan_merge_vf.plan_merge(b'D', b'E'))
934
942
        self.assertEqual([
935
 
                          ('unchanged', b'a\n'),
936
 
                          ('unchanged', b'b\n'),
937
 
                          ('unchanged', b'c\n'),
938
 
                          ('unchanged', b'd\n'),
939
 
                          ('new-b', b'h\n'),
940
 
                          ('unchanged', b'g\n'),
941
 
                          ('killed-b', b'h\n'),
942
 
                          ('unchanged', b'e\n'),
943
 
                          ('unchanged', b'f\n'),
944
 
                         ], plan)
 
943
            ('unchanged', b'a\n'),
 
944
            ('unchanged', b'b\n'),
 
945
            ('unchanged', b'c\n'),
 
946
            ('unchanged', b'd\n'),
 
947
            ('new-b', b'h\n'),
 
948
            ('unchanged', b'g\n'),
 
949
            ('killed-b', b'h\n'),
 
950
            ('unchanged', b'e\n'),
 
951
            ('unchanged', b'f\n'),
 
952
            ], plan)
945
953
        pwm = versionedfile.PlanWeaveMerge(plan)
946
954
        self.assertEqualDiff(b'a\nb\nc\nd\ng\nh\ne\nf\n',
947
955
                             b''.join(pwm.base_from_plan()))
949
957
        # => 'gh'
950
958
        plan = list(self.plan_merge_vf.plan_merge(b'E', b'D'))
951
959
        self.assertEqual([
952
 
                          ('unchanged', b'a\n'),
953
 
                          ('unchanged', b'b\n'),
954
 
                          ('unchanged', b'c\n'),
955
 
                          ('unchanged', b'd\n'),
956
 
                          ('new-b', b'g\n'),
957
 
                          ('unchanged', b'h\n'),
958
 
                          ('killed-b', b'g\n'),
959
 
                          ('unchanged', b'e\n'),
960
 
                          ('unchanged', b'f\n'),
961
 
                         ], plan)
 
960
            ('unchanged', b'a\n'),
 
961
            ('unchanged', b'b\n'),
 
962
            ('unchanged', b'c\n'),
 
963
            ('unchanged', b'd\n'),
 
964
            ('new-b', b'g\n'),
 
965
            ('unchanged', b'h\n'),
 
966
            ('killed-b', b'g\n'),
 
967
            ('unchanged', b'e\n'),
 
968
            ('unchanged', b'f\n'),
 
969
            ], plan)
962
970
        pwm = versionedfile.PlanWeaveMerge(plan)
963
971
        self.assertEqualDiff(b'a\nb\nc\nd\nh\ng\ne\nf\n',
964
972
                             b''.join(pwm.base_from_plan()))
967
975
        # differently
968
976
        plan = list(self.plan_merge_vf.plan_lca_merge(b'D', b'E'))
969
977
        self.assertEqual([
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'),
979
 
                         ], plan)
 
978
            ('unchanged', b'a\n'),
 
979
            ('unchanged', b'b\n'),
 
980
            ('unchanged', b'c\n'),
 
981
            ('unchanged', b'd\n'),
 
982
            ('conflicted-b', b'h\n'),
 
983
            ('unchanged', b'g\n'),
 
984
            ('conflicted-a', b'h\n'),
 
985
            ('unchanged', b'e\n'),
 
986
            ('unchanged', b'f\n'),
 
987
            ], plan)
980
988
        pwm = versionedfile.PlanWeaveMerge(plan)
981
989
        self.assertEqualDiff(b'a\nb\nc\nd\ng\ne\nf\n',
982
990
                             b''.join(pwm.base_from_plan()))
984
992
        # double-conflict
985
993
        plan = list(self.plan_merge_vf.plan_lca_merge(b'E', b'D'))
986
994
        self.assertEqual([
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'),
996
 
                         ], plan)
 
995
            ('unchanged', b'a\n'),
 
996
            ('unchanged', b'b\n'),
 
997
            ('unchanged', b'c\n'),
 
998
            ('unchanged', b'd\n'),
 
999
            ('conflicted-b', b'g\n'),
 
1000
            ('unchanged', b'h\n'),
 
1001
            ('conflicted-a', b'g\n'),
 
1002
            ('unchanged', b'e\n'),
 
1003
            ('unchanged', b'f\n'),
 
1004
            ], plan)
997
1005
        pwm = versionedfile.PlanWeaveMerge(plan)
998
1006
        self.assertEqualDiff(b'a\nb\nc\nd\nh\ne\nf\n',
999
1007
                             b''.join(pwm.base_from_plan()))
1017
1025
    def test__remove_external_references(self):
1018
1026
        # First, nothing to remove
1019
1027
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
1020
 
            {1: [2], 2: [3], 3: []}, [1], {3: [2], 2: [1], 1: []})
 
1028
                                            {1: [2], 2: [3], 3: []}, [1], {3: [2], 2: [1], 1: []})
1021
1029
        # The reverse direction
1022
1030
        self.assertRemoveExternalReferences({1: [2], 2: [3], 3: []},
1023
 
            {3: [2], 2: [1], 1: []}, [3], {1: [2], 2: [3], 3: []})
 
1031
                                            {3: [2], 2: [1], 1: []}, [3], {1: [2], 2: [3], 3: []})
1024
1032
        # Extra references
1025
1033
        self.assertRemoveExternalReferences({3: [2], 2: [1], 1: []},
1026
 
            {1: [2], 2: [3], 3: []}, [1], {3: [2, 4], 2: [1, 5], 1: [6]})
 
1034
                                            {1: [2], 2: [3], 3: []}, [1], {3: [2, 4], 2: [1, 5], 1: [6]})
1027
1035
        # Multiple tails
1028
1036
        self.assertRemoveExternalReferences(
1029
1037
            {4: [2, 3], 3: [], 2: [1], 1: []},
1060
1068
        self.assertPruneTails({1: []}, [5],
1061
1069
                              {1: [], 2: [3, 4], 3: [5], 4: [5], 5: []})
1062
1070
        # Prune a partial chain
1063
 
        self.assertPruneTails({1: [6], 6:[]}, [5],
 
1071
        self.assertPruneTails({1: [6], 6: []}, [5],
1064
1072
                              {1: [2, 6], 2: [3, 4], 3: [5], 4: [5], 5: [],
1065
1073
                               6: []})
1066
1074
        # Prune a chain with multiple tips, that pulls out intermediates
1067
 
        self.assertPruneTails({1:[3], 3:[]}, [4, 5],
1068
 
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
1069
 
        self.assertPruneTails({1:[3], 3:[]}, [5, 4],
1070
 
                              {1: [2, 3], 2: [4, 5], 3: [], 4:[], 5:[]})
 
1075
        self.assertPruneTails({1: [3], 3: []}, [4, 5],
 
1076
                              {1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
 
1077
        self.assertPruneTails({1: [3], 3: []}, [5, 4],
 
1078
                              {1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []})
1071
1079
 
1072
1080
    def test_subtract_plans(self):
1073
1081
        old_plan = [
1074
 
        ('unchanged', b'a\n'),
1075
 
        ('new-a', b'b\n'),
1076
 
        ('killed-a', b'c\n'),
1077
 
        ('new-b', b'd\n'),
1078
 
        ('new-b', b'e\n'),
1079
 
        ('killed-b', b'f\n'),
1080
 
        ('killed-b', b'g\n'),
 
1082
            ('unchanged', b'a\n'),
 
1083
            ('new-a', b'b\n'),
 
1084
            ('killed-a', b'c\n'),
 
1085
            ('new-b', b'd\n'),
 
1086
            ('new-b', b'e\n'),
 
1087
            ('killed-b', b'f\n'),
 
1088
            ('killed-b', b'g\n'),
1081
1089
        ]
1082
1090
        new_plan = [
1083
 
        ('unchanged', b'a\n'),
1084
 
        ('new-a', b'b\n'),
1085
 
        ('killed-a', b'c\n'),
1086
 
        ('new-b', b'd\n'),
1087
 
        ('new-b', b'h\n'),
1088
 
        ('killed-b', b'f\n'),
1089
 
        ('killed-b', b'i\n'),
 
1091
            ('unchanged', b'a\n'),
 
1092
            ('new-a', b'b\n'),
 
1093
            ('killed-a', b'c\n'),
 
1094
            ('new-b', b'd\n'),
 
1095
            ('new-b', b'h\n'),
 
1096
            ('killed-b', b'f\n'),
 
1097
            ('killed-b', b'i\n'),
1090
1098
        ]
1091
1099
        subtracted_plan = [
1092
 
        ('unchanged', b'a\n'),
1093
 
        ('new-a', b'b\n'),
1094
 
        ('killed-a', b'c\n'),
1095
 
        ('new-b', b'h\n'),
1096
 
        ('unchanged', b'f\n'),
1097
 
        ('killed-b', b'i\n'),
 
1100
            ('unchanged', b'a\n'),
 
1101
            ('new-a', b'b\n'),
 
1102
            ('killed-a', b'c\n'),
 
1103
            ('new-b', b'h\n'),
 
1104
            ('unchanged', b'f\n'),
 
1105
            ('killed-b', b'i\n'),
1098
1106
        ]
1099
1107
        self.assertEqual(subtracted_plan,
1100
 
            list(_PlanMerge._subtract_plans(old_plan, new_plan)))
 
1108
                         list(_PlanMerge._subtract_plans(old_plan, new_plan)))
1101
1109
 
1102
1110
    def setup_merge_with_base(self):
1103
1111
        self.add_rev(b'root', b'COMMON', [], b'abc')
1113
1121
                          ('unchanged', b'b\n'),
1114
1122
                          ('killed-b', b'c\n'),
1115
1123
                          ('new-a', b'd\n')
1116
 
                         ], list(plan))
 
1124
                          ], list(plan))
1117
1125
 
1118
1126
    def test_plan_lca_merge(self):
1119
1127
        self.setup_plan_merge()
1120
1128
        plan = self.plan_merge_vf.plan_lca_merge(b'B', b'C')
1121
1129
        self.assertEqual([
1122
 
                          ('new-b', b'f\n'),
1123
 
                          ('unchanged', b'a\n'),
1124
 
                          ('killed-b', b'c\n'),
1125
 
                          ('new-a', b'e\n'),
1126
 
                          ('new-a', b'h\n'),
1127
 
                          ('killed-a', b'b\n'),
1128
 
                          ('unchanged', b'g\n')],
1129
 
                         list(plan))
 
1130
            ('new-b', b'f\n'),
 
1131
            ('unchanged', b'a\n'),
 
1132
            ('killed-b', b'c\n'),
 
1133
            ('new-a', b'e\n'),
 
1134
            ('new-a', b'h\n'),
 
1135
            ('killed-a', b'b\n'),
 
1136
            ('unchanged', b'g\n')],
 
1137
            list(plan))
1130
1138
 
1131
1139
    def test_plan_lca_merge_uncommitted_files(self):
1132
1140
        self.setup_plan_merge_uncommitted()
1133
1141
        plan = self.plan_merge_vf.plan_lca_merge(b'B:', b'C:')
1134
1142
        self.assertEqual([
1135
 
                          ('new-b', b'f\n'),
1136
 
                          ('unchanged', b'a\n'),
1137
 
                          ('killed-b', b'c\n'),
1138
 
                          ('new-a', b'e\n'),
1139
 
                          ('new-a', b'h\n'),
1140
 
                          ('killed-a', b'b\n'),
1141
 
                          ('unchanged', b'g\n')],
1142
 
                         list(plan))
 
1143
            ('new-b', b'f\n'),
 
1144
            ('unchanged', b'a\n'),
 
1145
            ('killed-b', b'c\n'),
 
1146
            ('new-a', b'e\n'),
 
1147
            ('new-a', b'h\n'),
 
1148
            ('killed-a', b'b\n'),
 
1149
            ('unchanged', b'g\n')],
 
1150
            list(plan))
1143
1151
 
1144
1152
    def test_plan_lca_merge_with_base(self):
1145
1153
        self.setup_merge_with_base()
1149
1157
                          ('unchanged', b'b\n'),
1150
1158
                          ('killed-b', b'c\n'),
1151
1159
                          ('new-a', b'd\n')
1152
 
                         ], list(plan))
 
1160
                          ], list(plan))
1153
1161
 
1154
1162
    def test_plan_lca_merge_with_criss_cross(self):
1155
1163
        self.add_version((b'root', b'ROOT'), [], b'abc')
1158
1166
        self.add_version((b'root', b'REV2'), [(b'root', b'ROOT')], b'abce')
1159
1167
        # both sides merge, discarding others' changes
1160
1168
        self.add_version((b'root', b'LCA1'),
1161
 
            [(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
 
1169
                         [(b'root', b'REV1'), (b'root', b'REV2')], b'abcd')
1162
1170
        self.add_version((b'root', b'LCA2'),
1163
 
            [(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
 
1171
                         [(b'root', b'REV1'), (b'root', b'REV2')], b'fabce')
1164
1172
        plan = self.plan_merge_vf.plan_lca_merge(b'LCA1', b'LCA2')
1165
1173
        self.assertEqual([('new-b', b'f\n'),
1166
1174
                          ('unchanged', b'a\n'),
1168
1176
                          ('unchanged', b'c\n'),
1169
1177
                          ('conflicted-a', b'd\n'),
1170
1178
                          ('conflicted-b', b'e\n'),
1171
 
                         ], list(plan))
 
1179
                          ], list(plan))
1172
1180
 
1173
1181
    def test_plan_lca_merge_with_null(self):
1174
1182
        self.add_version((b'root', b'A'), [], b'ab')
1177
1185
        self.assertEqual([('new-a', b'a\n'),
1178
1186
                          ('unchanged', b'b\n'),
1179
1187
                          ('new-b', b'c\n'),
1180
 
                         ], list(plan))
 
1188
                          ], list(plan))
1181
1189
 
1182
1190
    def test_plan_merge_with_delete_and_change(self):
1183
1191
        self.add_rev(b'root', b'C', [], b'a')
1186
1194
        plan = self.plan_merge_vf.plan_merge(b'A', b'B')
1187
1195
        self.assertEqual([('killed-both', b'a\n'),
1188
1196
                          ('new-a', b'b\n'),
1189
 
                         ], list(plan))
 
1197
                          ], list(plan))
1190
1198
 
1191
1199
    def test_plan_merge_with_move_and_change(self):
1192
1200
        self.add_rev(b'root', b'C', [], b'abcd')
1199
1207
                          ('new-b', b'B\n'),
1200
1208
                          ('killed-a', b'c\n'),
1201
1209
                          ('unchanged', b'd\n'),
1202
 
                         ], list(plan))
 
1210
                          ], list(plan))
1203
1211
 
1204
1212
 
1205
1213
class LoggingMerger(object):
1236
1244
        #
1237
1245
        builder = self.get_builder()
1238
1246
        builder.build_snapshot(None,
1239
 
            [('add', ('', None, 'directory', None))],
1240
 
            revision_id=b'A-id' )
 
1247
                               [('add', ('', None, 'directory', None))],
 
1248
                               revision_id=b'A-id')
1241
1249
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1242
1250
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1243
1251
        return builder
1304
1312
        self.assertEqual(b'A-id', merger.base_rev_id)
1305
1313
        self.assertTrue(merger._is_criss_cross)
1306
1314
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1307
 
                                            for t in merger._lca_trees])
 
1315
                                              for t in merger._lca_trees])
1308
1316
        # If we swap the order, we should get a different lca order
1309
1317
        builder.build_snapshot([b'E-id'], [], revision_id=b'F-id')
1310
1318
        merger = self.make_Merger(builder, b'D-id')
1311
1319
        self.assertEqual([b'C-id', b'B-id'], [t.get_revision_id()
1312
 
                                            for t in merger._lca_trees])
 
1320
                                              for t in merger._lca_trees])
1313
1321
 
1314
1322
    def test_find_base_triple_criss_cross(self):
1315
1323
        #       A-.
1337
1345
        # C   D
1338
1346
        builder = self.get_builder()
1339
1347
        builder.build_snapshot(None,
1340
 
            [('add', ('', None, 'directory', None))],
1341
 
            revision_id=b'A-id')
 
1348
                               [('add', ('', None, 'directory', None))],
 
1349
                               revision_id=b'A-id')
1342
1350
        builder.build_snapshot([],
1343
 
            [('add', ('', None, 'directory', None))],
1344
 
            revision_id=b'B-id')
 
1351
                               [('add', ('', None, 'directory', None))],
 
1352
                               revision_id=b'B-id')
1345
1353
        builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'D-id')
1346
1354
        builder.build_snapshot([b'A-id', b'B-id'], [], revision_id=b'C-id')
1347
1355
        merger = self.make_Merger(builder, b'D-id')
1348
1356
        self.assertEqual(b'A-id', merger.base_rev_id)
1349
1357
        self.assertTrue(merger._is_criss_cross)
1350
1358
        self.assertEqual([b'A-id', b'B-id'], [t.get_revision_id()
1351
 
                                            for t in merger._lca_trees])
 
1359
                                              for t in merger._lca_trees])
1352
1360
 
1353
1361
    def test_no_criss_cross_passed_to_merge_type(self):
1354
1362
        class LCATreesMerger(LoggingMerger):
1365
1373
        merger.merge_type = _mod_merge.Merge3Merger
1366
1374
        merge_obj = merger.make_merger()
1367
1375
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1368
 
                                            for t in merger._lca_trees])
 
1376
                                              for t in merger._lca_trees])
1369
1377
 
1370
1378
    def test_criss_cross_not_supported_merge_type(self):
1371
1379
        merger = self.make_Merger(self.setup_criss_cross_graph(), b'E-id')
1391
1399
    def make_merge_obj(self, builder, other_revision_id,
1392
1400
                       interesting_files=None):
1393
1401
        merger = self.make_Merger(builder, other_revision_id,
1394
 
            interesting_files=interesting_files)
 
1402
                                  interesting_files=interesting_files)
1395
1403
        return merger.make_merger()
1396
1404
 
1397
1405
    def test_simple(self):
1398
1406
        builder = self.get_builder()
1399
1407
        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')
 
1408
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1409
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1410
                               revision_id=b'A-id')
 
1411
        builder.build_snapshot([b'A-id'],
 
1412
                               [('modify', ('a', b'a\nb\nC\nc\n'))],
 
1413
                               revision_id=b'C-id')
 
1414
        builder.build_snapshot([b'A-id'],
 
1415
                               [('modify', ('a', b'a\nB\nb\nc\n'))],
 
1416
                               revision_id=b'B-id')
1409
1417
        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')
 
1418
                               [('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
 
1419
                               revision_id=b'E-id')
1412
1420
        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', )
 
1421
                               [('modify', ('a', b'a\nB\nb\nC\nc\n'))],
 
1422
                               revision_id=b'D-id', )
1415
1423
        merge_obj = self.make_merge_obj(builder, b'E-id')
1416
1424
 
1417
1425
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1418
 
                                            for t in merge_obj._lca_trees])
 
1426
                                              for t in merge_obj._lca_trees])
1419
1427
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1420
1428
        entries = list(merge_obj._entries_lca())
1421
1429
 
1427
1435
                           ((root_id, [root_id, root_id]), root_id, root_id),
1428
1436
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1429
1437
                           ((False, [False, False]), False, False)),
1430
 
                         ], entries)
 
1438
                          ], entries)
1431
1439
 
1432
1440
    def test_not_in_base(self):
1433
1441
        # LCAs all have the same last-modified revision for the file, as do
1443
1451
 
1444
1452
        builder = self.get_builder()
1445
1453
        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
                               [('add', (u'', b'a-root-id', 'directory', None))],
 
1455
                               revision_id=b'A-id')
 
1456
        builder.build_snapshot([b'A-id'],
 
1457
                               [('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
1458
                               revision_id=b'B-id')
 
1459
        builder.build_snapshot([b'A-id'],
 
1460
                               [('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
 
1461
                               revision_id=b'C-id')
1454
1462
        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')
 
1463
                               [('add', (u'bar', b'bar-id', 'file', b'd\ne\nf\n'))],
 
1464
                               revision_id=b'D-id')
1457
1465
        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')
 
1466
                               [('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
1467
                               revision_id=b'E-id')
1460
1468
        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')
 
1469
                               [('modify', (u'bar', b'd\ne\nf\nG\n'))],
 
1470
                               revision_id=b'G-id')
1463
1471
        builder.build_snapshot([b'D-id', b'E-id'], [], revision_id=b'F-id')
1464
1472
        merge_obj = self.make_merge_obj(builder, b'G-id')
1465
1473
 
1466
1474
        self.assertEqual([b'D-id', b'E-id'], [t.get_revision_id()
1467
 
                                            for t in merge_obj._lca_trees])
 
1475
                                              for t in merge_obj._lca_trees])
1468
1476
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1469
1477
        entries = list(merge_obj._entries_lca())
1470
1478
        root_id = b'a-root-id'
1473
1481
                           ((None, [root_id, root_id]), root_id, root_id),
1474
1482
                           ((None, [u'bar', u'bar']), u'bar', u'bar'),
1475
1483
                           ((None, [False, False]), False, False)),
1476
 
                         ], entries)
 
1484
                          ], entries)
1477
1485
 
1478
1486
    def test_not_in_this(self):
1479
1487
        builder = self.get_builder()
1480
1488
        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')
 
1489
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1490
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1491
                               revision_id=b'A-id')
 
1492
        builder.build_snapshot([b'A-id'],
 
1493
                               [('modify', ('a', b'a\nB\nb\nc\n'))],
 
1494
                               revision_id=b'B-id')
 
1495
        builder.build_snapshot([b'A-id'],
 
1496
                               [('modify', ('a', b'a\nb\nC\nc\n'))],
 
1497
                               revision_id=b'C-id')
1490
1498
        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')
 
1499
                               [('modify', ('a', b'a\nB\nb\nC\nc\nE\n'))],
 
1500
                               revision_id=b'E-id')
1493
1501
        builder.build_snapshot([b'B-id', b'C-id'],
1494
 
            [('unversion', 'a')],
1495
 
            revision_id=b'D-id')
 
1502
                               [('unversion', 'a')],
 
1503
                               revision_id=b'D-id')
1496
1504
        merge_obj = self.make_merge_obj(builder, b'E-id')
1497
1505
 
1498
1506
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1499
 
                                            for t in merge_obj._lca_trees])
 
1507
                                              for t in merge_obj._lca_trees])
1500
1508
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1501
1509
 
1502
1510
        entries = list(merge_obj._entries_lca())
1506
1514
                           ((root_id, [root_id, root_id]), root_id, None),
1507
1515
                           ((u'a', [u'a', u'a']), u'a', None),
1508
1516
                           ((False, [False, False]), False, None)),
1509
 
                         ], entries)
 
1517
                          ], entries)
1510
1518
 
1511
1519
    def test_file_not_in_one_lca(self):
1512
1520
        #   A   # just root
1516
1524
        #   D E # D and E both have the file, unchanged from C
1517
1525
        builder = self.get_builder()
1518
1526
        builder.build_snapshot(None,
1519
 
            [('add', (u'', b'a-root-id', 'directory', None))],
1520
 
            revision_id=b'A-id')
 
1527
                               [('add', (u'', b'a-root-id', 'directory', None))],
 
1528
                               revision_id=b'A-id')
1521
1529
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1522
1530
        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')
 
1531
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1532
                               revision_id=b'C-id')
1525
1533
        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')
 
1534
                               [], revision_id=b'E-id')  # Inherited from C
 
1535
        builder.build_snapshot([b'B-id', b'C-id'],  # Merged from C
 
1536
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1537
                               revision_id=b'D-id')
1530
1538
        merge_obj = self.make_merge_obj(builder, b'E-id')
1531
1539
 
1532
1540
        self.assertEqual([b'B-id', b'C-id'], [t.get_revision_id()
1533
 
                                            for t in merge_obj._lca_trees])
 
1541
                                              for t in merge_obj._lca_trees])
1534
1542
        self.assertEqual(b'A-id', merge_obj.base_tree.get_revision_id())
1535
1543
 
1536
1544
        entries = list(merge_obj._entries_lca())
1539
1547
    def test_not_in_other(self):
1540
1548
        builder = self.get_builder()
1541
1549
        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')
 
1550
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1551
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1552
                               revision_id=b'A-id')
1545
1553
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1546
1554
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1547
1555
        builder.build_snapshot(
1548
 
                [b'C-id', b'B-id'],
1549
 
                [('unversion', 'a')], revision_id=b'E-id')
 
1556
            [b'C-id', b'B-id'],
 
1557
            [('unversion', 'a')], revision_id=b'E-id')
1550
1558
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1551
1559
        merge_obj = self.make_merge_obj(builder, b'E-id')
1552
1560
 
1557
1565
                           ((root_id, [root_id, root_id]), None, root_id),
1558
1566
                           ((u'a', [u'a', u'a']), None, u'a'),
1559
1567
                           ((False, [False, False]), None, False)),
1560
 
                         ], entries)
 
1568
                          ], entries)
1561
1569
 
1562
1570
    def test_not_in_other_or_lca(self):
1563
1571
        #       A    base, introduces 'foo'
1573
1581
        # D would then win 'cleanly' and no record would be given
1574
1582
        builder = self.get_builder()
1575
1583
        builder.build_snapshot(None,
1576
 
            [('add', (u'', b'a-root-id', 'directory', None)),
1577
 
             ('add', (u'foo', b'foo-id', 'file', b'content\n'))],
1578
 
            revision_id=b'A-id')
 
1584
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1585
                                ('add', (u'foo', b'foo-id', 'file', b'content\n'))],
 
1586
                               revision_id=b'A-id')
1579
1587
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1580
1588
        builder.build_snapshot([b'A-id'],
1581
 
            [('unversion', 'foo')], revision_id=b'C-id')
 
1589
                               [('unversion', 'foo')], revision_id=b'C-id')
1582
1590
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1583
1591
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1584
1592
        merge_obj = self.make_merge_obj(builder, b'E-id')
1603
1611
        # picked C and D picked B, so we should issue a conflict
1604
1612
        builder = self.get_builder()
1605
1613
        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')
 
1614
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1615
                                ('add', (u'foo', b'foo-id', 'file', b'content\n'))],
 
1616
                               revision_id=b'A-id')
1609
1617
        builder.build_snapshot([b'A-id'], [
1610
1618
            ('modify', ('foo', b'new-content\n'))],
1611
1619
            revision_id=b'B-id')
1612
1620
        builder.build_snapshot([b'A-id'],
1613
 
            [('unversion', 'foo')],
1614
 
            revision_id=b'C-id')
 
1621
                               [('unversion', 'foo')],
 
1622
                               revision_id=b'C-id')
1615
1623
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1616
1624
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1617
1625
        merge_obj = self.make_merge_obj(builder, b'E-id')
1623
1631
                           ((root_id, [root_id, None]), None, root_id),
1624
1632
                           ((u'foo', [u'foo', None]), None, 'foo'),
1625
1633
                           ((False, [False, None]), None, False)),
1626
 
                         ], entries)
 
1634
                          ], entries)
1627
1635
 
1628
1636
    def test_only_in_one_lca(self):
1629
1637
        #   A   add only root
1643
1651
        #   w/ C=BASE, D=THIS, E=OTHER we have 'happy convergence'
1644
1652
        builder = self.get_builder()
1645
1653
        builder.build_snapshot(None,
1646
 
            [('add', (u'', b'a-root-id', 'directory', None))],
1647
 
            revision_id=b'A-id')
 
1654
                               [('add', (u'', b'a-root-id', 'directory', None))],
 
1655
                               revision_id=b'A-id')
1648
1656
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1649
1657
        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')
 
1658
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1659
                               revision_id=b'C-id')
1652
1660
        builder.build_snapshot([b'C-id', b'B-id'],
1653
 
            [('unversion', 'a')],
1654
 
            revision_id=b'E-id')
 
1661
                               [('unversion', 'a')],
 
1662
                               revision_id=b'E-id')
1655
1663
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1656
1664
        merge_obj = self.make_merge_obj(builder, b'E-id')
1657
1665
 
1661
1669
    def test_only_in_other(self):
1662
1670
        builder = self.get_builder()
1663
1671
        builder.build_snapshot(None,
1664
 
            [('add', (u'', b'a-root-id', 'directory', None))],
1665
 
            revision_id=b'A-id')
 
1672
                               [('add', (u'', b'a-root-id', 'directory', None))],
 
1673
                               revision_id=b'A-id')
1666
1674
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1667
1675
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1668
1676
        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')
 
1677
                               [('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
1678
                               revision_id=b'E-id')
1671
1679
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1672
1680
        merge_obj = self.make_merge_obj(builder, b'E-id')
1673
1681
 
1678
1686
                           ((None, [None, None]), root_id, None),
1679
1687
                           ((None, [None, None]), u'a', None),
1680
1688
                           ((None, [None, None]), False, None)),
1681
 
                         ], entries)
 
1689
                          ], entries)
1682
1690
 
1683
1691
    def test_one_lca_supersedes(self):
1684
1692
        # One LCA supersedes the other LCAs last modified value, but the
1696
1704
        #   completely supersedes the value in D.
1697
1705
        builder = self.get_builder()
1698
1706
        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')
 
1707
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1708
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1709
                               revision_id=b'A-id')
1702
1710
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1703
1711
        builder.build_snapshot([b'A-id'],
1704
 
            [('modify', ('foo', b'B content\n'))],
1705
 
            revision_id=b'B-id')
 
1712
                               [('modify', ('foo', b'B content\n'))],
 
1713
                               revision_id=b'B-id')
1706
1714
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1707
1715
        builder.build_snapshot([b'C-id', b'B-id'],
1708
 
            [('modify', ('foo', b'E content\n'))],
1709
 
            revision_id=b'E-id')
 
1716
                               [('modify', ('foo', b'E content\n'))],
 
1717
                               revision_id=b'E-id')
1710
1718
        builder.build_snapshot([b'E-id', b'D-id'], [], revision_id=b'G-id')
1711
1719
        builder.build_snapshot([b'D-id', b'E-id'],
1712
 
            [('modify', ('foo', b'F content\n'))],
1713
 
            revision_id=b'F-id')
 
1720
                               [('modify', ('foo', b'F content\n'))],
 
1721
                               revision_id=b'F-id')
1714
1722
        merge_obj = self.make_merge_obj(builder, b'G-id')
1715
1723
 
1716
1724
        self.assertEqual([], list(merge_obj._entries_lca()))
1746
1754
        #
1747
1755
        builder = self.get_builder()
1748
1756
        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')
 
1757
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1758
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1759
                               revision_id=b'A-id')
1752
1760
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1753
1761
        builder.build_snapshot([b'A-id'],
1754
 
            [('rename', ('foo', 'bar'))],
1755
 
            revision_id=b'B-id')
 
1762
                               [('rename', ('foo', 'bar'))],
 
1763
                               revision_id=b'B-id')
1756
1764
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1757
1765
        builder.build_snapshot([b'C-id', b'B-id'],
1758
 
            [('rename', ('foo', 'bing'))],
1759
 
            revision_id=b'E-id') # override to bing
 
1766
                               [('rename', ('foo', 'bing'))],
 
1767
                               revision_id=b'E-id')  # override to bing
1760
1768
        builder.build_snapshot([b'E-id', b'D-id'],
1761
 
            [('rename', ('bing', 'barry'))],
1762
 
            revision_id=b'G-id') # override to barry
 
1769
                               [('rename', ('bing', 'barry'))],
 
1770
                               revision_id=b'G-id')  # override to barry
1763
1771
        builder.build_snapshot([b'D-id', b'E-id'],
1764
 
            [('rename', ('bar', 'bing'))],
1765
 
            revision_id=b'F-id') # Merge in E's change
 
1772
                               [('rename', ('bar', 'bing'))],
 
1773
                               revision_id=b'F-id')  # Merge in E's change
1766
1774
        merge_obj = self.make_merge_obj(builder, b'G-id')
1767
1775
 
1768
1776
        self.expectFailure("We don't do an actual heads() check on lca values,"
1769
 
            " or use the per-attribute graph",
1770
 
            self.assertEqual, [], list(merge_obj._entries_lca()))
 
1777
                           " or use the per-attribute graph",
 
1778
                           self.assertEqual, [], list(merge_obj._entries_lca()))
1771
1779
 
1772
1780
    def test_one_lca_accidentally_pruned(self):
1773
1781
        # Another incorrect resolution from the same basic flaw:
1784
1792
        # (superseding B).
1785
1793
        builder = self.get_builder()
1786
1794
        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')
 
1795
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1796
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1797
                               revision_id=b'A-id')
1790
1798
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1791
1799
        builder.build_snapshot([b'A-id'],
1792
 
            [('rename', ('foo', 'bar'))],
1793
 
            revision_id=b'B-id')
 
1800
                               [('rename', ('foo', 'bar'))],
 
1801
                               revision_id=b'B-id')
1794
1802
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1795
1803
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1796
1804
        builder.build_snapshot([b'E-id', b'D-id'],
1797
 
            [('rename', ('foo', 'bar'))],
1798
 
            revision_id=b'G-id')
 
1805
                               [('rename', ('foo', 'bar'))],
 
1806
                               revision_id=b'G-id')
1799
1807
        builder.build_snapshot([b'D-id', b'E-id'],
1800
 
            [('rename', ('bar', 'bing'))],
1801
 
            revision_id=b'F-id') # should end up conflicting
 
1808
                               [('rename', ('bar', 'bing'))],
 
1809
                               revision_id=b'F-id')  # should end up conflicting
1802
1810
        merge_obj = self.make_merge_obj(builder, b'G-id')
1803
1811
 
1804
1812
        entries = list(merge_obj._entries_lca())
1805
1813
        root_id = b'a-root-id'
1806
1814
        self.expectFailure("We prune values from BASE even when relevant.",
1807
 
            self.assertEqual,
1808
 
                [(b'foo-id', False,
1809
 
                  ((root_id, [root_id, root_id]), root_id, root_id),
1810
 
                  ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
1811
 
                  ((False, [False, False]), False, False)),
1812
 
                ], entries)
 
1815
                           self.assertEqual,
 
1816
                           [(b'foo-id', False,
 
1817
                             ((root_id, [root_id, root_id]), root_id, root_id),
 
1818
                               ((u'foo', [u'bar', u'foo']), u'bar', u'bing'),
 
1819
                               ((False, [False, False]), False, False)),
 
1820
                            ], entries)
1813
1821
 
1814
1822
    def test_both_sides_revert(self):
1815
1823
        # Both sides of a criss-cross revert the text to the lca
1821
1829
        # This should conflict
1822
1830
        builder = self.get_builder()
1823
1831
        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')
 
1832
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1833
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1834
                               revision_id=b'A-id')
 
1835
        builder.build_snapshot([b'A-id'],
 
1836
                               [('modify', ('foo', b'B content\n'))],
 
1837
                               revision_id=b'B-id')
 
1838
        builder.build_snapshot([b'A-id'],
 
1839
                               [('modify', ('foo', b'C content\n'))],
 
1840
                               revision_id=b'C-id')
1833
1841
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1834
1842
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1835
1843
        merge_obj = self.make_merge_obj(builder, b'E-id')
1841
1849
                           ((root_id, [root_id, root_id]), root_id, root_id),
1842
1850
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1843
1851
                           ((False, [False, False]), False, False)),
1844
 
                         ], entries)
 
1852
                          ], entries)
1845
1853
 
1846
1854
    def test_different_lca_resolve_one_side_updates_content(self):
1847
1855
        # Both sides converge, but then one side updates the text.
1856
1864
        # merge resolution
1857
1865
        builder = self.get_builder()
1858
1866
        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', )
 
1867
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1868
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1869
                               revision_id=b'A-id')
 
1870
        builder.build_snapshot([b'A-id'],
 
1871
                               [('modify', ('foo', b'B content\n'))],
 
1872
                               revision_id=b'B-id')
 
1873
        builder.build_snapshot([b'A-id'],
 
1874
                               [('modify', ('foo', b'C content\n'))],
 
1875
                               revision_id=b'C-id', )
1868
1876
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1869
1877
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1870
1878
        builder.build_snapshot([b'D-id'],
1871
 
            [('modify', ('foo', b'F content\n'))],
1872
 
            revision_id=b'F-id')
 
1879
                               [('modify', ('foo', b'F content\n'))],
 
1880
                               revision_id=b'F-id')
1873
1881
        merge_obj = self.make_merge_obj(builder, b'E-id')
1874
1882
 
1875
1883
        entries = list(merge_obj._entries_lca())
1879
1887
                           ((root_id, [root_id, root_id]), root_id, root_id),
1880
1888
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
1881
1889
                           ((False, [False, False]), False, False)),
1882
 
                         ], entries)
 
1890
                          ], entries)
1883
1891
 
1884
1892
    def test_same_lca_resolution_one_side_updates_content(self):
1885
1893
        # Both sides converge, but then one side updates the text.
1897
1905
 
1898
1906
        builder = self.get_builder()
1899
1907
        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')
 
1908
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1909
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
1910
                               revision_id=b'A-id')
 
1911
        builder.build_snapshot([b'A-id'],
 
1912
                               [('modify', ('foo', b'B content\n'))],
 
1913
                               revision_id=b'B-id')
 
1914
        builder.build_snapshot([b'A-id'],
 
1915
                               [('modify', ('foo', b'C content\n'))],
 
1916
                               revision_id=b'C-id')
1909
1917
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1910
1918
        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
 
1919
                               [('modify', ('foo', b'C content\n'))],
 
1920
                               revision_id=b'D-id')  # Same as E
1913
1921
        builder.build_snapshot([b'D-id'],
1914
 
            [('modify', ('foo', b'F content\n'))],
1915
 
            revision_id=b'F-id')
 
1922
                               [('modify', ('foo', b'F content\n'))],
 
1923
                               revision_id=b'F-id')
1916
1924
        merge_obj = self.make_merge_obj(builder, b'E-id')
1917
1925
 
1918
1926
        entries = list(merge_obj._entries_lca())
1919
1927
        self.expectFailure("We don't detect that LCA resolution was the"
1920
1928
                           " same on both sides",
1921
 
            self.assertEqual, [], entries)
 
1929
                           self.assertEqual, [], entries)
1922
1930
 
1923
1931
    def test_only_path_changed(self):
1924
1932
        builder = self.get_builder()
1925
1933
        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')
 
1934
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1935
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
 
1936
                               revision_id=b'A-id')
1929
1937
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1930
1938
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1931
1939
        builder.build_snapshot([b'C-id', b'B-id'],
1932
 
            [('rename', (u'a', u'b'))],
1933
 
            revision_id=b'E-id')
 
1940
                               [('rename', (u'a', u'b'))],
 
1941
                               revision_id=b'E-id')
1934
1942
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1935
1943
        merge_obj = self.make_merge_obj(builder, b'E-id')
1936
1944
        entries = list(merge_obj._entries_lca())
1941
1949
                           ((root_id, [root_id, root_id]), root_id, root_id),
1942
1950
                           ((u'a', [u'a', u'a']), u'b', u'a'),
1943
1951
                           ((False, [False, False]), False, False)),
1944
 
                         ], entries)
 
1952
                          ], entries)
1945
1953
 
1946
1954
    def test_kind_changed(self):
1947
1955
        # Identical content, except 'D' changes a-id into a directory
1948
1956
        builder = self.get_builder()
1949
1957
        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')
 
1958
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1959
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
 
1960
                               revision_id=b'A-id')
1953
1961
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1954
1962
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1955
1963
        builder.build_snapshot([b'C-id', b'B-id'],
1956
 
            [('unversion', 'a'),
1957
 
             ('flush', None),
1958
 
             ('add', (u'a', b'a-id', 'directory', None))],
1959
 
            revision_id=b'E-id')
 
1964
                               [('unversion', 'a'),
 
1965
                                ('flush', None),
 
1966
                                ('add', (u'a', b'a-id', 'directory', None))],
 
1967
                               revision_id=b'E-id')
1960
1968
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
1961
1969
        merge_obj = self.make_merge_obj(builder, b'E-id')
1962
1970
        entries = list(merge_obj._entries_lca())
1967
1975
                           ((root_id, [root_id, root_id]), root_id, root_id),
1968
1976
                           ((u'a', [u'a', u'a']), u'a', u'a'),
1969
1977
                           ((False, [False, False]), False, False)),
1970
 
                         ], entries)
 
1978
                          ], entries)
1971
1979
 
1972
1980
    def test_this_changed_kind(self):
1973
1981
        # Identical content, but THIS changes a file to a directory
1974
1982
        builder = self.get_builder()
1975
1983
        builder.build_snapshot(None,
1976
 
            [('add', (u'', b'a-root-id', 'directory', None)),
1977
 
             ('add', (u'a', b'a-id', 'file', b'content\n'))],
1978
 
            revision_id=b'A-id')
 
1984
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
1985
                                ('add', (u'a', b'a-id', 'file', b'content\n'))],
 
1986
                               revision_id=b'A-id')
1979
1987
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
1980
1988
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
1981
1989
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
1982
1990
        builder.build_snapshot([b'B-id', b'C-id'],
1983
 
            [('unversion', 'a'),
1984
 
             ('flush', None),
1985
 
             ('add', (u'a', b'a-id', 'directory', None))],
1986
 
            revision_id=b'D-id')
 
1991
                               [('unversion', 'a'),
 
1992
                                ('flush', None),
 
1993
                                ('add', (u'a', b'a-id', 'directory', None))],
 
1994
                               revision_id=b'D-id')
1987
1995
        merge_obj = self.make_merge_obj(builder, b'E-id')
1988
1996
        entries = list(merge_obj._entries_lca())
1989
1997
        # Only the kind was changed (content)
1993
2001
        # Two files modified, but we should filter one of them
1994
2002
        builder = self.get_builder()
1995
2003
        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')
 
2004
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2005
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
 
2006
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
 
2007
                               revision_id=b'A-id')
2000
2008
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2001
2009
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2002
2010
        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')
 
2011
                               [('modify', ('a', b'new-content\n')),
 
2012
                                ('modify', ('b', b'new-content\n'))],
 
2013
                               revision_id=b'E-id')
2006
2014
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2007
2015
        merge_obj = self.make_merge_obj(builder, b'E-id',
2008
2016
                                        interesting_files=['b'])
2013
2021
                           ((root_id, [root_id, root_id]), root_id, root_id),
2014
2022
                           ((u'b', [u'b', u'b']), u'b', u'b'),
2015
2023
                           ((False, [False, False]), False, False)),
2016
 
                         ], entries)
 
2024
                          ], entries)
2017
2025
 
2018
2026
    def test_interesting_file_in_this(self):
2019
2027
        # This renamed the file, but it should still match the entry in other
2020
2028
        builder = self.get_builder()
2021
2029
        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')
 
2030
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2031
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
 
2032
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
 
2033
                               revision_id=b'A-id')
2026
2034
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2027
2035
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2028
2036
        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')
 
2037
                               [('modify', ('a', b'new-content\n')),
 
2038
                                ('modify', ('b', b'new-content\n'))],
 
2039
                               revision_id=b'E-id')
2032
2040
        builder.build_snapshot([b'B-id', b'C-id'],
2033
 
            [('rename', ('b', 'c'))],
2034
 
            revision_id=b'D-id')
 
2041
                               [('rename', ('b', 'c'))],
 
2042
                               revision_id=b'D-id')
2035
2043
        merge_obj = self.make_merge_obj(builder, b'E-id',
2036
2044
                                        interesting_files=['c'])
2037
2045
        entries = list(merge_obj._entries_lca())
2041
2049
                           ((root_id, [root_id, root_id]), root_id, root_id),
2042
2050
                           ((u'b', [u'b', u'b']), u'b', u'c'),
2043
2051
                           ((False, [False, False]), False, False)),
2044
 
                         ], entries)
 
2052
                          ], entries)
2045
2053
 
2046
2054
    def test_interesting_file_in_base(self):
2047
2055
        # This renamed the file, but it should still match the entry in BASE
2048
2056
        builder = self.get_builder()
2049
2057
        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')
 
2058
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2059
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
 
2060
                                   ('add', (u'c', b'c-id', 'file', b'content\n'))],
 
2061
                               revision_id=b'A-id')
 
2062
        builder.build_snapshot([b'A-id'],
 
2063
                               [('rename', ('c', 'b'))],
 
2064
                               revision_id=b'B-id')
 
2065
        builder.build_snapshot([b'A-id'],
 
2066
                               [('rename', ('c', 'b'))],
 
2067
                               revision_id=b'C-id')
2060
2068
        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')
 
2069
                               [('modify', ('a', b'new-content\n')),
 
2070
                                ('modify', ('b', b'new-content\n'))],
 
2071
                               revision_id=b'E-id')
2064
2072
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2065
2073
        merge_obj = self.make_merge_obj(builder, b'E-id',
2066
2074
                                        interesting_files=['c'])
2071
2079
                           ((root_id, [root_id, root_id]), root_id, root_id),
2072
2080
                           ((u'c', [u'b', u'b']), u'b', u'b'),
2073
2081
                           ((False, [False, False]), False, False)),
2074
 
                         ], entries)
 
2082
                          ], entries)
2075
2083
 
2076
2084
    def test_interesting_file_in_lca(self):
2077
2085
        # This renamed the file, but it should still match the entry in LCA
2078
2086
        builder = self.get_builder()
2079
2087
        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')
 
2088
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2089
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
 
2090
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
 
2091
                               revision_id=b'A-id')
2084
2092
        builder.build_snapshot([b'A-id'],
2085
 
            [('rename', ('b', 'c'))], revision_id=b'B-id')
 
2093
                               [('rename', ('b', 'c'))], revision_id=b'B-id')
2086
2094
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2087
2095
        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')
 
2096
                               [('modify', ('a', b'new-content\n')),
 
2097
                                ('modify', ('b', b'new-content\n'))],
 
2098
                               revision_id=b'E-id')
2091
2099
        builder.build_snapshot([b'B-id', b'C-id'],
2092
 
            [('rename', ('c', 'b'))], revision_id=b'D-id')
 
2100
                               [('rename', ('c', 'b'))], revision_id=b'D-id')
2093
2101
        merge_obj = self.make_merge_obj(builder, b'E-id',
2094
2102
                                        interesting_files=['c'])
2095
2103
        entries = list(merge_obj._entries_lca())
2099
2107
                           ((root_id, [root_id, root_id]), root_id, root_id),
2100
2108
                           ((u'b', [u'c', u'b']), u'b', u'b'),
2101
2109
                           ((False, [False, False]), False, False)),
2102
 
                         ], entries)
 
2110
                          ], entries)
2103
2111
 
2104
2112
    def test_interesting_files(self):
2105
2113
        # Two files modified, but we should filter one of them
2106
2114
        builder = self.get_builder()
2107
2115
        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')
 
2116
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2117
                                ('add', (u'a', b'a-id', 'file', b'content\n')),
 
2118
                                   ('add', (u'b', b'b-id', 'file', b'content\n'))],
 
2119
                               revision_id=b'A-id')
2112
2120
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2113
2121
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2114
2122
        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')
 
2123
                               [('modify', ('a', b'new-content\n')),
 
2124
                                ('modify', ('b', b'new-content\n'))], revision_id=b'E-id')
2117
2125
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2118
2126
        merge_obj = self.make_merge_obj(builder, b'E-id',
2119
2127
                                        interesting_files=['b'])
2124
2132
                           ((root_id, [root_id, root_id]), root_id, root_id),
2125
2133
                           ((u'b', [u'b', u'b']), u'b', u'b'),
2126
2134
                           ((False, [False, False]), False, False)),
2127
 
                         ], entries)
2128
 
 
 
2135
                          ], entries)
2129
2136
 
2130
2137
 
2131
2138
class TestMergerEntriesLCAOnDisk(tests.TestCaseWithTransport):
2159
2166
    def test_simple_lca(self):
2160
2167
        builder = self.get_builder()
2161
2168
        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')
 
2169
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2170
                                ('add', (u'a', b'a-id', 'file', b'a\nb\nc\n'))],
 
2171
                               revision_id=b'A-id')
2165
2172
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2166
2173
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2167
2174
        builder.build_snapshot([b'C-id', b'B-id'], [], revision_id=b'E-id')
2168
2175
        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')
 
2176
                               [('modify', ('a', b'a\nb\nc\nd\ne\nf\n'))],
 
2177
                               revision_id=b'D-id')
2171
2178
        wt, conflicts = self.do_merge(builder, b'E-id')
2172
2179
        self.assertEqual(0, conflicts)
2173
2180
        # The merge should have simply update the contents of 'a'
2187
2194
        #     F     Path at 'baz' in F, which supersedes 'bar' and 'foo'
2188
2195
        builder = self.get_builder()
2189
2196
        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')
 
2197
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2198
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
2199
                               revision_id=b'A-id')
2193
2200
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2194
2201
        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')
 
2202
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id', )
 
2203
        builder.build_snapshot([b'C-id', b'B-id'],  # merge the rename
 
2204
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')
2198
2205
        builder.build_snapshot([b'E-id'],
2199
 
            [('rename', ('bar', 'baz'))], revision_id=b'F-id')
 
2206
                               [('rename', ('bar', 'baz'))], revision_id=b'F-id')
2200
2207
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2201
2208
        wt, conflicts = self.do_merge(builder, b'F-id')
2202
2209
        self.assertEqual(0, conflicts)
2218
2225
        #     F     F deletes 'bar'
2219
2226
        builder = self.get_builder()
2220
2227
        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')
 
2228
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2229
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
2230
                               revision_id=b'A-id')
2224
2231
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2225
2232
        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')
 
2233
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id')
 
2234
        builder.build_snapshot([b'C-id', b'B-id'],  # merge the rename
 
2235
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')
2229
2236
        builder.build_snapshot([b'E-id'],
2230
 
            [('unversion', 'bar')], revision_id=b'F-id')
 
2237
                               [('unversion', 'bar')], revision_id=b'F-id')
2231
2238
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2232
2239
        wt, conflicts = self.do_merge(builder, b'F-id')
2233
2240
        self.assertEqual(0, conflicts)
2245
2252
        #     F     Executable bit changed
2246
2253
        builder = self.get_builder()
2247
2254
        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')
 
2255
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2256
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
2257
                               revision_id=b'A-id')
2251
2258
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2252
2259
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2253
2260
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2287
2294
        # have symlink support
2288
2295
        builder = self.get_builder()
2289
2296
        builder.build_snapshot(None,
2290
 
            [('add', (u'', b'a-root-id', 'directory', None))],
2291
 
            revision_id=b'A-id')
 
2297
                               [('add', (u'', b'a-root-id', 'directory', None))],
 
2298
                               revision_id=b'A-id')
2292
2299
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2293
2300
        builder.build_snapshot([b'A-id'], [], revision_id=b'B-id')
2294
2301
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2321
2328
        # inventory contains "None" rather than a real sha1
2322
2329
        builder = self.get_builder()
2323
2330
        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')
 
2331
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2332
                                ('add', (u'foo', b'foo-id', 'file', b'A content\n'))],
 
2333
                               revision_id=b'A-id')
 
2334
        builder.build_snapshot([b'A-id'],
 
2335
                               [('modify', ('foo', b'B content\n'))],
 
2336
                               revision_id=b'B-id')
 
2337
        builder.build_snapshot([b'A-id'],
 
2338
                               [('modify', ('foo', b'C content\n'))],
 
2339
                               revision_id=b'C-id')
2333
2340
        builder.build_snapshot([b'C-id', b'B-id'], [],
2334
 
                revision_id=b'E-id')
 
2341
                               revision_id=b'E-id')
2335
2342
        builder.build_snapshot([b'B-id', b'C-id'], [],
2336
 
                revision_id=b'D-id')
 
2343
                               revision_id=b'D-id')
2337
2344
        wt, conflicts = self.do_merge(builder, b'E-id')
2338
2345
        self.assertEqual(1, conflicts)
2339
2346
        self.assertEqualDiff(b'<<<<<<< TREE\n'
2439
2446
                           ((root_id, [root_id, root_id]), root_id, root_id),
2440
2447
                           ((u'foo', [u'barry', u'foo']), u'blah', u'barry'),
2441
2448
                           ((False, [False, False]), False, False)),
2442
 
                         ], entries)
 
2449
                          ], entries)
2443
2450
        conflicts = wt.merge_from_branch(wt.branch, to_revision=b'F-id')
2444
2451
        self.assertEqual(0, conflicts)
2445
2452
        self.assertEqual('blah', wt.id2path(b'foo-id'))
2544
2551
                           ((None, [root_id, None]), root_id, root_id),
2545
2552
                           ((None, [u'foo', None]), u'foo', u'foo'),
2546
2553
                           ((None, [False, None]), False, False)),
2547
 
                         ], entries)
 
2554
                          ], entries)
2548
2555
 
2549
2556
    def test_symlink_all_wt(self):
2550
2557
        """Check behavior if all trees are Working Trees."""
2598
2605
        wt_other.lock_read()
2599
2606
        self.addCleanup(wt_other.unlock)
2600
2607
        merge_obj = _mod_merge.Merge3Merger(wt, wt, wt_base,
2601
 
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
 
2608
                                            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2602
2609
        entries = list(merge_obj._entries_lca())
2603
2610
        root_id = wt.path2id('')
2604
2611
        self.assertEqual([(b'foo-id', True,
2606
2613
                           ((root_id, [root_id, root_id]), root_id, root_id),
2607
2614
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2608
2615
                           ((False, [False, False]), False, False)),
2609
 
                         ], entries)
 
2616
                          ], entries)
2610
2617
 
2611
2618
    def test_other_reverted_path_to_base(self):
2612
2619
        #   A       Path at 'foo'
2620
2627
        #     F     Path at 'foo'
2621
2628
        builder = self.get_builder()
2622
2629
        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')
 
2630
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2631
                                ('add', (u'foo', b'foo-id', 'file', b'a\nb\nc\n'))],
 
2632
                               revision_id=b'A-id')
2626
2633
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2627
2634
        builder.build_snapshot([b'A-id'],
2628
 
            [('rename', ('foo', 'bar'))], revision_id=b'B-id')
 
2635
                               [('rename', ('foo', 'bar'))], revision_id=b'B-id')
2629
2636
        builder.build_snapshot([b'C-id', b'B-id'],
2630
 
            [('rename', ('foo', 'bar'))], revision_id=b'E-id') # merge the rename
 
2637
                               [('rename', ('foo', 'bar'))], revision_id=b'E-id')  # merge the rename
2631
2638
        builder.build_snapshot([b'E-id'],
2632
 
            [('rename', ('bar', 'foo'))], revision_id=b'F-id') # Rename back to BASE
 
2639
                               [('rename', ('bar', 'foo'))], revision_id=b'F-id')  # Rename back to BASE
2633
2640
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2634
2641
        wt, conflicts = self.do_merge(builder, b'F-id')
2635
2642
        self.assertEqual(0, conflicts)
2638
2645
    def test_other_reverted_content_to_base(self):
2639
2646
        builder = self.get_builder()
2640
2647
        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')
 
2648
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2649
                                ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
 
2650
                               revision_id=b'A-id')
2644
2651
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2645
2652
        builder.build_snapshot([b'A-id'],
2646
 
            [('modify', ('foo', b'B content\n'))],
2647
 
            revision_id=b'B-id')
 
2653
                               [('modify', ('foo', b'B content\n'))],
 
2654
                               revision_id=b'B-id')
2648
2655
        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
 
2656
                               [('modify', ('foo', b'B content\n'))],
 
2657
                               revision_id=b'E-id')  # merge the content
2651
2658
        builder.build_snapshot([b'E-id'],
2652
 
            [('modify', ('foo', b'base content\n'))],
2653
 
            revision_id=b'F-id') # Revert back to BASE
 
2659
                               [('modify', ('foo', b'base content\n'))],
 
2660
                               revision_id=b'F-id')  # Revert back to BASE
2654
2661
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2655
2662
        wt, conflicts = self.do_merge(builder, b'F-id')
2656
2663
        self.assertEqual(0, conflicts)
2662
2669
    def test_other_modified_content(self):
2663
2670
        builder = self.get_builder()
2664
2671
        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')
 
2672
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2673
                                ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
 
2674
                               revision_id=b'A-id')
2668
2675
        builder.build_snapshot([b'A-id'], [], revision_id=b'C-id')
2669
2676
        builder.build_snapshot([b'A-id'],
2670
 
            [('modify', ('foo', b'B content\n'))],
2671
 
            revision_id=b'B-id')
 
2677
                               [('modify', ('foo', b'B content\n'))],
 
2678
                               revision_id=b'B-id')
2672
2679
        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
 
2680
                               [('modify', ('foo', b'B content\n'))],
 
2681
                               revision_id=b'E-id')  # merge the content
2675
2682
        builder.build_snapshot([b'E-id'],
2676
 
            [('modify', ('foo', b'F content\n'))],
2677
 
            revision_id=b'F-id') # Override B content
 
2683
                               [('modify', ('foo', b'F content\n'))],
 
2684
                               revision_id=b'F-id')  # Override B content
2678
2685
        builder.build_snapshot([b'B-id', b'C-id'], [], revision_id=b'D-id')
2679
2686
        wt, conflicts = self.do_merge(builder, b'F-id')
2680
2687
        self.assertEqual(0, conflicts)
2692
2699
        #   D E E updates content, renames 'b' => 'c'
2693
2700
        builder = self.get_builder()
2694
2701
        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')
 
2702
                               [('add', (u'', b'a-root-id', 'directory', None)),
 
2703
                                ('add', (u'a', b'a-id', 'file', b'base content\n')),
 
2704
                                   ('add', (u'foo', b'foo-id', 'file', b'base content\n'))],
 
2705
                               revision_id=b'A-id')
 
2706
        builder.build_snapshot([b'A-id'],
 
2707
                               [('modify', ('foo', b'B content\n'))],
 
2708
                               revision_id=b'B-id')
 
2709
        builder.build_snapshot([b'A-id'],
 
2710
                               [('rename', ('a', 'b'))],
 
2711
                               revision_id=b'C-id')
2705
2712
        builder.build_snapshot([b'C-id', b'B-id'],
2706
 
            [('rename', ('b', 'c')),
2707
 
             ('modify', ('foo', b'E content\n'))],
2708
 
            revision_id=b'E-id')
 
2713
                               [('rename', ('b', 'c')),
 
2714
                                ('modify', ('foo', b'E content\n'))],
 
2715
                               revision_id=b'E-id')
2709
2716
        builder.build_snapshot([b'B-id', b'C-id'],
2710
 
            [('rename', ('a', 'b'))], revision_id=b'D-id') # merged change
 
2717
                               [('rename', ('a', 'b'))], revision_id=b'D-id')  # merged change
2711
2718
        wt_this = self.get_wt_from_builder(builder)
2712
2719
        wt_base = wt_this.controldir.sprout('base', b'A-id').open_workingtree()
2713
2720
        wt_base.lock_read()
2714
2721
        self.addCleanup(wt_base.unlock)
2715
 
        wt_lca1 = wt_this.controldir.sprout('b-tree', b'B-id').open_workingtree()
 
2722
        wt_lca1 = wt_this.controldir.sprout(
 
2723
            'b-tree', b'B-id').open_workingtree()
2716
2724
        wt_lca1.lock_read()
2717
2725
        self.addCleanup(wt_lca1.unlock)
2718
 
        wt_lca2 = wt_this.controldir.sprout('c-tree', b'C-id').open_workingtree()
 
2726
        wt_lca2 = wt_this.controldir.sprout(
 
2727
            'c-tree', b'C-id').open_workingtree()
2719
2728
        wt_lca2.lock_read()
2720
2729
        self.addCleanup(wt_lca2.unlock)
2721
 
        wt_other = wt_this.controldir.sprout('other', b'E-id').open_workingtree()
 
2730
        wt_other = wt_this.controldir.sprout(
 
2731
            'other', b'E-id').open_workingtree()
2722
2732
        wt_other.lock_read()
2723
2733
        self.addCleanup(wt_other.unlock)
2724
2734
        merge_obj = _mod_merge.Merge3Merger(wt_this, wt_this, wt_base,
2725
 
            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
 
2735
                                            wt_other, lca_trees=[wt_lca1, wt_lca2], do_merge=False)
2726
2736
        entries = list(merge_obj._entries_lca())
2727
2737
        root_id = b'a-root-id'
2728
2738
        self.assertEqual([(b'a-id', False,
2735
2745
                           ((root_id, [root_id, root_id]), root_id, root_id),
2736
2746
                           ((u'foo', [u'foo', u'foo']), u'foo', u'foo'),
2737
2747
                           ((False, [False, False]), False, False)),
2738
 
                         ], entries)
 
2748
                          ], entries)
2739
2749
 
2740
2750
    def test_nested_tree_unmodified(self):
2741
2751
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2742
2752
        # 'tree-reference'
2743
2753
        wt = self.make_branch_and_tree('tree',
2744
 
            format='development-subtree')
 
2754
                                       format='development-subtree')
2745
2755
        wt.lock_write()
2746
2756
        self.addCleanup(wt.unlock)
2747
2757
        sub_tree = self.make_branch_and_tree('tree/sub-tree',
2748
 
            format='development-subtree')
 
2758
                                             format='development-subtree')
2749
2759
        wt.set_root_id(b'a-root-id')
2750
2760
        sub_tree.set_root_id(b'sub-tree-root')
2751
2761
        self.build_tree_contents([('tree/sub-tree/file', b'text1')])
2775
2785
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2776
2786
        # 'tree-reference'
2777
2787
        wt = self.make_branch_and_tree('tree',
2778
 
            format='development-subtree')
 
2788
                                       format='development-subtree')
2779
2789
        wt.lock_write()
2780
2790
        self.addCleanup(wt.unlock)
2781
2791
        sub_tree = self.make_branch_and_tree('tree/sub',
2782
 
            format='development-subtree')
 
2792
                                             format='development-subtree')
2783
2793
        wt.set_root_id(b'a-root-id')
2784
2794
        sub_tree.set_root_id(b'sub-tree-root')
2785
2795
        self.build_tree_contents([('tree/sub/file', b'text1')])
2813
2823
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2814
2824
        # 'tree-reference'
2815
2825
        wt = self.make_branch_and_tree('tree',
2816
 
            format='development-subtree')
 
2826
                                       format='development-subtree')
2817
2827
        wt.lock_write()
2818
2828
        self.addCleanup(wt.unlock)
2819
2829
        sub_tree = self.make_branch_and_tree('tree/sub',
2820
 
            format='development-subtree')
 
2830
                                             format='development-subtree')
2821
2831
        wt.set_root_id(b'a-root-id')
2822
2832
        sub_tree.set_root_id(b'sub-tree-root')
2823
2833
        self.build_tree_contents([('tree/sub/file', b'text1')])
2850
2860
                           ((root_id, [root_id, root_id]), root_id, root_id),
2851
2861
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2852
2862
                           ((False, [False, False]), False, False)),
2853
 
                         ], entries)
 
2863
                          ], entries)
2854
2864
 
2855
2865
    def test_nested_tree_subtree_renamed_and_modified(self):
2856
2866
        # Tested with a real WT, because BranchBuilder/MemoryTree don't handle
2857
2867
        # 'tree-reference'
2858
2868
        wt = self.make_branch_and_tree('tree',
2859
 
            format='development-subtree')
 
2869
                                       format='development-subtree')
2860
2870
        wt.lock_write()
2861
2871
        self.addCleanup(wt.unlock)
2862
2872
        sub_tree = self.make_branch_and_tree('tree/sub',
2863
 
            format='development-subtree')
 
2873
                                             format='development-subtree')
2864
2874
        wt.set_root_id(b'a-root-id')
2865
2875
        sub_tree.set_root_id(b'sub-tree-root')
2866
2876
        self.build_tree_contents([('tree/sub/file', b'text1')])
2895
2905
                           ((root_id, [root_id, root_id]), root_id, root_id),
2896
2906
                           ((u'sub', [u'sub', u'sub']), u'alt_sub', u'sub'),
2897
2907
                           ((False, [False, False]), False, False)),
2898
 
                         ], entries)
 
2908
                          ], entries)
2899
2909
 
2900
2910
 
2901
2911
class TestLCAMultiWay(tests.TestCase):
2903
2913
    def assertLCAMultiWay(self, expected, base, lcas, other, this,
2904
2914
                          allow_overriding_lca=True):
2905
2915
        self.assertEqual(expected, _mod_merge.Merge3Merger._lca_multi_way(
2906
 
                                (base, lcas), other, this,
2907
 
                                allow_overriding_lca=allow_overriding_lca))
 
2916
            (base, lcas), other, this,
 
2917
            allow_overriding_lca=allow_overriding_lca))
2908
2918
 
2909
2919
    def test_other_equal_equal_lcas(self):
2910
2920
        """Test when OTHER=LCA and all LCAs are identical."""
2911
2921
        self.assertLCAMultiWay('this',
2912
 
            'bval', ['bval', 'bval'], 'bval', 'bval')
2913
 
        self.assertLCAMultiWay('this',
2914
 
            'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
2915
 
        self.assertLCAMultiWay('this',
2916
 
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
2917
 
        self.assertLCAMultiWay('this',
2918
 
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
2919
 
        self.assertLCAMultiWay('this',
2920
 
            'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
 
2922
                               'bval', ['bval', 'bval'], 'bval', 'bval')
 
2923
        self.assertLCAMultiWay('this',
 
2924
                               'bval', ['lcaval', 'lcaval'], 'lcaval', 'bval')
 
2925
        self.assertLCAMultiWay('this',
 
2926
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'bval')
 
2927
        self.assertLCAMultiWay('this',
 
2928
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', 'tval')
 
2929
        self.assertLCAMultiWay('this',
 
2930
                               'bval', ['lcaval', 'lcaval', 'lcaval'], 'lcaval', None)
2921
2931
 
2922
2932
    def test_other_equal_this(self):
2923
2933
        """Test when other and this are identical."""
2924
2934
        self.assertLCAMultiWay('this',
2925
 
            'bval', ['bval', 'bval'], 'oval', 'oval')
2926
 
        self.assertLCAMultiWay('this',
2927
 
            'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
2928
 
        self.assertLCAMultiWay('this',
2929
 
            'bval', ['cval', 'dval'], 'oval', 'oval')
2930
 
        self.assertLCAMultiWay('this',
2931
 
            'bval', [None, 'lcaval'], 'oval', 'oval')
2932
 
        self.assertLCAMultiWay('this',
2933
 
            None, [None, 'lcaval'], 'oval', 'oval')
2934
 
        self.assertLCAMultiWay('this',
2935
 
            None, ['lcaval', 'lcaval'], 'oval', 'oval')
2936
 
        self.assertLCAMultiWay('this',
2937
 
            None, ['cval', 'dval'], 'oval', 'oval')
2938
 
        self.assertLCAMultiWay('this',
2939
 
            None, ['cval', 'dval'], None, None)
2940
 
        self.assertLCAMultiWay('this',
2941
 
            None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
 
2935
                               'bval', ['bval', 'bval'], 'oval', 'oval')
 
2936
        self.assertLCAMultiWay('this',
 
2937
                               'bval', ['lcaval', 'lcaval'], 'oval', 'oval')
 
2938
        self.assertLCAMultiWay('this',
 
2939
                               'bval', ['cval', 'dval'], 'oval', 'oval')
 
2940
        self.assertLCAMultiWay('this',
 
2941
                               'bval', [None, 'lcaval'], 'oval', 'oval')
 
2942
        self.assertLCAMultiWay('this',
 
2943
                               None, [None, 'lcaval'], 'oval', 'oval')
 
2944
        self.assertLCAMultiWay('this',
 
2945
                               None, ['lcaval', 'lcaval'], 'oval', 'oval')
 
2946
        self.assertLCAMultiWay('this',
 
2947
                               None, ['cval', 'dval'], 'oval', 'oval')
 
2948
        self.assertLCAMultiWay('this',
 
2949
                               None, ['cval', 'dval'], None, None)
 
2950
        self.assertLCAMultiWay('this',
 
2951
                               None, ['cval', 'dval', 'eval', 'fval'], 'oval', 'oval')
2942
2952
 
2943
2953
    def test_no_lcas(self):
2944
2954
        self.assertLCAMultiWay('this',
2945
 
            'bval', [], 'bval', 'tval')
 
2955
                               'bval', [], 'bval', 'tval')
2946
2956
        self.assertLCAMultiWay('other',
2947
 
            'bval', [], 'oval', 'bval')
 
2957
                               'bval', [], 'oval', 'bval')
2948
2958
        self.assertLCAMultiWay('conflict',
2949
 
            'bval', [], 'oval', 'tval')
 
2959
                               'bval', [], 'oval', 'tval')
2950
2960
        self.assertLCAMultiWay('this',
2951
 
            'bval', [], 'oval', 'oval')
 
2961
                               'bval', [], 'oval', 'oval')
2952
2962
 
2953
2963
    def test_lca_supersedes_other_lca(self):
2954
2964
        """If one lca == base, the other lca takes precedence"""
2955
2965
        self.assertLCAMultiWay('this',
2956
 
            'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
 
2966
                               'bval', ['bval', 'lcaval'], 'lcaval', 'tval')
2957
2967
        self.assertLCAMultiWay('this',
2958
 
            'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
 
2968
                               'bval', ['bval', 'lcaval'], 'lcaval', 'bval')
2959
2969
        # This is actually considered a 'revert' because the 'lcaval' in LCAS
2960
2970
        # supersedes the BASE val (in the other LCA) but then OTHER reverts it
2961
2971
        # back to bval.
2962
2972
        self.assertLCAMultiWay('other',
2963
 
            'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
 
2973
                               'bval', ['bval', 'lcaval'], 'bval', 'lcaval')
2964
2974
        self.assertLCAMultiWay('conflict',
2965
 
            'bval', ['bval', 'lcaval'], 'bval', 'tval')
 
2975
                               'bval', ['bval', 'lcaval'], 'bval', 'tval')
2966
2976
 
2967
2977
    def test_other_and_this_pick_different_lca(self):
2968
2978
        # OTHER and THIS resolve the lca conflict in different ways
2969
2979
        self.assertLCAMultiWay('conflict',
2970
 
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
2971
 
        self.assertLCAMultiWay('conflict',
2972
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
2973
 
        self.assertLCAMultiWay('conflict',
2974
 
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
 
2980
                               'bval', ['lca1val', 'lca2val'], 'lca1val', 'lca2val')
 
2981
        self.assertLCAMultiWay('conflict',
 
2982
                               'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'lca2val')
 
2983
        self.assertLCAMultiWay('conflict',
 
2984
                               'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'lca2val')
2975
2985
 
2976
2986
    def test_other_in_lca(self):
2977
2987
        # OTHER takes a value of one of the LCAs, THIS takes a new value, which
2978
2988
        # theoretically supersedes both LCA values and 'wins'
2979
 
        self.assertLCAMultiWay('this',
2980
 
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
2981
 
        self.assertLCAMultiWay('this',
2982
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval')
2983
 
        self.assertLCAMultiWay('conflict',
2984
 
            'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval',
2985
 
            allow_overriding_lca=False)
2986
 
        self.assertLCAMultiWay('conflict',
2987
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'newval',
2988
 
            allow_overriding_lca=False)
 
2989
        self.assertLCAMultiWay(
 
2990
            'this', 'bval', ['lca1val', 'lca2val'], 'lca1val', 'newval')
 
2991
        self.assertLCAMultiWay(
 
2992
            'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
 
2993
            'newval')
 
2994
        self.assertLCAMultiWay('conflict',
 
2995
                               'bval', ['lca1val',
 
2996
                                        'lca2val'], 'lca1val', 'newval',
 
2997
                               allow_overriding_lca=False)
 
2998
        self.assertLCAMultiWay('conflict',
 
2999
                               'bval', ['lca1val', 'lca2val',
 
3000
                                        'lca3val'], 'lca1val', 'newval',
 
3001
                               allow_overriding_lca=False)
2989
3002
        # THIS reverted back to BASE, but that is an explicit supersede of all
2990
3003
        # LCAs
2991
 
        self.assertLCAMultiWay('this',
2992
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval')
2993
 
        self.assertLCAMultiWay('this',
2994
 
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
2995
 
        self.assertLCAMultiWay('conflict',
2996
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val', 'bval',
2997
 
            allow_overriding_lca=False)
2998
 
        self.assertLCAMultiWay('conflict',
2999
 
            'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval',
3000
 
            allow_overriding_lca=False)
 
3004
        self.assertLCAMultiWay(
 
3005
            'this', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'lca1val',
 
3006
            'bval')
 
3007
        self.assertLCAMultiWay(
 
3008
            'this', 'bval', ['lca1val', 'lca2val', 'bval'], 'lca1val', 'bval')
 
3009
        self.assertLCAMultiWay('conflict',
 
3010
                               'bval', ['lca1val', 'lca2val',
 
3011
                                        'lca3val'], 'lca1val', 'bval',
 
3012
                               allow_overriding_lca=False)
 
3013
        self.assertLCAMultiWay('conflict',
 
3014
                               'bval', ['lca1val', 'lca2val',
 
3015
                                        'bval'], 'lca1val', 'bval',
 
3016
                               allow_overriding_lca=False)
3001
3017
 
3002
3018
    def test_this_in_lca(self):
3003
3019
        # THIS takes a value of one of the LCAs, OTHER takes a new value, which
3004
3020
        # theoretically supersedes both LCA values and 'wins'
3005
 
        self.assertLCAMultiWay('other',
3006
 
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
3007
 
        self.assertLCAMultiWay('other',
3008
 
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
3009
 
        self.assertLCAMultiWay('conflict',
3010
 
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val',
3011
 
            allow_overriding_lca=False)
3012
 
        self.assertLCAMultiWay('conflict',
3013
 
            'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val',
3014
 
            allow_overriding_lca=False)
 
3021
        self.assertLCAMultiWay(
 
3022
            'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca1val')
 
3023
        self.assertLCAMultiWay(
 
3024
            'other', 'bval', ['lca1val', 'lca2val'], 'oval', 'lca2val')
 
3025
        self.assertLCAMultiWay('conflict',
 
3026
                               'bval', ['lca1val',
 
3027
                                        'lca2val'], 'oval', 'lca1val',
 
3028
                               allow_overriding_lca=False)
 
3029
        self.assertLCAMultiWay('conflict',
 
3030
                               'bval', ['lca1val',
 
3031
                                        'lca2val'], 'oval', 'lca2val',
 
3032
                               allow_overriding_lca=False)
3015
3033
        # OTHER reverted back to BASE, but that is an explicit supersede of all
3016
3034
        # LCAs
3017
 
        self.assertLCAMultiWay('other',
3018
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val')
3019
 
        self.assertLCAMultiWay('conflict',
3020
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval', 'lca3val',
3021
 
            allow_overriding_lca=False)
 
3035
        self.assertLCAMultiWay(
 
3036
            'other', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'bval',
 
3037
            'lca3val')
 
3038
        self.assertLCAMultiWay(
 
3039
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'],
 
3040
            'bval', 'lca3val', allow_overriding_lca=False)
3022
3041
 
3023
3042
    def test_all_differ(self):
3024
 
        self.assertLCAMultiWay('conflict',
3025
 
            'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
3026
 
        self.assertLCAMultiWay('conflict',
3027
 
            'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval', 'tval')
3028
 
        self.assertLCAMultiWay('conflict',
3029
 
            'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval', 'tval')
 
3043
        self.assertLCAMultiWay(
 
3044
            'conflict', 'bval', ['lca1val', 'lca2val'], 'oval', 'tval')
 
3045
        self.assertLCAMultiWay(
 
3046
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca2val'], 'oval',
 
3047
            'tval')
 
3048
        self.assertLCAMultiWay(
 
3049
            'conflict', 'bval', ['lca1val', 'lca2val', 'lca3val'], 'oval',
 
3050
            'tval')
3030
3051
 
3031
3052
 
3032
3053
class TestConfigurableFileMerger(tests.TestCaseWithTransport):
3070
3091
        factory = self.get_merger_factory()
3071
3092
        self._install_hook(factory)
3072
3093
        builder = self.make_builder()
3073
 
        builder.add_file(b'bar-id', builder.tree_root, file_name, b'text1', True)
 
3094
        builder.add_file(b'bar-id', builder.tree_root,
 
3095
                         file_name, b'text1', True)
3074
3096
        builder.change_contents(b'bar-id', other=b'text4', this=b'text3')
3075
3097
        return builder
3076
3098
 
3099
3121
 
3100
3122
    def test_hook_called_for_text_conflicts(self):
3101
3123
        builder = self.make_text_conflict()
3102
 
        conflicts = builder.merge()
 
3124
        builder.merge()
3103
3125
        # The hook should call the merge_text() method
3104
3126
        self.assertEqual(['merge_text'], self.calls)
3105
3127
 
3106
3128
    def test_hook_not_called_for_kind_change(self):
3107
3129
        builder = self.make_kind_change()
3108
 
        conflicts = builder.merge()
 
3130
        builder.merge()
3109
3131
        # The hook should not call the merge_text() method
3110
3132
        self.assertEqual([], self.calls)
3111
3133
 
3112
3134
    def test_hook_not_called_for_other_files(self):
3113
3135
        builder = self.make_text_conflict('foobar')
3114
 
        conflicts = builder.merge()
 
3136
        builder.merge()
3115
3137
        # The hook should not call the merge_text() method
3116
3138
        self.assertEqual([], self.calls)
3117
3139
 
3120
3142
 
3121
3143
    def setup_simple_branch(self, relpath, shape=None, root_id=None):
3122
3144
        """One commit, containing tree specified by optional shape.
3123
 
        
 
3145
 
3124
3146
        Default is empty tree (just root entry).
3125
3147
        """
3126
3148
        if root_id is None:
3131
3153
            adjusted_shape = [relpath + '/' + elem for elem in shape]
3132
3154
            self.build_tree(adjusted_shape)
3133
3155
            ids = [
3134
 
                (b'%s-%s-id' % (relpath.encode('utf-8'), basename(elem.rstrip('/')).encode('ascii')))
 
3156
                (b'%s-%s-id' % (relpath.encode('utf-8'),
 
3157
                                basename(elem.rstrip('/')).encode('ascii')))
3135
3158
                for elem in shape]
3136
3159
            wt.add(shape, ids=ids)
3137
3160
        rev_id = b'r1-%s' % (relpath.encode('utf-8'),)
3155
3178
 
3156
3179
    def do_merge_into(self, location, merge_as):
3157
3180
        """Helper for using MergeIntoMerger.
3158
 
        
 
3181
 
3159
3182
        :param location: location of directory to merge from, either the
3160
3183
            location of a branch or of a path inside a branch.
3161
3184
        :param merge_as: the path in a tree to add the new directory as.
3174
3197
        other_tree = branch_to_merge.basis_tree()
3175
3198
        op.add_cleanup(other_tree.lock_read().unlock)
3176
3199
        # Perform the merge
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)
 
3200
        merger = _mod_merge.MergeIntoMerger(
 
3201
            this_tree=wt, other_tree=other_tree, other_branch=branch_to_merge,
 
3202
            target_subdir=subdir_relpath, source_subpath=subdir_to_merge)
3180
3203
        merger.set_base_revision(_mod_revision.NULL_REVISION, branch_to_merge)
3181
3204
        conflicts = merger.do_merge()
3182
3205
        merger.set_pending()
3200
3223
        project_wt.lock_read()
3201
3224
        self.addCleanup(project_wt.unlock)
3202
3225
        # The r1-lib1 revision should be merged into this one
3203
 
        self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
 
3226
        self.assertEqual([b'r1-project', b'r1-lib1'],
 
3227
                         project_wt.get_parent_ids())
3204
3228
        self.assertTreeEntriesEqual(
3205
3229
            [('', b'project-root-id'),
3206
3230
             ('README', b'project-README-id'),
3210
3234
             ('lib1/Makefile', b'lib1-Makefile-id'),
3211
3235
             ('lib1/README', b'lib1-README-id'),
3212
3236
             ('lib1/foo.c', b'lib1-foo.c-id'),
3213
 
            ], project_wt)
 
3237
             ], project_wt)
3214
3238
 
3215
3239
    def test_subdir(self):
3216
3240
        """Merge a branch into a subdirectory of an existing directory."""
3219
3243
        project_wt.lock_read()
3220
3244
        self.addCleanup(project_wt.unlock)
3221
3245
        # The r1-lib1 revision should be merged into this one
3222
 
        self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
 
3246
        self.assertEqual([b'r1-project', b'r1-lib1'],
 
3247
                         project_wt.get_parent_ids())
3223
3248
        self.assertTreeEntriesEqual(
3224
3249
            [('', b'project-root-id'),
3225
3250
             ('README', b'project-README-id'),
3229
3254
             ('dir/lib1/Makefile', b'lib1-Makefile-id'),
3230
3255
             ('dir/lib1/README', b'lib1-README-id'),
3231
3256
             ('dir/lib1/foo.c', b'lib1-foo.c-id'),
3232
 
            ], project_wt)
 
3257
             ], project_wt)
3233
3258
 
3234
3259
    def test_newdir_with_repeat_roots(self):
3235
3260
        """If the file-id of the dir to be merged already exists a new ID will
3241
3266
        project_wt.lock_read()
3242
3267
        self.addCleanup(project_wt.unlock)
3243
3268
        # The r1-lib1 revision should be merged into this one
3244
 
        self.assertEqual([b'r1-project', b'r1-lib1'], project_wt.get_parent_ids())
 
3269
        self.assertEqual([b'r1-project', b'r1-lib1'],
 
3270
                         project_wt.get_parent_ids())
3245
3271
        new_lib1_id = project_wt.path2id('lib1')
3246
3272
        self.assertNotEqual(None, new_lib1_id)
3247
3273
        self.assertTreeEntriesEqual(
3253
3279
             ('lib1/Makefile', b'lib1-Makefile-id'),
3254
3280
             ('lib1/README', b'lib1-README-id'),
3255
3281
             ('lib1/foo.c', b'lib1-foo.c-id'),
3256
 
            ], project_wt)
 
3282
             ], project_wt)
3257
3283
 
3258
3284
    def test_name_conflict(self):
3259
3285
        """When the target directory name already exists a conflict is
3260
3286
        generated and the original directory is renamed to foo.moved.
3261
3287
        """
3262
3288
        dest_wt = self.setup_simple_branch('dest', ['dir/', 'dir/file.txt'])
3263
 
        src_wt = self.setup_simple_branch('src', ['README'])
 
3289
        self.setup_simple_branch('src', ['README'])
3264
3290
        conflicts = self.do_merge_into('src', 'dest/dir')
3265
3291
        self.assertEqual(1, conflicts)
3266
3292
        dest_wt.lock_read()
3273
3299
             ('dir.moved', b'dest-dir-id'),
3274
3300
             ('dir/README', b'src-README-id'),
3275
3301
             ('dir.moved/file.txt', b'dest-file.txt-id'),
3276
 
            ], dest_wt)
 
3302
             ], dest_wt)
3277
3303
 
3278
3304
    def test_file_id_conflict(self):
3279
3305
        """A conflict is generated if the merge-into adds a file (or other
3280
3306
        inventory entry) with a file-id that already exists in the target tree.
3281
3307
        """
3282
 
        dest_wt = self.setup_simple_branch('dest', ['file.txt'])
 
3308
        self.setup_simple_branch('dest', ['file.txt'])
3283
3309
        # Make a second tree with a file-id that will clash with file.txt in
3284
3310
        # dest.
3285
3311
        src_wt = self.make_branch_and_tree('src')
3297
3323
        subtree.
3298
3324
        """
3299
3325
        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')
 
3326
        self.setup_simple_branch('src', ['hello.txt', 'dir/', 'dir/foo.c'])
 
3327
        self.do_merge_into('src/dir', 'dest/dir')
3303
3328
        dest_wt.lock_read()
3304
3329
        self.addCleanup(dest_wt.unlock)
3305
3330
        # The r1-lib1 revision should NOT be merged into this one (this is a
3309
3334
            [('', b'dest-root-id'),
3310
3335
             ('dir', b'src-dir-id'),
3311
3336
             ('dir/foo.c', b'src-foo.c-id'),
3312
 
            ], dest_wt)
 
3337
             ], dest_wt)
3313
3338
 
3314
3339
    def test_only_file(self):
3315
3340
        """An edge case: merge just one file, not a whole dir."""
3316
3341
        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')
 
3342
        self.setup_simple_branch('two-file', ['file1.txt', 'file2.txt'])
 
3343
        self.do_merge_into('two-file/file1.txt', 'dest/file1.txt')
3320
3344
        dest_wt.lock_read()
3321
3345
        self.addCleanup(dest_wt.unlock)
3322
3346
        # The r1-lib1 revision should NOT be merged into this one
3330
3354
        does not exist.
3331
3355
        """
3332
3356
        dest_wt = self.setup_simple_branch('dest')
3333
 
        two_file_wt = self.setup_simple_branch('src', ['dir/'])
 
3357
        self.setup_simple_branch('src', ['dir/'])
3334
3358
        self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3335
 
            'src/no-such-dir', 'dest/foo')
 
3359
                          'src/no-such-dir', 'dest/foo')
3336
3360
        dest_wt.lock_read()
3337
3361
        self.addCleanup(dest_wt.unlock)
3338
3362
        # The dest tree is unmodified.
3344
3368
        tree does not exist.
3345
3369
        """
3346
3370
        dest_wt = self.setup_simple_branch('dest')
3347
 
        two_file_wt = self.setup_simple_branch('src', ['file.txt'])
 
3371
        self.setup_simple_branch('src', ['file.txt'])
3348
3372
        self.assertRaises(_mod_merge.PathNotInTree, self.do_merge_into,
3349
 
            'src', 'dest/no-such-dir/foo')
 
3373
                          'src', 'dest/no-such-dir/foo')
3350
3374
        dest_wt.lock_read()
3351
3375
        self.addCleanup(dest_wt.unlock)
3352
3376
        # The dest tree is unmodified.
3363
3387
        self.tree_a.add('file', b'file-id')
3364
3388
        self.tree_a.commit('added file')
3365
3389
 
3366
 
        self.tree_b = self.tree_a.controldir.sprout('tree_b').open_workingtree()
 
3390
        self.tree_b = self.tree_a.controldir.sprout(
 
3391
            'tree_b').open_workingtree()
3367
3392
        self.build_tree_contents([('tree_b/file', b'content_2')])
3368
3393
        self.tree_b.commit('modify file')
3369
3394
 
3372
3397
        self.build_tree_contents([('tree_c/file', b'content_3')])
3373
3398
        tree_c.commit("more content")
3374
3399
        calls = []
 
3400
 
3375
3401
        def factory(merger):
3376
3402
            self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3377
3403
            merger.other_tree = tree_c
3385
3411
 
3386
3412
    def test_post_merge_hook_called(self):
3387
3413
        calls = []
 
3414
 
3388
3415
        def factory(merger):
3389
3416
            self.assertIsInstance(merger, _mod_merge.Merge3Merger)
3390
3417
            calls.append(merger)