~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-09 15:09:12 UTC
  • mto: This revision was merged to the branch mainline in revision 3699.
  • Revision ID: john@arbash-meinel.com-20080909150912-wyttm8he1zsls2ck
Use the right timing function on win32

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
import errno
28
28
    patiencediff,
29
29
    registry,
30
30
    revision as _mod_revision,
31
 
    tree as _mod_tree,
32
31
    tsort,
33
32
    )
34
33
from bzrlib.branch import Branch
55
54
from bzrlib.trace import mutter, warning, note, is_quiet
56
55
from bzrlib.transform import (TransformPreview, TreeTransform,
57
56
                              resolve_conflicts, cook_conflicts,
58
 
                              conflict_pass, FinalPaths, create_from_tree,
 
57
                              conflict_pass, FinalPaths, create_by_entry,
59
58
                              unique_add, ROOT_PARENT)
60
59
from bzrlib.versionedfile import PlanWeaveMerge
61
60
from bzrlib import ui
64
63
 
65
64
 
66
65
def transform_tree(from_tree, to_tree, interesting_ids=None):
67
 
    from_tree.lock_tree_write()
68
 
    try:
69
 
        merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
70
 
                    interesting_ids=interesting_ids, this_tree=from_tree)
71
 
    finally:
72
 
        from_tree.unlock()
 
66
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
 
67
                interesting_ids=interesting_ids, this_tree=from_tree)
73
68
 
74
69
 
75
70
class Merger(object):
76
71
    def __init__(self, this_branch, other_tree=None, base_tree=None,
77
 
                 this_tree=None, pb=None, change_reporter=None,
 
72
                 this_tree=None, pb=DummyProgress(), change_reporter=None,
78
73
                 recurse='down', revision_graph=None):
79
74
        object.__init__(self)
80
75
        self.this_branch = this_branch
93
88
        self.interesting_files = None
94
89
        self.show_base = False
95
90
        self.reprocess = False
96
 
        if pb is None:
97
 
            pb = DummyProgress()
98
91
        self._pb = pb
99
92
        self.pp = None
100
93
        self.recurse = recurse
103
96
        self._revision_graph = revision_graph
104
97
        self._base_is_ancestor = None
105
98
        self._base_is_other_ancestor = None
106
 
        self._is_criss_cross = None
107
 
        self._lca_trees = None
108
 
 
109
 
    def cache_trees_with_revision_ids(self, trees):
110
 
        """Cache any tree in trees if it has a revision_id."""
111
 
        for maybe_tree in trees:
112
 
            if maybe_tree is None:
113
 
                continue
114
 
            try:
115
 
                rev_id = maybe_tree.get_revision_id()
116
 
            except AttributeError:
117
 
                continue
118
 
            self._cached_trees[rev_id] = maybe_tree
119
99
 
120
100
    @property
121
101
    def revision_graph(self):
149
129
                                      _set_base_is_other_ancestor)
150
130
 
151
131
    @staticmethod
152
 
    def from_uncommitted(tree, other_tree, pb=None, base_tree=None):
 
132
    def from_uncommitted(tree, other_tree, pb):
153
133
        """Return a Merger for uncommitted changes in other_tree.
154
134
 
155
135
        :param tree: The tree to merge into
156
136
        :param other_tree: The tree to get uncommitted changes from
157
137
        :param pb: A progress indicator
158
 
        :param base_tree: The basis to use for the merge.  If unspecified,
159
 
            other_tree.basis_tree() will be used.
160
138
        """
161
 
        if base_tree is None:
162
 
            base_tree = other_tree.basis_tree()
163
 
        merger = Merger(tree.branch, other_tree, base_tree, tree, pb)
 
139
        merger = Merger(tree.branch, other_tree, other_tree.basis_tree(), tree,
 
140
                        pb)
164
141
        merger.base_rev_id = merger.base_tree.get_revision_id()
165
142
        merger.other_rev_id = None
166
143
        merger.other_basis = merger.base_rev_id
192
169
 
193
170
    @staticmethod
194
171
    def from_revision_ids(pb, tree, other, base=None, other_branch=None,
195
 
                          base_branch=None, revision_graph=None,
196
 
                          tree_branch=None):
 
172
                          base_branch=None, revision_graph=None):
197
173
        """Return a Merger for revision-ids.
198
174
 
199
 
        :param pb: A progress indicator
200
175
        :param tree: The tree to merge changes into
201
176
        :param other: The revision-id to use as OTHER
202
177
        :param base: The revision-id to use as BASE.  If not specified, will
207
182
            not supplied, other_branch or tree.branch will be used.
208
183
        :param revision_graph: If you have a revision_graph precomputed, pass
209
184
            it in, otherwise it will be created for you.
210
 
        :param tree_branch: The branch associated with tree.  If not supplied,
211
 
            tree.branch will be used.
 
185
        :param pb: A progress indicator
212
186
        """
213
 
        if tree_branch is None:
214
 
            tree_branch = tree.branch
