~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: John Arbash Meinel
  • Date: 2008-07-11 21:41:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080711214124-qi09irlj7pd5cuzg
Shortcut the case when one revision is in the ancestry of the other.

At the cost of a heads() check, when one parent supersedes, we don't have to extract
the text for the other. Changes merge time from 3m37s => 3m21s. Using a
CachingParentsProvider would drop the time down to 3m11s.

Show diffs side-by-side

added added

removed removed

Lines of Context:
23
23
from bzrlib import (
24
24
    debug,
25
25
    errors,
26
 
    graph as _mod_graph,
27
26
    osutils,
28
27
    patiencediff,
29
28
    registry,
30
29
    revision as _mod_revision,
31
 
    tree as _mod_tree,
32
30
    tsort,
33
31
    )
34
32
from bzrlib.branch import Branch
55
53
from bzrlib.trace import mutter, warning, note, is_quiet
56
54
from bzrlib.transform import (TransformPreview, TreeTransform,
57
55
                              resolve_conflicts, cook_conflicts,
58
 
                              conflict_pass, FinalPaths, create_from_tree,
 
56
                              conflict_pass, FinalPaths, create_by_entry,
59
57
                              unique_add, ROOT_PARENT)
60
58
from bzrlib.versionedfile import PlanWeaveMerge
61
59
from bzrlib import ui
97
95
        self._revision_graph = revision_graph
98
96
        self._base_is_ancestor = None
99
97
        self._base_is_other_ancestor = None
100
 
        self._is_criss_cross = None
101
 
        self._lca_trees = None
102
98
 
103
99
    @property
104
100
    def revision_graph(self):
132
128
                                      _set_base_is_other_ancestor)
133
129
 
134
130
    @staticmethod
135
 
    def from_uncommitted(tree, other_tree, pb, base_tree=None):
 
131
    def from_uncommitted(tree, other_tree, pb):
136
132
        """Return a Merger for uncommitted changes in other_tree.
137
133
 
138
134
        :param tree: The tree to merge into
139
135
        :param other_tree: The tree to get uncommitted changes from
140
136
        :param pb: A progress indicator
141
 
        :param base_tree: The basis to use for the merge.  If unspecified,
142
 
            other_tree.basis_tree() will be used.
143
137
        """
144
 
        if base_tree is None:
145
 
            base_tree = other_tree.basis_tree()
146
 
        merger = Merger(tree.branch, other_tree, base_tree, tree, pb)
 
138
        merger = Merger(tree.branch, other_tree, other_tree.basis_tree(), tree,
 
139
                        pb)
147
140
        merger.base_rev_id = merger.base_tree.get_revision_id()
148
141
        merger.other_rev_id = None
149
142
        merger.other_basis = merger.base_rev_id
175
168
 
176
169
    @staticmethod
177
170
    def from_revision_ids(pb, tree, other, base=None, other_branch=None,
178
 
                          base_branch=None, revision_graph=None,
179
 
                          tree_branch=None):
 
171
                          base_branch=None, revision_graph=None):
180
172
        """Return a Merger for revision-ids.
181
173
 
182
 
        :param pb: A progress indicator
183
174
        :param tree: The tree to merge changes into
184
175
        :param other: The revision-id to use as OTHER
185
176
        :param base: The revision-id to use as BASE.  If not specified, will
190
181
            not supplied, other_branch or tree.branch will be used.
191
182
        :param revision_graph: If you have a revision_graph precomputed, pass
192
183
            it in, otherwise it will be created for you.
193
 
        :param tree_branch: The branch associated with tree.  If not supplied,
194
 
            tree.branch will be used.
 
184
        :param pb: A progress indicator
195
185
        """
196
 
        if tree_branch is None:
197
 
            tree_branch = tree.branch
198
 
        merger = Merger(tree_branch, this_tree=tree, pb=pb,
 
186
        merger = Merger(tree.branch, this_tree=tree, pb=pb,
199
187
                        revision_graph=revision_graph)
200
188
        if other_branch is None:
201
189
            other_branch = tree.branch
364
352
                     ensure_null(self.other_basis)]
365
353
        if NULL_REVISION in revisions:
366
354
            self.base_rev_id = NULL_REVISION
367
 
            self.base_tree = self.revision_tree(self.base_rev_id)
368
 
            self._is_criss_cross = False
369
355
        else:
370
 
            lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
371
 
            self._is_criss_cross = False
372
 
            if len(lcas) == 0:
373
 
                self.base_rev_id = NULL_REVISION
374
 
            elif len(lcas) == 1:
375
 
                self.base_rev_id = list(lcas)[0]
376
 
            else: # len(lcas) > 1
377
 
                if len(lcas) > 2:
378
 
                    # find_unique_lca can only handle 2 nodes, so we have to
379
 
                    # start back at the beginning. It is a shame to traverse
380
 
                    # the graph again, but better than re-implementing
381
 
                    # find_unique_lca.
382
 
                    self.base_rev_id = self.revision_graph.find_unique_lca(
383
 
                                            revisions[0], revisions[1])
384
 
                else:
385
 
                    self.base_rev_id = self.revision_graph.find_unique_lca(
386
 
                                            *lcas)
387
 
                self._is_criss_cross = True
 
356
            self.base_rev_id, steps = self.revision_graph.find_unique_lca(
 
357
                revisions[0], revisions[1], count_steps=True)
388
358
            if self.base_rev_id == NULL_REVISION:
389
359
                raise UnrelatedBranches()
390
 
            if self._is_criss_cross:
 
360
            if steps > 1:
