~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Robert Collins
  • Date: 2007-07-25 00:52:21 UTC
  • mfrom: (2650 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2651.
  • Revision ID: robertc@robertcollins.net-20070725005221-0ysm6il5mqnme3wz
Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
from bzrlib import (
23
23
    errors,
24
24
    osutils,
 
25
    patiencediff,
25
26
    registry,
26
27
    revision as _mod_revision,
27
28
    )
49
50
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
50
51
                              conflict_pass, FinalPaths, create_by_entry,
51
52
                              unique_add, ROOT_PARENT)
52
 
from bzrlib.versionedfile import WeaveMerge
 
53
from bzrlib.versionedfile import PlanWeaveMerge
53
54
from bzrlib import ui
54
55
 
55
56
# TODO: Report back as changes are merged in
99
100
            self._cached_trees[revision_id] = tree
100
101
        return self._cached_trees[revision_id]
101
102
 
102
 
    def _get_tree(self, treespec):
 
103
    def _get_tree(self, treespec, possible_transports=None):
103
104
        from bzrlib import workingtree
104
105
        location, revno = treespec
105
106
        if revno is None:
106
107
            tree = workingtree.WorkingTree.open_containing(location)[0]
107
108
            return tree.branch, tree
108
 
        branch = Branch.open_containing(location)[0]
 
109
        branch = Branch.open_containing(location, possible_transports)[0]
109
110
        if revno == -1:
110
111
            revision_id = branch.last_revision()
111
112
        else:
163
164
        self.interesting_files = file_list
164
165
 
165
166
    def set_pending(self):
166
 
        if not self.base_is_ancestor or not self.base_is_other_ancestor:
 
167
        if not self.base_is_ancestor or not self.base_is_other_ancestor or self.other_rev_id is None:
167
168
            return
168
169
        self._add_parent()
169
170
 
186
187
                if tree is not None:
187
188
                    tree.unlock()
188
189
 
189
 
    def set_other(self, other_revision):
 
190
    def set_other(self, other_revision, possible_transports=None):
190
191
        """Set the revision and tree to merge from.
191
192
 
192
193
        This sets the other_tree, other_rev_id, other_basis attributes.
193
194
 
194
195
        :param other_revision: The [path, revision] list to merge from.
195
196
        """
196
 
        self.other_branch, self.other_tree = self._get_tree(other_revision)
 
197
        self.other_branch, self.other_tree = self._get_tree(other_revision,
 
198
                                                            possible_transports)
197
199
        if other_revision[1] == -1:
198
200
            self.other_rev_id = _mod_revision.ensure_null(
199
201
                self.other_branch.last_revision())
224
226
        self.other_tree = self.revision_tree(revision_id)
225
227
        self.other_basis = revision_id
226
228
 
 
229
    def set_base_revision(self, revision_id, branch):
 
230
        """Set 'base' based on a branch and revision id
 
231
 
 
232
        :param revision_id: The revision to use for a tree
 
233
        :param branch: The branch containing this tree
 
234
        """
 
235
        self.base_rev_id = revision_id
 
236
        self.base_branch = branch
 
237
        self._maybe_fetch(branch, self.this_branch, revision_id)
 
238
        self.base_tree = self.revision_tree(revision_id)
 
239
        self.base_is_ancestor = is_ancestor(self.this_basis,
 
240
                                            self.base_rev_id,
 
241
                                            self.this_branch)
 
242
        self.base_is_other_ancestor = is_ancestor(self.other_basis,
 
243
                                                  self.base_rev_id,
 
244
                                                  self.this_branch)
 
245
 
227
246
    def _maybe_fetch(self, source, target, revision_id):
228
247
        if (source.repository.bzrdir.root_transport.base !=
229
248
            target.repository.bzrdir.root_transport.base):
901
920
                 interesting_ids=None, pb=DummyProgress(), pp=None,
902
921
                 reprocess=False, change_reporter=None,
903
922
                 interesting_files=None):
904
 
        self.this_revision_tree = self._get_revision_tree(this_tree)
905
 
        self.other_revision_tree = self._get_revision_tree(other_tree)
906
923
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
907
924
                                          base_tree, other_tree, 
908
925
                                          interesting_ids=interesting_ids, 