215
 
        merger = Merger(tree_branch, this_tree=tree, pb=pb,
 
187
        merger = Merger(tree.branch, this_tree=tree, pb=pb,
216
188
                        revision_graph=revision_graph)
217
189
        if other_branch is None:
218
190
            other_branch = tree.branch
258
230
 
259
231
        if self.other_rev_id is None:
260
232
            other_basis_tree = self.revision_tree(self.other_basis)
261
 
            if other_basis_tree.has_changes(self.other_tree):
 
233
            changes = other_basis_tree.changes_from(self.other_tree)
 
234
            if changes.has_changed():
262
235
                raise WorkingTreeNotRevision(self.this_tree)
263
236
            other_rev_id = self.other_basis
264
237
            self.other_tree = other_basis_tree
290
263
            basis_tree = self.revision_tree(self.this_tree.last_revision())
291
264
        except errors.NoSuchRevision:
292
265
            basis_tree = self.this_tree.basis_tree()
293
 
        if not self.this_tree.has_changes(basis_tree):
 
266
        changes = self.this_tree.changes_from(basis_tree)
 
267
        if not changes.has_changed():
294
268
            self.this_rev_id = self.this_basis
295
269
 
296
270
    def set_interesting_files(self, file_list):
379
353
                     ensure_null(self.other_basis)]
380
354
        if NULL_REVISION in revisions:
381
355
            self.base_rev_id = NULL_REVISION
382
 
            self.base_tree = self.revision_tree(self.base_rev_id)
383
 
            self._is_criss_cross = False
384
356
        else:
385
 
            lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
386
 
            self._is_criss_cross = False
387
 
            if len(lcas) == 0:
388
 
                self.base_rev_id = NULL_REVISION
389
 
            elif len(lcas) == 1:
390
 
                self.base_rev_id = list(lcas)[0]
391
 
            else: # len(lcas) > 1
392
 
                if len(lcas) > 2:
393
 
                    # find_unique_lca can only handle 2 nodes, so we have to
394
 
                    # start back at the beginning. It is a shame to traverse
395
 
                    # the graph again, but better than re-implementing
396
 
                    # find_unique_lca.
397
 
                    self.base_rev_id = self.revision_graph.find_unique_lca(
398
 
                                            revisions[0], revisions[1])
399
 
                else:
400
 
                    self.base_rev_id = self.revision_graph.find_unique_lca(
401
 
                                            *lcas)
402
 
                self._is_criss_cross = True
 
357
            self.base_rev_id, steps = self.revision_graph.find_unique_lca(
 
358
                revisions[0], revisions[1], count_steps=True)
403
359
            if self.base_rev_id == NULL_REVISION:
404
360
                raise UnrelatedBranches()
405
 
            if self._is_criss_cross:
 
361
            if steps > 1:
406
362
                warning('Warning: criss-cross merge encountered.  See bzr'
407
363
                        ' help criss-cross.')
408
 
                mutter('Criss-cross lcas: %r' % lcas)
409
 
                interesting_revision_ids = [self.base_rev_id]
410
 
                interesting_revision_ids.extend(lcas)
411
 
                interesting_trees = dict((t.get_revision_id(), t)
412
 
                    for t in self.this_branch.repository.revision_trees(
413
 
                        interesting_revision_ids))
414
 
                self._cached_trees.update(interesting_trees)
415
 
                self.base_tree = interesting_trees.pop(self.base_rev_id)
416
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
417
 
                    revisions[0], lcas)
418
 
                self._lca_trees = [interesting_trees[key]
419
 
                                   for key in sorted_lca_keys]
420
 
            else:
421
 
                self.base_tree = self.revision_tree(self.base_rev_id)
 
364
        self.base_tree = self.revision_tree(self.base_rev_id)
422
365
        self.base_is_ancestor = True
423
366
        self.base_is_other_ancestor = True
424
 
        mutter('Base revid: %r' % self.base_rev_id)
425
367
 
426
368
    def set_base(self, base_revision):
427
369
        """Set the base revision to use for the merge.
467
409
        if self.merge_type.supports_cherrypick:
468
410
            kwargs['cherrypick'] = (not self.base_is_ancestor or
469
411
                                    not self.base_is_other_ancestor)
470
 
        if self._is_criss_cross and getattr(self.merge_type,
471
 
                                            'supports_lca_trees', False):
472
 
            kwargs['lca_trees'] = self._lca_trees
473
412
        return self.merge_type(pb=self._pb,
474
413
                               change_reporter=self.change_reporter,
475
414
                               **kwargs)
476
415
 
477
416
    def _do_merge_to(self, merge):
478
 
        if self.other_branch is not None:
479
 
            self.other_branch.update_references(self.this_branch)
480
417
        merge.do_merge()
481
418
        if self.recurse == 'down':
482
419
            for relpath, file_id in self.this_tree.iter_references():
494
431
                    sub_tree.branch.repository.revision_tree(base_revision)
495
432
                sub_merge.base_rev_id = base_revision
496
433
                sub_merge.do_merge()
497
 
 
 
434
        
498
435
    def do_merge(self):
499
436
        self.this_tree.lock_tree_write()
500
437
        try:
523
460
        return len(merge.cooked_conflicts)
524
461
 
525
462
 
526
 
class _InventoryNoneEntry(object):
527
 
    """This represents an inventory entry which *isn't there*.