391
361
                warning('Warning: criss-cross merge encountered.  See bzr'
392
362
                        ' help criss-cross.')
393
 
                mutter('Criss-cross lcas: %r' % lcas)
394
 
                interesting_revision_ids = [self.base_rev_id]
395
 
                interesting_revision_ids.extend(lcas)
396
 
                interesting_trees = dict((t.get_revision_id(), t)
397
 
                    for t in self.this_branch.repository.revision_trees(
398
 
                        interesting_revision_ids))
399
 
                self._cached_trees.update(interesting_trees)
400
 
                self.base_tree = interesting_trees.pop(self.base_rev_id)
401
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
402
 
                    revisions[0], lcas)
403
 
                self._lca_trees = [interesting_trees[key]
404
 
                                   for key in sorted_lca_keys]
405
 
            else:
406
 
                self.base_tree = self.revision_tree(self.base_rev_id)
 
363
        self.base_tree = self.revision_tree(self.base_rev_id)
407
364
        self.base_is_ancestor = True
408
365
        self.base_is_other_ancestor = True
409
 
        mutter('Base revid: %r' % self.base_rev_id)
410
366
 
411
367
    def set_base(self, base_revision):
412
368
        """Set the base revision to use for the merge.
452
408
        if self.merge_type.supports_cherrypick:
453
409
            kwargs['cherrypick'] = (not self.base_is_ancestor or
454
410
                                    not self.base_is_other_ancestor)
455
 
        if self._is_criss_cross and getattr(self.merge_type,
456
 
                                            'supports_lca_trees', False):
457
 
            kwargs['lca_trees'] = self._lca_trees
458
411
        return self.merge_type(pb=self._pb,
459
412
                               change_reporter=self.change_reporter,
460
413
                               **kwargs)
461
414
 
462
 
    def _do_merge_to(self, merge):
463
 
        merge.do_merge()
464
 
        if self.recurse == 'down':
465
 
            for relpath, file_id in self.this_tree.iter_references():
466
 
                sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
467
 
                other_revision = self.other_tree.get_reference_revision(
468
 
                    file_id, relpath)
469
 
                if  other_revision == sub_tree.last_revision():
470
 
                    continue
471
 
                sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
472
 
                sub_merge.merge_type = self.merge_type
473
 
                other_branch = self.other_branch.reference_parent(file_id, relpath)
474
 
                sub_merge.set_other_revision(other_revision, other_branch)
475
 
                base_revision = self.base_tree.get_reference_revision(file_id)
476
 
                sub_merge.base_tree = \
477
 
                    sub_tree.branch.repository.revision_tree(base_revision)
478
 
                sub_merge.base_rev_id = base_revision
479
 
                sub_merge.do_merge()
480
 
        
481
415
    def do_merge(self):
482
416
        self.this_tree.lock_tree_write()
 
417
        if self.base_tree is not None:
 
418
            self.base_tree.lock_read()
 
419
        if self.other_tree is not None:
 
420
            self.other_tree.lock_read()
483
421
        try:
 
422
            merge = self.make_merger()
 
423
            merge.do_merge()
 
424
            if self.recurse == 'down':
 
425
                for relpath, file_id in self.this_tree.iter_references():
 
426
                    sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
 
427
                    other_revision = self.other_tree.get_reference_revision(
 
428
                        file_id, relpath)
 
429
                    if  other_revision == sub_tree.last_revision():
 
430
                        continue
 
431
                    sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
 
432
                    sub_merge.merge_type = self.merge_type
 
433
                    other_branch = self.other_branch.reference_parent(file_id, relpath)
 
434
                    sub_merge.set_other_revision(other_revision, other_branch)
 
435
                    base_revision = self.base_tree.get_reference_revision(file_id)
 
436
                    sub_merge.base_tree = \
 
437
                        sub_tree.branch.repository.revision_tree(base_revision)
 
438
                    sub_merge.base_rev_id = base_revision
 
439
                    sub_merge.do_merge()
 
440
 
 
441
        finally:
 
442
            if self.other_tree is not None:
 
443
                self.other_tree.unlock()
484
444
            if self.base_tree is not None:
485
 
                self.base_tree.lock_read()
486
 
            try:
487
 
                if self.other_tree is not None:
488
 
                    self.other_tree.lock_read()
489
 
                try:
490
 
                    merge = self.make_merger()
491
 
                    self._do_merge_to(merge)
492
 
                finally:
493
 
                    if self.other_tree is not None:
494
 
                        self.other_tree.unlock()
495
 
            finally:
496
 
                if self.base_tree is not None:
497
 
                    self.base_tree.unlock()
498
 
        finally:
 
445
                self.base_tree.unlock()
499
446
            self.this_tree.unlock()
500
447
        if len(merge.cooked_conflicts) == 0:
501
448
            if not self.ignore_zero and not is_quiet():
506
453
        return len(merge.cooked_conflicts)
507
454
 
508
455
 
509
 
class _InventoryNoneEntry(object):
510
 
    """This represents an inventory entry which *isn't there*.
511
 
 
512
 
    It simplifies the merging logic if we always have an InventoryEntry, even
513
 
    if it isn't actually present
