~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_merge.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:28:36 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711212836-1ehghrnzgaioqwou
Handle more edge cases.

Specifically, we don't have to switch on the whole-ancestry logic until we have
3 or more LCAs, we were doing it when there was 2.
However, when we do switch on the whole ancestry logic, it is possible to
get extra 'tails' which have ancestors that are in the ancestry of the
unique lca, but they themselves do not terminate on the unique lca.
The current code is a bit of a compromise, as we probably should chase down
more nodes to include. If we include more than 1 lca, then we run into the
same problem, where common lines have to have one side 'win' and the other 'lose'.
A different possibility would be to create an artificial ancestor node,
which uses the common lines from the different LCAs, so that it is assumed they
all come from the same source. This would end up ignoring cases where changes
were reverted and then reintroduced.

Show diffs side-by-side

added added

removed removed

Lines of Context:
478
478
    def add_version(self, key, parents, text):
479
479
        self.vf.add_lines(key, parents, [c+'\n' for c in text])
480
480
 
 
481
    def add_rev(self, prefix, revision_id, parents, text):
 
482
        self.add_version((prefix, revision_id), [(prefix, p) for p in parents],
 
483
                         text)
 
484
 
481
485
    def add_uncommitted_version(self, key, parents, text):
482
486
        self.plan_merge_vf.add_lines(key, parents,
483
487
                                     [c+'\n' for c in text])
544
548
                          ('new-b', 'z\n')],
545
549
                          list(my_plan.plan_merge()))
546
550
 
 
551
    def test_plan_merge_tail_ancestors(self):
 
552
        # The graph looks like this:
 
553
        #       A       # Common to all ancestors
 
554
        #      / \
 
555
        #     B   C     # Ancestors of E, only common to one side
 
556
        #     |\ /|
 
557
        #     D E F     # D, F are unique to G, H respectively
 
558
        #     |/ \|     # E is the LCA for G & H, and the unique LCA for
 
559
        #     G   H     # I, J
 
560
        #     |\ /|
 
561
        #     | X |
 
562
        #     |/ \|
 
563
        #     I   J     # criss-cross merge of G, H
 
564
        #
 
565
        # In this situation, a simple pruning of ancestors of E will leave D &
 
566
        # F "dangling", which looks like they introduce lines different from
 
567
        # the ones in E, but in actuality C&B introduced the lines, and they
 
568
        # are already present in E
 
569
 
 
570
        # Introduce the base text
 
571
        self.add_rev('root', 'A', [], 'abc')
 
572
        # Introduces a new line B
 
573
        self.add_rev('root', 'B', ['A'], 'aBbc')
 
574
        # Introduces a new line C
 
575
        self.add_rev('root', 'C', ['A'], 'abCc')
 
576
        # Introduce new line D
 
577
        self.add_rev('root', 'D', ['B'], 'DaBbc')
 
578
        # Merges B and C by just incorporating both
 
579
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
 
580
        # Introduce new line F
 
581
        self.add_rev('root', 'F', ['C'], 'abCcF')
 
582
        # Merge D & E by just combining the texts
 
583
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
 
584
        # Merge F & E by just combining the texts
 
585
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
 
586
        # Merge G & H by just combining texts
 
587
        self.add_rev('root', 'I', ['G', 'H'], 'DaBbCcF')
 
588
        # Merge G & H but supersede an old line in B
 
589
        self.add_rev('root', 'J', ['H', 'G'], 'DaJbCcF')
 
590
        plan = self.plan_merge_vf.plan_merge('I', 'J')
 
591
        self.assertEqual([
 
592
                          ('unchanged', 'D\n'),
 
593
                          ('unchanged', 'a\n'),
 
594
                          ('killed-b', 'B\n'),
 
595
                          ('new-b', 'J\n'),
 
596
                          ('unchanged', 'b\n'),
 
597
                          ('unchanged', 'C\n'),
 
598
                          ('unchanged', 'c\n'),
 
599
                          ('unchanged', 'F\n')],
 
600
                         list(plan))
 
601
 
 
602
    def test_plan_merge_tail_triple_ancestors(self):
 
603
        # The graph looks like this:
 
604
        #       A       # Common to all ancestors
 
605
        #      / \
 
606
        #     B   C     # Ancestors of E, only common to one side
 
607
        #     |\ /|
 
608
        #     D E F     # D, F are unique to G, H respectively
 
609
        #     |/|\|     # E is the LCA for G & H, and the unique LCA for
 
610
        #     G Q H     # I, J
 
611
        #     |\ /|     # Q is just an extra node which is merged into both
 
612
        #     | X |     # I and J
 
613
        #     |/ \|
 
614
        #     I   J     # criss-cross merge of G, H
 
615
        #
 
616
        # This is the same as the test_plan_merge_tail_ancestors, except we add
 
617
        # a third LCA that doesn't add new lines, but will trigger our more
 
618
        # involved ancestry logic
 
619
 
 
620
        self.add_rev('root', 'A', [], 'abc')
 
621
        self.add_rev('root', 'B', ['A'], 'aBbc')
 
622
        self.add_rev('root', 'C', ['A'], 'abCc')
 
623
        self.add_rev('root', 'D', ['B'], 'DaBbc')
 
624
        self.add_rev('root', 'E', ['B', 'C'], 'aBbCc')
 
625
        self.add_rev('root', 'F', ['C'], 'abCcF')
 
626
        self.add_rev('root', 'G', ['D', 'E'], 'DaBbCc')
 
627
        self.add_rev('root', 'H', ['F', 'E'], 'aBbCcF')
 
628
        self.add_rev('root', 'Q', ['E'], 'aBbCc')
 
629
        self.add_rev('root', 'I', ['G', 'Q', 'H'], 'DaBbCcF')
 
630
        # Merge G & H but supersede an old line in B
 
631
        self.add_rev('root', 'J', ['H', 'Q', 'G'], 'DaJbCcF')
 
632
        plan = self.plan_merge_vf.plan_merge('I', 'J')
 
633
        # This has a slight incorrect value, as it considers the lines in A to
 
634
        # be 'killed-base', because they show up as new in both B and C, and
 
635
        # then E has to resolve which ones are the 'real' ones.
 
636
        # However, I believe this is okay as the important lines will all
 
637
        # derive from E.
 
638
        self.assertEqual([
 
639
                          ('unchanged', 'D\n'),
 
640
                          ('unchanged', 'a\n'),
 
641
                          ('killed-b', 'B\n'),
 
642
                          ('new-b', 'J\n'),
 
643
                          ('unchanged', 'b\n'),
 
644
                          ('killed-base', 'c\n'), # Not technically correct
 
645
                          ('killed-base', 'a\n'), # as they came from A
 
646
                          ('killed-base', 'b\n'), # but the final text is right
 
647
                          ('unchanged', 'C\n'),
 
648
                          ('unchanged', 'c\n'),
 
649
                          ('unchanged', 'F\n')],
 
650
                         list(plan))
 
651
 
547
652
    def test_plan_merge_uncommitted_files(self):
548
653
        self.setup_plan_merge_uncommitted()
549
654
        plan = self.plan_merge_vf.plan_merge('B:', 'C:')