528
 
 
529
 
    It simplifies the merging logic if we always have an InventoryEntry, even
530
 
    if it isn't actually present
531
 
    """
532
 
    executable = None
533
 
    kind = None
534
 
    name = None
535
 
    parent_id = None
536
 
    revision = None
537
 
    symlink_target = None
538
 
    text_sha1 = None
539
 
 
540
 
_none_entry = _InventoryNoneEntry()
541
 
 
542
 
 
543
463
class Merge3Merger(object):
544
464
    """Three-way merger that uses the merge3 text merger"""
545
465
    requires_base = True
549
469
    supports_cherrypick = True
550
470
    supports_reverse_cherrypick = True
551
471
    winner_idx = {"this": 2, "other": 1, "conflict": 1}
552
 
    supports_lca_trees = True
553
472
 
554
 
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
 
473
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
555
474
                 interesting_ids=None, reprocess=False, show_base=False,
556
475
                 pb=DummyProgress(), pp=None, change_reporter=None,
557
476
                 interesting_files=None, do_merge=True,
558
 
                 cherrypick=False, lca_trees=None):
 
477
                 cherrypick=False):
559
478
        """Initialize the merger object and perform the merge.
560
479
 
561
480
        :param working_tree: The working tree to apply the merge to
562
481
        :param this_tree: The local tree in the merge operation
563
482
        :param base_tree: The common tree in the merge operation
564
 
        :param other_tree: The other tree to merge changes from
 
483
        :param other_tree: The other other tree to merge changes from
565
484
        :param interesting_ids: The file_ids of files that should be
566
485
            participate in the merge.  May not be combined with
567
486
            interesting_files.
577
496
            be combined with interesting_ids.  If neither interesting_files nor
578
497
            interesting_ids is specified, all files may participate in the
579
498
            merge.
580
 
        :param lca_trees: Can be set to a dictionary of {revision_id:rev_tree}
581
 
            if the ancestry was found to include a criss-cross merge.
582
 
            Otherwise should be None.
583
499
        """
584
500
        object.__init__(self)
585
501
        if interesting_files is not None and interesting_ids is not None:
594
510
        self.cooked_conflicts = []
595
511
        self.reprocess = reprocess
596
512
        self.show_base = show_base
597
 
        self._lca_trees = lca_trees
598
 
        # Uncommenting this will change the default algorithm to always use
599
 
        # _entries_lca. This can be useful for running the test suite and
600
 
        # making sure we haven't missed any corner cases.
601
 
        # if lca_trees is None:
602
 
        #     self._lca_trees = [self.base_tree]
603
513
        self.pb = pb
604
514
        self.pp = pp
605
515
        self.change_reporter = change_reporter
646
556
        return self.tt
647
557
 
648
558
    def _compute_transform(self):
649
 
        if self._lca_trees is None:
650
 
            entries = self._entries3()
651
 
            resolver = self._three_way
652
 
        else:
653
 
            entries = self._entries_lca()
654
 
            resolver = self._lca_multi_way
 
559
        entries = self._entries3()
655
560
        child_pb = ui.ui_factory.nested_progress_bar()
656
561
        try:
657
562
            for num, (file_id, changed, parents3, names3,
658
563
                      executable3) in enumerate(entries):
659
564
                child_pb.update('Preparing file merge', num, len(entries))
660
 
                self._merge_names(file_id, parents3, names3, resolver=resolver)
 
565
                self._merge_names(file_id, parents3, names3)
661
566
                if changed:
662
567
                    file_status = self.merge_contents(file_id)
663
568
                else:
664
569
                    file_status = 'unmodified'
665
570
                self._merge_executable(file_id,
666
 
                    executable3, file_status, resolver=resolver)
 
571
                    executable3, file_status)
667
572
        finally:
668
573
            child_pb.finished()
669
574
        self.fix_root()
695
600
        iterator = self.other_tree.iter_changes(self.base_tree,
696
601
                include_unchanged=True, specific_files=self.interesting_files,
697
602
                extra_trees=[self.this_tree])
698
 
        this_entries = dict((e.file_id, e) for p, e in
699
 
                            self.this_tree.iter_entries_by_dir(
700
 
                            self.interesting_ids))
701
603
        for (file_id, paths, changed, versioned, parents, names, kind,
702
604
             executable) in iterator:
703
605
            if (self.interesting_ids is not None and
704
606
                file_id not in self.interesting_ids):
705
607
                continue
706
 
            entry = this_entries.get(file_id)
707
 
            if entry is not None:
 
608
            if file_id in self.this_tree.inventory:
 
609
                entry = self.this_tree.inventory[file_id]
708
610
                this_name = entry.name
709
611
                this_parent = entry.parent_id
710
612
                this_executable = entry.executable
718
620
            result.append((file_id, changed, parents3, names3, executable3))
719
621
        return result
720
622
 
721
 
    def _entries_lca(self):
722
 
        """Gather data about files modified between multiple trees.
723
 
 
724
 
        This compares OTHER versus all LCA trees, and for interesting entries,
725
 
        it then compares with THIS and BASE.
726
 
 
727
 
        For the multi-valued entries, the format will be (BASE, [lca1, lca2])