514
 
    """
515
 
    executable = None
516
 
    kind = None
517
 
    name = None
518
 
    parent_id = None
519
 
    revision = None
520
 
    symlink_target = None
521
 
    text_sha1 = None
522
 
 
523
 
_none_entry = _InventoryNoneEntry()
524
 
 
525
 
 
526
456
class Merge3Merger(object):
527
457
    """Three-way merger that uses the merge3 text merger"""
528
458
    requires_base = True
532
462
    supports_cherrypick = True
533
463
    supports_reverse_cherrypick = True
534
464
    winner_idx = {"this": 2, "other": 1, "conflict": 1}
535
 
    supports_lca_trees = True
536
465
 
537
466
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
538
467
                 interesting_ids=None, reprocess=False, show_base=False,
539
468
                 pb=DummyProgress(), pp=None, change_reporter=None,
540
469
                 interesting_files=None, do_merge=True,
541
 
                 cherrypick=False, lca_trees=None):
 
470
                 cherrypick=False):
542
471
        """Initialize the merger object and perform the merge.
543
472
 
544
473
        :param working_tree: The working tree to apply the merge to
560
489
            be combined with interesting_ids.  If neither interesting_files nor
561
490
            interesting_ids is specified, all files may participate in the
562
491
            merge.
563
 
        :param lca_trees: Can be set to a dictionary of {revision_id:rev_tree}
564
 
            if the ancestry was found to include a criss-cross merge.
565
 
            Otherwise should be None.
566
492
        """
567
493
        object.__init__(self)
568
494
        if interesting_files is not None and interesting_ids is not None:
577
503
        self.cooked_conflicts = []
578
504
        self.reprocess = reprocess
579
505
        self.show_base = show_base
580
 
        self._lca_trees = lca_trees
581
 
        # Uncommenting this will change the default algorithm to always use
582
 
        # _entries_lca. This can be useful for running the test suite and
583
 
        # making sure we haven't missed any corner cases.
584
 
        # if lca_trees is None:
585
 
        #     self._lca_trees = [self.base_tree]
586
506
        self.pb = pb
587
507
        self.pp = pp
588
508
        self.change_reporter = change_reporter
629
549
        return self.tt
630
550
 
631
551
    def _compute_transform(self):
632
 
        if self._lca_trees is None:
633
 
            entries = self._entries3()
634
 
            resolver = self._three_way
635
 
        else:
636
 
            entries = self._entries_lca()
637
 
            resolver = self._lca_multi_way
 
552
        entries = self._entries3()
638
553
        child_pb = ui.ui_factory.nested_progress_bar()
639
554
        try:
640
555
            for num, (file_id, changed, parents3, names3,
641
556
                      executable3) in enumerate(entries):
642
557
                child_pb.update('Preparing file merge', num, len(entries))
643
 
                self._merge_names(file_id, parents3, names3, resolver=resolver)
 
558
                self._merge_names(file_id, parents3, names3)
644
559
                if changed:
645
560
                    file_status = self.merge_contents(file_id)
646
561
                else:
647
562
                    file_status = 'unmodified'
648
563
                self._merge_executable(file_id,
649
 
                    executable3, file_status, resolver=resolver)
 
564
                    executable3, file_status)
650
565
        finally:
651
566
            child_pb.finished()
652
567
        self.fix_root()
676
591
        """
677
592
        result = []
678
593
        iterator = self.other_tree.iter_changes(self.base_tree,
679
 
                include_unchanged=True, specific_files=self.interesting_files,
 
594
                include_unchanged=False, specific_files=self.interesting_files,
680
595
                extra_trees=[self.this_tree])
681
 
        this_entries = dict((e.file_id, e) for p, e in
682
 
                            self.this_tree.iter_entries_by_dir(
683
 
                            self.interesting_ids))
684
596
        for (file_id, paths, changed, versioned, parents, names, kind,
685
597
             executable) in iterator:
686
598
            if (self.interesting_ids is not None and
687
599
                file_id not in self.interesting_ids):
688
600
                continue
689
 
            entry = this_entries.get(file_id)
690
 
            if entry is not None:
 
601
            if file_id in self.this_tree.inventory:
 
602
                entry = self.this_tree.inventory[file_id]
691
603
                this_name = entry.name
692
604
                this_parent = entry.parent_id
693
605
                this_executable = entry.executable
701
613
            result.append((file_id, changed, parents3, names3, executable3))
702
614
        return result
703
615
 
704
 
    def _entries_lca(self):
705
 
        """Gather data about files modified between multiple trees.
706
 
 
707
 
        This compares OTHER versus all LCA trees, and for interesting entries,
708
 
        it then compares with THIS and BASE.
709
 
 
710
 
        For the multi-valued entries, the format will be (BASE, [lca1, lca2])
711
 
        :return: [(file_id, changed, parents, names, executable)]
712
 
            file_id     Simple file_id of the entry
713
 
            changed     Boolean, True if the kind or contents changed
714
 
                        else False
715
 
            parents     ((base, [parent_id, in, lcas]), parent_id_other,
716
 
                         parent_id_this)
717
 
            names       ((base, [name, in, lcas]), name_in_other, name_in_this)
718
 
            executable  ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
719
 
        """
720
 
        if self.interesting_files is not None:
721
 
            lookup_trees = [self.this_tree, self.base_tree]
722
 
            lookup_trees.extend(self._lca_trees)
723
 
            # I think we should include the lca trees as well
724
 
            interesting_ids = self.other_tree.paths2ids(self.interesting_files,
725
 
                                                        lookup_trees)
726
 
        else:
727
 
            interesting_ids = self.interesting_ids
728
 
        result = []
729
 
        walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
730
 
 
731
 
        base_inventory = self.base_tree.inventory
732
 
        this_inventory = self.this_tree.inventory
733
 
        for path, file_id, other_ie, lca_values in walker.iter_all():
734
 
            # Is this modified at all from any of the other trees?
735
 
            if other_ie is None:
736
 
                other_ie = _none_entry
737
 
            if interesting_ids is not None and file_id not in interesting_ids:
738
 
                continue