909
926
                                          pb=pb, pp=pp, reprocess=reprocess,
910
927
                                          change_reporter=change_reporter)
911
928
 
912
 
    def _get_revision_tree(self, tree):
913
 
        """Return a revision tree related to this tree.
914
 
        If the tree is a WorkingTree, the basis will be returned.
915
 
        """
916
 
        if getattr(tree, 'get_weave', False) is False:
917
 
            # If we have a WorkingTree, try using the basis
918
 
            return tree.branch.basis_tree()
919
 
        else:
920
 
            return tree
921
 
 
922
 
    def _check_file(self, file_id):
923
 
        """Check that the revision tree's version of the file matches."""
924
 
        for tree, rt in ((self.this_tree, self.this_revision_tree), 
925
 
                         (self.other_tree, self.other_revision_tree)):
926
 
            if rt is tree:
927
 
                continue
928
 
            if tree.get_file_sha1(file_id) != rt.get_file_sha1(file_id):
929
 
                raise WorkingTreeNotRevision(self.this_tree)
930
 
 
931
929
    def _merged_lines(self, file_id):
932
930
        """Generate the merged lines.
933
931
        There is no distinction between lines that are meant to contain <<<<<<<
934
932
        and conflicts.
935
933
        """
936
 
        weave = self.this_revision_tree.get_weave(file_id)
937
 
        this_revision_id = self.this_revision_tree.inventory[file_id].revision
938
 
        other_revision_id = \
939
 
            self.other_revision_tree.inventory[file_id].revision
940
 
        wm = WeaveMerge(weave, this_revision_id, other_revision_id, 
941
 
                        '<<<<<<< TREE\n', '>>>>>>> MERGE-SOURCE\n')
942
 
        return wm.merge_lines(self.reprocess)
 
934
        plan = self.this_tree.plan_file_merge(file_id, self.other_tree)
 
935
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
936
            '>>>>>>> MERGE-SOURCE\n')
 
937
        return textmerge.merge_lines(self.reprocess)
943
938
 
944
939
    def text_merge(self, file_id, trans_id):
945
940
        """Perform a (weave) text merge for a given file and file-id.
946
941
        If conflicts are encountered, .THIS and .OTHER files will be emitted,
947
942
        and a conflict will be noted.
948
943
        """
949
 
        self._check_file(file_id)
950
944
        lines, conflicts = self._merged_lines(file_id)
951
945
        lines = list(lines)
952
946
        # Note we're checking whether the OUTPUT is binary in this case, 
1048
1042
    """
1049
1043
    from bzrlib import option
1050
1044
    return option._merge_type_registry
 
1045
 
 
1046
 
 
1047
def _plan_annotate_merge(annotated_a, annotated_b, ancestors_a, ancestors_b):
 
1048
    def status_a(revision, text):
 
1049
        if revision in ancestors_b:
 
1050
            return 'killed-b', text
 
1051
        else:
 
1052
            return 'new-a', text
 
1053
 
 
1054
    def status_b(revision, text):
 
1055
        if revision in ancestors_a:
 
1056
            return 'killed-a', text
 
1057
        else:
 
1058
            return 'new-b', text
 
1059
 
 
1060
    plain_a = [t for (a, t) in annotated_a]
 
1061
    plain_b = [t for (a, t) in annotated_b]
 
1062
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_a, plain_b)
 
1063
    blocks = matcher.get_matching_blocks()
 
1064
    a_cur = 0
 
1065
    b_cur = 0
 
1066
    for ai, bi, l in blocks:
 
1067
        # process all mismatched sections
 
1068
        # (last mismatched section is handled because blocks always
 
1069
        # includes a 0-length last block)
 
1070
        for revision, text in annotated_a[a_cur:ai]:
 
1071
            yield status_a(revision, text)
 
1072
        for revision, text in annotated_b[b_cur:bi]:
 
1073
            yield status_b(revision, text)
 
1074
 
 
1075
        # and now the matched section
 
1076
        a_cur = ai + l
 
1077
        b_cur = bi + l
 
1078
        for text_a, text_b in zip(plain_a[ai:a_cur], plain_b[bi:b_cur]):
 
1079
            assert text_a == text_b
 
1080
            yield "unchanged", text_a