728
 
        :return: [(file_id, changed, parents, names, executable)]
729
 
            file_id     Simple file_id of the entry
730
 
            changed     Boolean, True if the kind or contents changed
731
 
                        else False
732
 
            parents     ((base, [parent_id, in, lcas]), parent_id_other,
733
 
                         parent_id_this)
734
 
            names       ((base, [name, in, lcas]), name_in_other, name_in_this)
735
 
            executable  ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
736
 
        """
737
 
        if self.interesting_files is not None:
738
 
            lookup_trees = [self.this_tree, self.base_tree]
739
 
            lookup_trees.extend(self._lca_trees)
740
 
            # I think we should include the lca trees as well
741
 
            interesting_ids = self.other_tree.paths2ids(self.interesting_files,
742
 
                                                        lookup_trees)
743
 
        else:
744
 
            interesting_ids = self.interesting_ids
745
 
        result = []
746
 
        walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
747
 
 
748
 
        base_inventory = self.base_tree.inventory
749
 
        this_inventory = self.this_tree.inventory
750
 
        for path, file_id, other_ie, lca_values in walker.iter_all():
751
 
            # Is this modified at all from any of the other trees?
752
 
            if other_ie is None:
753
 
                other_ie = _none_entry
754
 
            if interesting_ids is not None and file_id not in interesting_ids:
755
 
                continue
756
 
 
757
 
            # If other_revision is found in any of the lcas, that means this
758
 
            # node is uninteresting. This is because when merging, if there are
759
 
            # multiple heads(), we have to create a new node. So if we didn't,
760
 
            # we know that the ancestry is linear, and that OTHER did not
761
 
            # modify anything
762
 
            # See doc/developers/lca_merge_resolution.txt for details
763
 
            other_revision = other_ie.revision
764
 
            if other_revision is not None:
765
 
                # We can't use this shortcut when other_revision is None,
766
 
                # because it may be None because things are WorkingTrees, and
767
 
                # not because it is *actually* None.
768
 
                is_unmodified = False
769
 
                for lca_path, ie in lca_values:
770
 
                    if ie is not None and ie.revision == other_revision:
771
 
                        is_unmodified = True
772
 
                        break
773
 
                if is_unmodified:
774
 
                    continue
775
 
 
776
 
            lca_entries = []
777
 
            for lca_path, lca_ie in lca_values:
778
 
                if lca_ie is None:
779
 
                    lca_entries.append(_none_entry)
780
 
                else:
781
 
                    lca_entries.append(lca_ie)
782
 
 
783
 
            if file_id in base_inventory:
784
 
                base_ie = base_inventory[file_id]
785
 
            else:
786
 
                base_ie = _none_entry
787
 
 
788
 
            if file_id in this_inventory:
789
 
                this_ie = this_inventory[file_id]
790
 
            else:
791
 
                this_ie = _none_entry
792
 
 
793
 
            lca_kinds = []
794
 
            lca_parent_ids = []
795
 
            lca_names = []
796
 
            lca_executable = []
797
 
            for lca_ie in lca_entries:
798
 
                lca_kinds.append(lca_ie.kind)
799
 
                lca_parent_ids.append(lca_ie.parent_id)
800
 
                lca_names.append(lca_ie.name)
801
 
                lca_executable.append(lca_ie.executable)
802
 
 
803
 
            kind_winner = self._lca_multi_way(
804
 
                (base_ie.kind, lca_kinds),
805
 
                other_ie.kind, this_ie.kind)
806
 
            parent_id_winner = self._lca_multi_way(
807
 
                (base_ie.parent_id, lca_parent_ids),
808
 
                other_ie.parent_id, this_ie.parent_id)
809
 
            name_winner = self._lca_multi_way(
810
 
                (base_ie.name, lca_names),
811
 
                other_ie.name, this_ie.name)
812
 
 
813
 
            content_changed = True
814
 
            if kind_winner == 'this':
815
 
                # No kind change in OTHER, see if there are *any* changes
816
 
                if other_ie.kind == 'directory':
817
 
                    if parent_id_winner == 'this' and name_winner == 'this':
818
 
                        # No change for this directory in OTHER, skip
819
 
                        continue
820
 
                    content_changed = False
821
 
                elif other_ie.kind is None or other_ie.kind == 'file':
822
 
                    def get_sha1(ie, tree):
823
 
                        if ie.kind != 'file':
824
 
                            return None
825
 
                        return tree.get_file_sha1(file_id)
826
 
                    base_sha1 = get_sha1(base_ie, self.base_tree)
827
 
                    lca_sha1s = [get_sha1(ie, tree) for ie, tree
828
 
                                 in zip(lca_entries, self._lca_trees)]
829
 
                    this_sha1 = get_sha1(this_ie, self.this_tree)
830
 
                    other_sha1 = get_sha1(other_ie, self.other_tree)
831
 
                    sha1_winner = self._lca_multi_way(
832
 
                        (base_sha1, lca_sha1s), other_sha1, this_sha1,
833
 
                        allow_overriding_lca=False)
834
 
                    exec_winner = self._lca_multi_way(
835
 
                        (base_ie.executable, lca_executable),
836
 
                        other_ie.executable, this_ie.executable)
837
 
                    if (parent_id_winner == 'this' and name_winner == 'this'
838
 
                        and sha1_winner == 'this' and exec_winner == 'this'):
839
 
                        # No kind, parent, name, exec, or content change for
840
 
                        # OTHER, so this node is not considered interesting
841
 
                        continue
842
 
                    if sha1_winner == 'this':
843
 
                        content_changed = False
844
 
                elif other_ie.kind == 'symlink':
845
 
                    def get_target(ie, tree):
846
 
                        if ie.kind != 'symlink':
847
 
                            return None
848
 
                        return tree.get_symlink_target(file_id)
849
 
                    base_target = get_target(base_ie, self.base_tree)
850
 
                    lca_targets = [get_target(ie, tree) for ie, tree
851
 
                                   in zip(lca_entries, self._lca_trees)]
852
 
                    this_target = get_target(this_ie, self.this_tree)
853
 
                    other_target = get_target(other_ie, self.other_tree)
854
 
                    target_winner = self._lca_multi_way(
855
 
                        (base_target, lca_targets),
856
 
                        other_target, this_target)
857
 
                    if (parent_id_winner == 'this' and name_winner == 'this'
858
 
                        and target_winner == 'this'):
859
 
                        # No kind, parent, name, or symlink target change
860
 
                        # not interesting
861
 
                        continue
862
 
                    if target_winner == 'this':
863
 
                        content_changed = False
864
 
                elif other_ie.kind == 'tree-reference':
865
 
                    # The 'changed' information seems to be handled at a higher
866
 
                    # level. At least, _entries3 returns False for content
867
 
                    # changed, even when at a new revision_id.
868
 
                    content_changed = False
869
 
                    if (parent_id_winner == 'this' and name_winner == 'this'):
870
 
                        # Nothing interesting
871
 
                        continue
872
 
                else:
873
 
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
874
 
                # XXX: We need to handle kind == 'symlink'
875
 
 
876
 
            # If we have gotten this far, that means something has changed
877
 
            result.append((file_id, content_changed,
878
 
                           ((base_ie.parent_id, lca_parent_ids),
879
 
                            other_ie.parent_id, this_ie.parent_id),
880
 
                           ((base_ie.name, lca_names),
881
 
                            other_ie.name, this_ie.name),
882
 
                           ((base_ie.executable, lca_executable),
883
 
                            other_ie.executable, this_ie.executable)
884
 
                          ))
885
 
        return result
886
 
 
887
 
 
888
623
    def fix_root(self):
889
624
        try:
890
625
            self.tt.final_kind(self.tt.root)
891
626
        except NoSuchFile:
892
627
            self.tt.cancel_deletion(self.tt.root)
893
628
        if self.tt.final_file_id(self.tt.root) is None:
894
 
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
 
629
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
895
630
                                 self.tt.root)
 
631
        if self.other_tree.inventory.root is None:
 
632
            return
896
633
        other_root_file_id = self.other_tree.get_root_id()
897
 
        if other_root_file_id is None:
898
 
            return
899
634
        other_root = self.tt.trans_id_file_id(other_root_file_id)
900
635
        if other_root == self.tt.root:
901
636
            return
940
675
        if entry is None:
941
676
            return None
942
677
        return entry.name
943
 
 
 
678
    
944
679
    @staticmethod
945
680
    def contents_sha1(tree, file_id):
946
681
        """Determine the sha1 of the file contents (used as a key method)."""
979
714
            return "other"
980
715
 
981
716
    @staticmethod
982
 
    def _lca_multi_way(bases, other, this, allow_overriding_lca=True):
983
 
        """Consider LCAs when determining whether a change has occurred.