739
 
 
740
 
            # If other_revision is found in any of the lcas, that means this
741
 
            # node is uninteresting. This is because when merging, if there are
742
 
            # multiple heads(), we have to create a new node. So if we didn't,
743
 
            # we know that the ancestry is linear, and that OTHER did not
744
 
            # modify anything
745
 
            # See doc/developers/lca_merge_resolution.txt for details
746
 
            other_revision = other_ie.revision
747
 
            if other_revision is not None:
748
 
                # We can't use this shortcut when other_revision is None,
749
 
                # because it may be None because things are WorkingTrees, and
750
 
                # not because it is *actually* None.
751
 
                is_unmodified = False
752
 
                for lca_path, ie in lca_values:
753
 
                    if ie is not None and ie.revision == other_revision:
754
 
                        is_unmodified = True
755
 
                        break
756
 
                if is_unmodified:
757
 
                    continue
758
 
 
759
 
            lca_entries = []
760
 
            for lca_path, lca_ie in lca_values:
761
 
                if lca_ie is None:
762
 
                    lca_entries.append(_none_entry)
763
 
                else:
764
 
                    lca_entries.append(lca_ie)
765
 
 
766
 
            if file_id in base_inventory:
767
 
                base_ie = base_inventory[file_id]
768
 
            else:
769
 
                base_ie = _none_entry
770
 
 
771
 
            if file_id in this_inventory:
772
 
                this_ie = this_inventory[file_id]
773
 
            else:
774
 
                this_ie = _none_entry
775
 
 
776
 
            lca_kinds = []
777
 
            lca_parent_ids = []
778
 
            lca_names = []
779
 
            lca_executable = []
780
 
            for lca_ie in lca_entries:
781
 
                lca_kinds.append(lca_ie.kind)
782
 
                lca_parent_ids.append(lca_ie.parent_id)
783
 
                lca_names.append(lca_ie.name)
784
 
                lca_executable.append(lca_ie.executable)
785
 
 
786
 
            kind_winner = self._lca_multi_way(
787
 
                (base_ie.kind, lca_kinds),
788
 
                other_ie.kind, this_ie.kind)
789
 
            parent_id_winner = self._lca_multi_way(
790
 
                (base_ie.parent_id, lca_parent_ids),
791
 
                other_ie.parent_id, this_ie.parent_id)
792
 
            name_winner = self._lca_multi_way(
793
 
                (base_ie.name, lca_names),
794
 
                other_ie.name, this_ie.name)
795
 
 
796
 
            content_changed = True
797
 
            if kind_winner == 'this':
798
 
                # No kind change in OTHER, see if there are *any* changes
799
 
                if other_ie.kind == 'directory':
800
 
                    if parent_id_winner == 'this' and name_winner == 'this':
801
 
                        # No change for this directory in OTHER, skip
802
 
                        continue
803
 
                    content_changed = False
804
 
                elif other_ie.kind is None or other_ie.kind == 'file':
805
 
                    def get_sha1(ie, tree):
806
 
                        if ie.kind != 'file':
807
 
                            return None
808
 
                        return tree.get_file_sha1(file_id)
809
 
                    base_sha1 = get_sha1(base_ie, self.base_tree)
810
 
                    lca_sha1s = [get_sha1(ie, tree) for ie, tree
811
 
                                 in zip(lca_entries, self._lca_trees)]
812
 
                    this_sha1 = get_sha1(this_ie, self.this_tree)
813
 
                    other_sha1 = get_sha1(other_ie, self.other_tree)
814
 
                    sha1_winner = self._lca_multi_way(
815
 
                        (base_sha1, lca_sha1s), other_sha1, this_sha1,
816
 
                        allow_overriding_lca=False)
817
 
                    exec_winner = self._lca_multi_way(
818
 
                        (base_ie.executable, lca_executable),
819
 
                        other_ie.executable, this_ie.executable)
820
 
                    if (parent_id_winner == 'this' and name_winner == 'this'
821
 
                        and sha1_winner == 'this' and exec_winner == 'this'):
822
 
                        # No kind, parent, name, exec, or content change for
823
 
                        # OTHER, so this node is not considered interesting
824
 
                        continue
825
 
                    if sha1_winner == 'this':
826
 
                        content_changed = False
827
 
                elif other_ie.kind == 'symlink':
828
 
                    def get_target(ie, tree):
829
 
                        if ie.kind != 'symlink':
830
 
                            return None
831
 
                        return tree.get_symlink_target(file_id)
832
 
                    base_target = get_target(base_ie, self.base_tree)
833
 
                    lca_targets = [get_target(ie, tree) for ie, tree
834
 
                                   in zip(lca_entries, self._lca_trees)]
835
 
                    this_target = get_target(this_ie, self.this_tree)
836
 
                    other_target = get_target(other_ie, self.other_tree)
837
 
                    target_winner = self._lca_multi_way(
838
 
                        (base_target, lca_targets),
839
 
                        other_target, this_target)
840
 
                    if (parent_id_winner == 'this' and name_winner == 'this'
841
 
                        and target_winner == 'this'):
842
 
                        # No kind, parent, name, or symlink target change
843
 
                        # not interesting
844
 
                        continue
845
 
                    if target_winner == 'this':
846
 
                        content_changed = False
847
 
                elif other_ie.kind == 'tree-reference':
848
 
                    # The 'changed' information seems to be handled at a higher
849
 
                    # level. At least, _entries3 returns False for content
850
 
                    # changed, even when at a new revision_id.
851
 
                    content_changed = False
852
 
                    if (parent_id_winner == 'this' and name_winner == 'this'):