984
 
 
985
 
        If LCAS are all identical, this is the same as a _three_way comparison.
986
 
 
987
 
        :param bases: value in (BASE, [LCAS])
988
 
        :param other: value in OTHER
989
 
        :param this: value in THIS
990
 
        :param allow_overriding_lca: If there is more than one unique lca
991
 
            value, allow OTHER to override THIS if it has a new value, and
992
 
            THIS only has an lca value, or vice versa. This is appropriate for
993
 
            truly scalar values, not as much for non-scalars.
994
 
        :return: 'this', 'other', or 'conflict' depending on whether an entry
995
 
            changed or not.
996
 
        """
997
 
        # See doc/developers/lca_tree_merging.txt for details about this
998
 
        # algorithm.
999
 
        if other == this:
1000
 
            # Either Ambiguously clean, or nothing was actually changed. We
1001
 
            # don't really care
1002
 
            return 'this'
1003
 
        base_val, lca_vals = bases
1004
 
        # Remove 'base_val' from the lca_vals, because it is not interesting
1005
 
        filtered_lca_vals = [lca_val for lca_val in lca_vals
1006
 
                                      if lca_val != base_val]
1007
 
        if len(filtered_lca_vals) == 0:
1008
 
            return Merge3Merger._three_way(base_val, other, this)
1009
 
 
1010
 
        unique_lca_vals = set(filtered_lca_vals)
1011
 
        if len(unique_lca_vals) == 1:
1012
 
            return Merge3Merger._three_way(unique_lca_vals.pop(), other, this)
1013
 
 
1014
 
        if allow_overriding_lca:
1015
 
            if other in unique_lca_vals:
1016
 
                if this in unique_lca_vals:
1017
 
                    # Each side picked a different lca, conflict
1018
 
                    return 'conflict'
1019
 
                else:
1020
 
                    # This has a value which supersedes both lca values, and
1021
 
                    # other only has an lca value
1022
 
                    return 'this'
1023
 
            elif this in unique_lca_vals:
1024
 
                # OTHER has a value which supersedes both lca values, and this
1025
 
                # only has an lca value
1026
 
                return 'other'
1027
 
 
1028
 
        # At this point, the lcas disagree, and the tips disagree
1029
 
        return 'conflict'
1030
 
 
1031
 
    @staticmethod
1032
717
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1033
718
        """Do a three-way test on a scalar.
1034
719
        Return "this", "other" or "conflict", depending whether a value wins.
1066
751
            else:
1067
752
                names.append(entry.name)
1068
753
                parents.append(entry.parent_id)
1069
 
        return self._merge_names(file_id, parents, names,
1070
 
                                 resolver=self._three_way)
 
754
        return self._merge_names(file_id, parents, names)
1071
755
 
1072
 
    def _merge_names(self, file_id, parents, names, resolver):
 
756
    def _merge_names(self, file_id, parents, names):
1073
757
        """Perform a merge on file_id names and parents"""
1074
758
        base_name, other_name, this_name = names
1075
759
        base_parent, other_parent, this_parent = parents
1076
760
 
1077
 
        name_winner = resolver(*names)
 
761
        name_winner = self._three_way(*names)
1078
762
 
1079
 
        parent_id_winner = resolver(*parents)
 
763
        parent_id_winner = self._three_way(*parents)
1080
764
        if this_name is None:
1081
765
            if name_winner == "this":
1082
766
                name_winner = "other"
1086
770
            return
1087
771
        if name_winner == "conflict":
1088
772
            trans_id = self.tt.trans_id_file_id(file_id)
1089
 
            self._raw_conflicts.append(('name conflict', trans_id,
 
773
            self._raw_conflicts.append(('name conflict', trans_id, 
1090
774
                                        this_name, other_name))
1091
775
        if parent_id_winner == "conflict":
1092
776
            trans_id = self.tt.trans_id_file_id(file_id)
1093
 
            self._raw_conflicts.append(('parent conflict', trans_id,
 
777
            self._raw_conflicts.append(('parent conflict', trans_id, 
1094
778
                                        this_parent, other_parent))
1095
779
        if other_name is None:
1096
 
            # it doesn't matter whether the result was 'other' or
 
780
            # it doesn't matter whether the result was 'other' or 
1097
781
            # 'conflict'-- if there's no 'other', we leave it alone.
1098
782
            return
1099
783
        # if we get here, name_winner and parent_winner are set to safe values.
1105
789
                                parent_trans_id, trans_id)
1106
790
 
1107
791
    def merge_contents(self, file_id):
1108
 
        """Performs a merge on file_id contents."""
 
792
        """Performa a merge on file_id contents."""
1109
793
        def contents_pair(tree):
1110
794
            if file_id not in tree:
1111
795
                return (None, None)
1126
810
                self.tt.unversion_file(trans_id)
1127
811
                if file_id in self.this_tree:
1128
812
                    self.tt.delete_contents(trans_id)
1129
 
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
813
            file_group = self._dump_conflicts(name, parent_id, file_id, 
1130
814
                                              set_version=True)
1131
815
            self._raw_conflicts.append(('contents conflict', file_group))
1132
816
 
1135
819
        # file kind...
1136
820
        base_pair = contents_pair(self.base_tree)
1137
821
        other_pair = contents_pair(self.other_tree)
1138
 
        if self._lca_trees:
1139
 
            this_pair = contents_pair(self.this_tree)
1140
 
            lca_pairs = [contents_pair(tree) for tree in self._lca_trees]
1141
 
            winner = self._lca_multi_way((base_pair, lca_pairs), other_pair,
1142
 
                                         this_pair, allow_overriding_lca=False)
1143
 
        else:
1144
 
            if base_pair == other_pair:
1145
 
                winner = 'this'
1146
 
            else:
1147
 
                # We delayed evaluating this_pair as long as we can to avoid
1148
 
                # unnecessary sha1 calculation
1149
 
                this_pair = contents_pair(self.this_tree)
1150
 
                winner = self._three_way(base_pair, other_pair, this_pair)
1151
 
        if winner == 'this':
1152
 
            # No interesting changes introduced by OTHER
1153
 
            return "unmodified"
1154
 
        trans_id = self.tt.trans_id_file_id(file_id)
1155
 
        if winner == 'other':
1156
 
            # OTHER is a straight winner, so replace this contents with other
1157
 
            file_in_this = file_id in self.this_tree
1158
 
            if file_in_this:
1159
 
                # Remove any existing contents
1160
 
                self.tt.delete_contents(trans_id)
1161
 
            if file_id in self.other_tree:
1162
 
                # OTHER changed the file
1163
 
                create_from_tree(self.tt, trans_id,
1164
 
                                 self.other_tree, file_id)
1165
 
                if not file_in_this:
1166
 
                    self.tt.version_file(file_id, trans_id)
1167
 
                return "modified"
1168
 
            elif file_in_this:
1169
 
                # OTHER deleted the file
1170
 
                self.tt.unversion_file(trans_id)
1171
 
                return "deleted"
1172
 
        else:
1173
 
            # We have a hypothetical conflict, but if we have files, then we
1174
 
            # can try to merge the content
1175
 
            if this_pair[0] == 'file' and other_pair[0] == 'file':
 
822
        if base_pair == other_pair:
 
823
            # OTHER introduced no changes
 
824
            return "unmodified"
 
825
        this_pair = contents_pair(self.this_tree)
 
826
        if this_pair == other_pair:
 
827
            # THIS and OTHER introduced the same changes
 
828
            return "unmodified"
 
829
        else:
 
830
            trans_id = self.tt.trans_id_file_id(file_id)
 
831
            if this_pair == base_pair:
 
832
                # only OTHER introduced changes
 
833
                if file_id in self.this_tree:
 
834
                    # Remove any existing contents
 
835
                    self.tt.delete_contents(trans_id)
 
836
                if file_id in self.other_tree:
 
837
                    # OTHER changed the file
 
838
                    create_by_entry(self.tt, 
 
839
                                    self.other_tree.inventory[file_id], 
 
840
                                    self.other_tree, trans_id)
 
841
                    if file_id not in self.this_tree.inventory:
 
842
                        self.tt.version_file(file_id, trans_id)
 
843
                    return "modified"
 
844
                elif file_id in self.this_tree.inventory:
 
845
                    # OTHER deleted the file
 
846
                    self.tt.unversion_file(trans_id)
 
847
                    return "deleted"
 
848
            #BOTH THIS and OTHER introduced changes; scalar conflict
 
849
            elif this_pair[0] == "file" and other_pair[0] == "file":
1176
850
                # THIS and OTHER are both files, so text merge.  Either
1177
851
                # BASE is a file, or both converted to files, so at least we
1178
852
                # have agreement that output should be a file.
1180
854
                    self.text_merge(file_id, trans_id)
1181
855
                except BinaryFile:
1182
856
                    return contents_conflict()
1183
 
                if file_id not in self.this_tree:
 
857
                if file_id not in self.this_tree.inventory:
1184
858
                    self.tt.version_file(file_id, trans_id)
1185
859
                try:
1186
860
                    self.tt.tree_kind(trans_id)
1189
863
                    pass
1190
864
                return "modified"
1191
865
            else:
 
866
                # Scalar conflict, can't text merge.  Dump conflicts
1192
867
                return contents_conflict()
1193
868
 
1194
869
    def get_lines(self, tree, file_id):
1219
894
 
1220
895
        def iter_merge3(retval):
1221
896
            retval["text_conflicts"] = False
1222
 
            for line in m3.merge_lines(name_a = "TREE",
1223
 
                                       name_b = "MERGE-SOURCE",
 
897
            for line in m3.merge_lines(name_a = "TREE", 
 
898
                                       name_b = "MERGE-SOURCE", 
1224
899
                                       name_base = "BASE-REVISION",
1225
 
                                       start_marker=start_marker,
 
900
                                       start_marker=start_marker, 
1226
901
                                       base_marker=base_marker,
1227
902
                                       reprocess=self.reprocess):
1228
903
                if line.startswith(start_marker):
1237
912
            self._raw_conflicts.append(('text conflict', trans_id))
1238
913
            name = self.tt.final_name(trans_id)
1239
914
            parent_id = self.tt.final_parent(trans_id)
1240
 
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
915
            file_group = self._dump_conflicts(name, parent_id, file_id, 
1241
916
                                              this_lines, base_lines,
1242
917
                                              other_lines)
1243
918
            file_group.append(trans_id)
1244
919
 
1245
 
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
 
920
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None, 
1246
921
                        base_lines=None, other_lines=None, set_version=False,
1247
922
                        no_base=False):
1248
923
        """Emit conflict files.
1250
925
        determined automatically.  If set_version is true, the .OTHER, .THIS
1251
926
        or .BASE (in that order) will be created as versioned files.
1252
927
        """
1253
 
        data = [('OTHER', self.other_tree, other_lines),
 
928
        data = [('OTHER', self.other_tree, other_lines), 
1254
929
                ('THIS', self.this_tree, this_lines)]
1255
930
        if not no_base:
1256
931
            data.append(('BASE', self.base_tree, base_lines))
1265
940
                    self.tt.version_file(file_id, trans_id)
1266
941
                    versioned = True
1267
942
        return file_group
1268
 
 
1269
 
    def _conflict_file(self, name, parent_id, tree, file_id, suffix,
 
943
           
 
944
    def _conflict_file(self, name, parent_id, tree, file_id, suffix, 
1270
945
                       lines=None):
1271
946
        """Emit a single conflict file."""
1272
947
        name = name + '.' + suffix
1273
948
        trans_id = self.tt.create_path(name, parent_id)
1274
 
        create_from_tree(self.tt, trans_id, tree, file_id, lines)
 
949
        entry = tree.inventory[file_id]
 
950
        create_by_entry(self.tt, entry, tree, trans_id, lines)
1275
951
        return trans_id
1276
952
 
1277
953
    def merge_executable(self, file_id, file_status):
1278
954
        """Perform a merge on the execute bit."""
1279
955
        executable = [self.executable(t, file_id) for t in (self.base_tree,
1280
956
                      self.other_tree, self.this_tree)]
1281
 
        self._merge_executable(file_id, executable, file_status,
1282
 
                               resolver=self._three_way)
 
957
        self._merge_executable(file_id, executable, file_status)
1283
958
 
1284
 
    def _merge_executable(self, file_id, executable, file_status,
1285
 
                          resolver):
 
959
    def _merge_executable(self, file_id, executable, file_status):
1286
960
        """Perform a merge on the execute bit."""
1287
961
        base_executable, other_executable, this_executable = executable
1288
962
        if file_status == "deleted":
1289
963
            return
1290
 
        winner = resolver(*executable)
 
964
        winner = self._three_way(*executable)
1291
965
        if winner == "conflict":
1292
966
        # There must be a None in here, if we have a conflict, but we
1293
967
        # need executability since file status was not deleted.
1329
1003
                conflict_args = conflict[2:]
1330
1004
                if trans_id not in name_conflicts:
1331
1005
                    name_conflicts[trans_id] = {}
1332
 
                unique_add(name_conflicts[trans_id], conflict_type,
 
1006
                unique_add(name_conflicts[trans_id], conflict_type, 
1333
1007
                           conflict_args)
1334
1008
            if conflict_type == 'contents conflict':
1335
1009
                for trans_id in conflict[1]:
1413
1087
        """
1414
1088
        lines, conflicts = self._merged_lines(file_id)
1415
1089
        lines = list(lines)
1416
 
        # Note we're checking whether the OUTPUT is binary in this case,
 
1090
        # Note we're checking whether the OUTPUT is binary in this case, 
1417
1091
        # because we don't want to get into weave merge guts.
1418
1092
        check_text_lines(lines)
1419
1093
        self.tt.create_file(lines, trans_id)
1421
1095
            self._raw_conflicts.append(('text conflict', trans_id))
1422
1096
            name = self.tt.final_name(trans_id)
1423
1097
            parent_id = self.tt.final_parent(trans_id)
1424
 
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
1098
            file_group = self._dump_conflicts(name, parent_id, file_id, 
1425
1099
                                              no_base=True)
1426
1100
            file_group.append(trans_id)
1427
1101
 
1504
1178
                this_tree=None,
1505
1179
                pb=DummyProgress(),
1506
1180
                change_reporter=None):
1507
 
    """Primary interface for merging.
 
1181
    """Primary interface for merging. 
1508
1182
 
1509
 
        typical use is probably
 
1183
        typical use is probably 
1510
1184
        'merge_inner(branch, branch.get_revision_tree(other_revision),
1511
1185
                     branch.get_revision_tree(base_revision))'
1512
1186
        """
1531
1205
    get_revision_id = getattr(base_tree, 'get_revision_id', None)
1532
1206
    if get_revision_id is None:
1533
1207
        get_revision_id = base_tree.last_revision
1534
 
    merger.cache_trees_with_revision_ids([other_tree, base_tree, this_tree])
1535
1208
    merger.set_base_revision(get_revision_id(), this_branch)
1536
1209
    return merger.do_merge()
1537
1210
 
1605
1278
 
1606
1279
    def get_lines(self, revisions):
1607
1280
        """Get lines for revisions from the backing VersionedFiles.
1608
 
 
 
1281
        
1609
1282
        :raises RevisionNotPresent: on absent texts.
1610
1283
        """
1611
1284
        keys = [(self._key_prefix + (rev,)) for rev in revisions]
1613
1286
        for record in self.vf.get_record_stream(keys, 'unordered', True):
1614
1287
            if record.storage_kind == 'absent':
1615
1288
                raise errors.RevisionNotPresent(record.key, self.vf)
1616
 
            result[record.key[-1]] = osutils.chunks_to_lines(
1617
 
                record.get_bytes_as('chunked'))
 
1289
            result[record.key[-1]] = osutils.split_lines(
 
1290
                record.get_bytes_as('fulltext'))
1618
1291
        return result
1619
1292
 
1620
1293
    def plan_merge(self):
1810
1483
 
1811
1484
    def _find_unique_parents(self, tip_keys, base_key):
1812
1485
        """Find ancestors of tip that aren't ancestors of base.
1813
 
 
 
1486
        
1814
1487
        :param tip_keys: Nodes that are interesting
1815
1488
        :param base_key: Cull all ancestors of this node
1816
1489
        :return: The parent map for all revisions between tip_keys and
1876
1549
    @staticmethod
1877
1550
    def _prune_tails(parent_map, child_map, tails_to_remove):
1878
1551
        """Remove tails from the parent map.
1879
 
 
 
1552
        
1880
1553
        This will remove the supplied revisions until no more children have 0
1881
1554
        parents.
1882
1555