853
 
                        # Nothing interesting
854
 
                        continue
855
 
                else:
856
 
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
857
 
                # XXX: We need to handle kind == 'symlink'
858
 
 
859
 
            # If we have gotten this far, that means something has changed
860
 
            result.append((file_id, content_changed,
861
 
                           ((base_ie.parent_id, lca_parent_ids),
862
 
                            other_ie.parent_id, this_ie.parent_id),
863
 
                           ((base_ie.name, lca_names),
864
 
                            other_ie.name, this_ie.name),
865
 
                           ((base_ie.executable, lca_executable),
866
 
                            other_ie.executable, this_ie.executable)
867
 
                          ))
868
 
        return result
869
 
 
870
 
 
871
616
    def fix_root(self):
872
617
        try:
873
618
            self.tt.final_kind(self.tt.root)
876
621
        if self.tt.final_file_id(self.tt.root) is None:
877
622
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
878
623
                                 self.tt.root)
 
624
        if self.other_tree.inventory.root is None:
 
625
            return
879
626
        other_root_file_id = self.other_tree.get_root_id()
880
 
        if other_root_file_id is None:
881
 
            return
882
627
        other_root = self.tt.trans_id_file_id(other_root_file_id)
883
628
        if other_root == self.tt.root:
884
629
            return
886
631
            self.tt.final_kind(other_root)
887
632
        except NoSuchFile:
888
633
            return
889
 
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
890
 
            # the other tree's root is a non-root in the current tree
891
 
            return
892
634
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
893
635
        self.tt.cancel_creation(other_root)
894
636
        self.tt.cancel_versioning(other_root)
962
704
            return "other"
963
705
 
964
706
    @staticmethod
965
 
    def _lca_multi_way(bases, other, this, allow_overriding_lca=True):
966
 
        """Consider LCAs when determining whether a change has occurred.
967
 
 
968
 
        If LCAS are all identical, this is the same as a _three_way comparison.
969
 
 
970
 
        :param bases: value in (BASE, [LCAS])
971
 
        :param other: value in OTHER
972
 
        :param this: value in THIS
973
 
        :param allow_overriding_lca: If there is more than one unique lca
974
 
            value, allow OTHER to override THIS if it has a new value, and
975
 
            THIS only has an lca value, or vice versa. This is appropriate for
976
 
            truly scalar values, not as much for non-scalars.
977
 
        :return: 'this', 'other', or 'conflict' depending on whether an entry
978
 
            changed or not.
979
 
        """
980
 
        # See doc/developers/lca_tree_merging.txt for details about this
981
 
        # algorithm.
982
 
        if other == this:
983
 
            # Either Ambiguously clean, or nothing was actually changed. We
984
 
            # don't really care
985
 
            return 'this'
986
 
        base_val, lca_vals = bases
987
 
        # Remove 'base_val' from the lca_vals, because it is not interesting
988
 
        filtered_lca_vals = [lca_val for lca_val in lca_vals
989
 
                                      if lca_val != base_val]
990
 
        if len(filtered_lca_vals) == 0:
991
 
            return Merge3Merger._three_way(base_val, other, this)
992
 
 
993
 
        unique_lca_vals = set(filtered_lca_vals)
994
 
        if len(unique_lca_vals) == 1:
995
 
            return Merge3Merger._three_way(unique_lca_vals.pop(), other, this)
996
 
 
997
 
        if allow_overriding_lca:
998
 
            if other in unique_lca_vals:
999
 
                if this in unique_lca_vals:
1000
 
                    # Each side picked a different lca, conflict
1001
 
                    return 'conflict'
1002
 
                else:
1003
 
                    # This has a value which supersedes both lca values, and
1004
 
                    # other only has an lca value
1005
 
                    return 'this'
1006
 
            elif this in unique_lca_vals:
1007
 
                # OTHER has a value which supersedes both lca values, and this
1008
 
                # only has an lca value
1009
 
                return 'other'
1010
 
 
1011
 
        # At this point, the lcas disagree, and the tips disagree
1012
 
        return 'conflict'
1013
 
 
1014
 
    @staticmethod
1015
707
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1016
708
        """Do a three-way test on a scalar.
1017
709
        Return "this", "other" or "conflict", depending whether a value wins.
1049
741
            else:
1050
742
                names.append(entry.name)
1051
743
                parents.append(entry.parent_id)
1052
 
        return self._merge_names(file_id, parents, names,
1053
 
                                 resolver=self._three_way)
 
744
        return self._merge_names(file_id, parents, names)
1054
745
 
1055
 
    def _merge_names(self, file_id, parents, names, resolver):
 
746
    def _merge_names(self, file_id, parents, names):
1056
747
        """Perform a merge on file_id names and parents"""
1057
748
        base_name, other_name, this_name = names
1058
749
        base_parent, other_parent, this_parent = parents
1059
750
 
1060
 
        name_winner = resolver(*names)
 
751
        name_winner = self._three_way(*names)
1061
752
 
1062
 
        parent_id_winner = resolver(*parents)
 
753
        parent_id_winner = self._three_way(*parents)
1063
754
        if this_name is None:
1064
755
            if name_winner == "this":
1065
756
                name_winner = "other"
1088
779
                                parent_trans_id, trans_id)
1089
780
 
1090
781
    def merge_contents(self, file_id):
1091
 
        """Performs a merge on file_id contents."""
 
782
        """Performa a merge on file_id contents."""
1092
783
        def contents_pair(tree):
1093
784
            if file_id not in tree:
1094
785
                return (None, None)
1118
809
        # file kind...
1119
810
        base_pair = contents_pair(self.base_tree)
1120
811
        other_pair = contents_pair(self.other_tree)
1121
 
        if self._lca_trees:
1122
 
            this_pair = contents_pair(self.this_tree)
1123
 
            lca_pairs = [contents_pair(tree) for tree in self._lca_trees]
1124
 
            winner = self._lca_multi_way((base_pair, lca_pairs), other_pair,
1125
 
                                         this_pair, allow_overriding_lca=False)
1126
 
        else:
1127
 
            if base_pair == other_pair:
1128
 
                winner = 'this'
1129
 
            else:
1130
 
                # We delayed evaluating this_pair as long as we can to avoid
1131
 
                # unnecessary sha1 calculation
1132
 
                this_pair = contents_pair(self.this_tree)
1133
 
                winner = self._three_way(base_pair, other_pair, this_pair)
1134
 
        if winner == 'this':
1135
 
            # No interesting changes introduced by OTHER
1136
 
            return "unmodified"
1137
 
        trans_id = self.tt.trans_id_file_id(file_id)
1138
 
        if winner == 'other':
1139
 
            # OTHER is a straight winner, so replace this contents with other
1140
 
            file_in_this = file_id in self.this_tree
1141
 
            if file_in_this:
1142
 
                # Remove any existing contents
1143
 
                self.tt.delete_contents(trans_id)
1144
 
            if file_id in self.other_tree:
1145
 
                # OTHER changed the file
1146
 
                create_from_tree(self.tt, trans_id,
1147
 
                                 self.other_tree, file_id)
1148
 
                if not file_in_this:
1149
 
                    self.tt.version_file(file_id, trans_id)
1150
 
                return "modified"
1151
 
            elif file_in_this:
1152
 
                # OTHER deleted the file
1153
 
                self.tt.unversion_file(trans_id)
1154
 
                return "deleted"
1155
 
        else:
1156
 
            # We have a hypothetical conflict, but if we have files, then we
1157
 
            # can try to merge the content
1158
 
            if this_pair[0] == 'file' and other_pair[0] == 'file':
 
812
        if base_pair == other_pair:
 
813
            # OTHER introduced no changes
 
814
            return "unmodified"
 
815
        this_pair = contents_pair(self.this_tree)
 
816
        if this_pair == other_pair:
 
817
            # THIS and OTHER introduced the same changes
 
818
            return "unmodified"
 
819
        else:
 
820
            trans_id = self.tt.trans_id_file_id(file_id)
 
821
            if this_pair == base_pair:
 
822
                # only OTHER introduced changes
 
823
                if file_id in self.this_tree:
 
824
                    # Remove any existing contents
 
825
                    self.tt.delete_contents(trans_id)
 
826
                if file_id in self.other_tree:
 
827
                    # OTHER changed the file
 
828
                    create_by_entry(self.tt, 
 
829
                                    self.other_tree.inventory[file_id], 
 
830
                                    self.other_tree, trans_id)
 
831
                    if file_id not in self.this_tree.inventory:
 
832
                        self.tt.version_file(file_id, trans_id)
 
833
                    return "modified"
 
834
                elif file_id in self.this_tree.inventory:
 
835
                    # OTHER deleted the file
 
836
                    self.tt.unversion_file(trans_id)
 
837
                    return "deleted"
 
838
            #BOTH THIS and OTHER introduced changes; scalar conflict
 
839
            elif this_pair[0] == "file" and other_pair[0] == "file":
1159
840
                # THIS and OTHER are both files, so text merge.  Either
1160
841
                # BASE is a file, or both converted to files, so at least we
1161
842
                # have agreement that output should be a file.
1163
844
                    self.text_merge(file_id, trans_id)
1164
845
                except BinaryFile:
1165
846
                    return contents_conflict()
1166
 
                if file_id not in self.this_tree:
 
847
                if file_id not in self.this_tree.inventory:
1167
848
                    self.tt.version_file(file_id, trans_id)
1168
849
                try:
1169
850
                    self.tt.tree_kind(trans_id)
1172
853
                    pass
1173
854
                return "modified"
1174
855
            else:
 
856
                # Scalar conflict, can't text merge.  Dump conflicts
1175
857
                return contents_conflict()
1176
858
 
1177
859
    def get_lines(self, tree, file_id):
1249
931
                    versioned = True
1250
932
        return file_group
1251
933
           
1252
 
    def _conflict_file(self, name, parent_id, tree, file_id, suffix,
 
934
    def _conflict_file(self, name, parent_id, tree, file_id, suffix, 
1253
935
                       lines=None):
1254
936
        """Emit a single conflict file."""
1255
937
        name = name + '.' + suffix
1256
938
        trans_id = self.tt.create_path(name, parent_id)
1257
 
        create_from_tree(self.tt, trans_id, tree, file_id, lines)
 
939
        entry = tree.inventory[file_id]
 
940
        create_by_entry(self.tt, entry, tree, trans_id, lines)
1258
941
        return trans_id
1259
942
 
1260
943
    def merge_executable(self, file_id, file_status):
1261
944
        """Perform a merge on the execute bit."""
1262
945
        executable = [self.executable(t, file_id) for t in (self.base_tree,
1263
946
                      self.other_tree, self.this_tree)]
1264
 
        self._merge_executable(file_id, executable, file_status,
1265
 
                               resolver=self._three_way)
 
947
        self._merge_executable(file_id, executable, file_status)
1266
948
 
1267
 
    def _merge_executable(self, file_id, executable, file_status,
1268
 
                          resolver):
 
949
    def _merge_executable(self, file_id, executable, file_status):
1269
950
        """Perform a merge on the execute bit."""
1270
951
        base_executable, other_executable, this_executable = executable
1271
952
        if file_status == "deleted":
1272
953
            return
1273
 
        winner = resolver(*executable)
 
954
        winner = self._three_way(*executable)
1274
955
        if winner == "conflict":
1275
956
        # There must be a None in here, if we have a conflict, but we
1276
957
        # need executability since file status was not deleted.
1587
1268
 
1588
1269
    def get_lines(self, revisions):
1589
1270
        """Get lines for revisions from the backing VersionedFiles.
1590
 
 
 
1271
        
1591
1272
        :raises RevisionNotPresent: on absent texts.
1592
1273
        """
1593
1274
        keys = [(self._key_prefix + (rev,)) for rev in revisions]
1595
1276
        for record in self.vf.get_record_stream(keys, 'unordered', True):
1596
1277
            if record.storage_kind == 'absent':
1597
1278
                raise errors.RevisionNotPresent(record.key, self.vf)
1598
 
            result[record.key[-1]] = osutils.chunks_to_lines(
1599
 
                record.get_bytes_as('chunked'))
 
1279
            result[record.key[-1]] = osutils.split_lines(
 
1280
                record.get_bytes_as('fulltext'))
1600
1281
        return result
1601
1282
 
1602
1283
    def plan_merge(self):
1747
1428
        # rather than a key tuple. We will just map that directly to no common
1748
1429
        # ancestors.
1749
1430
        parent_map = {}
 
1431
        mutter('finding lcas for:\n%s, %s', self.a_rev, self.b_rev)
1750
1432
        while True:
1751
1433
            next_lcas = self.graph.find_lca(*cur_ancestors)
1752
1434
            # Map a plain NULL_REVISION to a simple no-ancestors
1759
1441
            for rev_key in cur_ancestors:
1760
1442
                ordered_parents = tuple(self.graph.find_merge_order(rev_key,
1761
1443
                                                                    next_lcas))
 
1444
                mutter('found %s => %s', rev_key[-1], [p[-1] for p
 
1445
                                                       in ordered_parents])
1762
1446
                parent_map[rev_key] = ordered_parents
1763
1447
            if len(next_lcas) == 0:
1764
1448
                break
1768
1452
            elif len(next_lcas) > 2:
1769
1453
                # More than 2 lca's, fall back to grabbing all nodes between
1770
1454
                # this and the unique lca.
1771
 
                mutter('More than 2 LCAs, falling back to all nodes for:'
1772
 
                       ' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
 
1455
                mutter('More than 2 LCAs, falling back to all nodes for: %s',
 
1456
                       cur_ancestors)
1773
1457
                cur_lcas = next_lcas
1774
1458
                while len(cur_lcas) > 1:
1775
1459
                    cur_lcas = self.graph.find_lca(*cur_lcas)
1778
1462
                    unique_lca = None
1779
1463
                else:
1780
1464
                    unique_lca = list(cur_lcas)[0]
1781
 
                    if unique_lca == NULL_REVISION:
1782
 
                        # find_lca will return a plain 'NULL_REVISION' rather
1783
 
                        # than a key tuple when there is no common ancestor, we
1784
 
                        # prefer to just use None, because it doesn't confuse
1785
 
                        # _get_interesting_texts()
1786
 
                        unique_lca = None
1787
1465
                parent_map.update(self._find_unique_parents(next_lcas,
1788
1466
                                                            unique_lca))
1789
1467
                break
1799
1477
            base_key. base_key will be included. References to nodes outside of
1800
1478
            the ancestor set will also be removed.
1801
1479
        """
 
1480
        # TODO: (performance) We could also "collapse" the graph at this point,
 
1481
        #       to remove uninteresting linear chains of revisions.
1802
1482
        # TODO: this would be simpler if find_unique_ancestors took a list
1803
1483
        #       instead of a single tip, internally it supports it, but it
1804
1484
        #       isn't a "backwards compatible" api change.
1805
1485
        if base_key is None:
1806
1486
            parent_map = dict(self.graph.iter_ancestry(tip_keys))
1807
 
            # We remove NULL_REVISION because it isn't a proper tuple key, and
1808
 
            # thus confuses things like _get_interesting_texts, and our logic
1809
 
            # to add the texts into the memory weave.
1810
 
            if NULL_REVISION in parent_map:
1811
 
                parent_map.pop(NULL_REVISION)
1812
1487
        else:
1813
1488
            interesting = set()
1814
1489
            for tip in tip_keys:
1816
1491
                    self.graph.find_unique_ancestors(tip, [base_key]))
1817
1492
            parent_map = self.graph.get_parent_map(interesting)
1818
1493
            parent_map[base_key] = ()
1819
 
        culled_parent_map, child_map, tails = self._remove_external_references(
1820
 
            parent_map)
1821
 
        # Remove all the tails but base_key
1822
 
        if base_key is not None:
1823
 
            tails.remove(base_key)
1824
 
            self._prune_tails(culled_parent_map, child_map, tails)
1825
 
        # Now remove all the uninteresting 'linear' regions
1826
 
        simple_map = _mod_graph.collapse_linear_regions(culled_parent_map)
1827
 
        return simple_map
1828
 
 
1829
 
    @staticmethod
1830
 
    def _remove_external_references(parent_map):
1831
 
        """Remove references that go outside of the parent map.
1832
 
 
1833
 
        :param parent_map: Something returned from Graph.get_parent_map(keys)
1834
 
        :return: (filtered_parent_map, child_map, tails)
1835
 
            filtered_parent_map is parent_map without external references
1836
 
            child_map is the {parent_key: [child_keys]} mapping
1837
 
            tails is a list of nodes that do not have any parents in the map
1838
 
        """
1839
 
        # TODO: The basic effect of this function seems more generic than
1840
 
        #       _PlanMerge. But the specific details of building a child_map,
1841
 
        #       and computing tails seems very specific to _PlanMerge.
1842
 
        #       Still, should this be in Graph land?
1843
 
        filtered_parent_map = {}
1844
 
        child_map = {}
1845
 
        tails = []
 
1494
        culled_parent_map = {}
 
1495
        extra_base_ancestors = set()
1846
1496
        for key, parent_keys in parent_map.iteritems():
1847
 
            culled_parent_keys = [p for p in parent_keys if p in parent_map]
1848
 
            if not culled_parent_keys:
1849
 
                tails.append(key)
1850
 
            for parent_key in culled_parent_keys:
1851
 
                child_map.setdefault(parent_key, []).append(key)
1852
 
            # TODO: Do we want to do this, it adds overhead for every node,
1853
 
            #       just to say that the node has no children
1854
 
            child_map.setdefault(key, [])
1855
 
            filtered_parent_map[key] = culled_parent_keys
1856
 
        return filtered_parent_map, child_map, tails
1857
 
 
1858
 
    @staticmethod
1859
 
    def _prune_tails(parent_map, child_map, tails_to_remove):
1860
 
        """Remove tails from the parent map.
1861
 
        
1862
 
        This will remove the supplied revisions until no more children have 0
1863
 
        parents.
1864
 
 
1865
 
        :param parent_map: A dict of {child: [parents]}, this dictionary will
1866
 
            be modified in place.
1867
 
        :param tails_to_remove: A list of tips that should be removed,
1868
 
            this list will be consumed
1869
 
        :param child_map: The reverse dict of parent_map ({parent: [children]})
1870
 
            this dict will be modified
1871
 
        :return: None, parent_map will be modified in place.
1872
 
        """
1873
 
        while tails_to_remove:
1874
 
            next = tails_to_remove.pop()
1875
 
            parent_map.pop(next)
1876
 
            children = child_map.pop(next)
1877
 
            for child in children:
1878
 
                child_parents = parent_map[child]
1879
 
                child_parents.remove(next)
1880
 
                if len(child_parents) == 0:
1881
 
                    tails_to_remove.append(child)
 
1497
            culled_parent_keys = tuple([p for p in parent_keys
 
1498
                                           if p in parent_map])
 
1499
            if not culled_parent_keys and key is not base_key:
 
1500
                # We have another 'tail', make sure to bring it into the
 
1501
                # ancestry, so that we don't think all of its lines are unique.
 
1502
                lcas = self.graph.find_lca(key, base_key)
 
1503
                if lcas == set([NULL_REVISION]):
 
1504
                    lcas = ()
 
1505
                if len(lcas) == 0:
 
1506
                    # Nothing to do, no common ancestor
 
1507
                    pass
 
1508
                else:
 
1509
                    # Technically, this isn't 100% correct, but it is better
 
1510
                    # than nothing. If we have more than 1 LCA, we probably
 
1511
                    # should keep tracking the rabbit down the hole, so that we
 
1512
                    # get proper annotations for lines.  For now, though, we
 
1513
                    # just add the first lca, and live with it
 
1514
                    lca = list(lcas)[0]
 
1515
                    extra_base_ancestors.add(lca)
 
1516
                    culled_parent_keys = (lca,)
 
1517
                    if lca not in culled_parent_map:
 
1518
                        culled_parent_map[lca] = ()
 
1519
            culled_parent_map[key] = culled_parent_keys
 
1520
        if extra_base_ancestors:
 
1521
            culled_parent_map[base_key] = self.graph.find_merge_order(base_key,
 
1522
                                                            extra_base_ancestors)
 
1523
        return culled_parent_map
1882
1524
 
1883
1525
    def _get_interesting_texts(self, parent_map):
1884
1526
        """Return a dict of texts we are interested in.
1916
1558
        tip_key = self._key_prefix + (_mod_revision.CURRENT_REVISION,)
1917
1559
        parent_map[tip_key] = (self.a_key, self.b_key)
1918
1560
 
 
1561
        ordering = []
1919
1562
        for seq_num, key, depth, eom in reversed(tsort.merge_sort(parent_map,
1920
1563
                                                                  tip_key)):
1921
1564
            if key == tip_key:
1923
1566
        # for key in tsort.topo_sort(parent_map):
1924
1567
            parent_keys = parent_map[key]
1925
1568
            revision_id = key[-1]
 
1569
            ordering.append(revision_id)
1926
1570
            parent_ids = [k[-1] for k in parent_keys]
1927
1571
            self._weave.add_lines(revision_id, parent_ids,
1928
1572
                                  all_texts[revision_id])
 
1573
        mutter('order in weave: %s', ordering)
1929
1574
 
1930
1575
    def plan_merge(self):
1931
1576
        """Generate a 'plan' for merging the two revisions.
1944
1589
                    raise AssertionError('There was an invalid head: %s != %s'
1945
1590
                                         % (self.b_key, self._head_key))
1946
1591
                plan = 'new-b'
1947
 
            head_rev = self._head_key[-1]
1948
 
            lines = self.get_lines([head_rev])[head_rev]
 
1592
            lines = self.get_lines([self._head_key[-1]])[self._head_key[-1]]
1949
1593
            return ((plan, line) for line in lines)
1950
1594
        return self._weave.plan_merge(self.a_rev, self.b_rev)
1951
1595