99
100
self._cached_trees[revision_id] = tree
100
101
return self._cached_trees[revision_id]
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]
110
111
revision_id = branch.last_revision()
186
187
if tree is not None:
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.
192
193
This sets the other_tree, other_rev_id, other_basis attributes.
194
195
:param other_revision: The [path, revision] list to merge from.
196
self.other_branch, self.other_tree = self._get_tree(other_revision)
197
self.other_branch, self.other_tree = self._get_tree(other_revision,
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
229
def set_base_revision(self, revision_id, branch):
230
"""Set 'base' based on a branch and revision id
232
:param revision_id: The revision to use for a tree
233
:param branch: The branch containing this tree
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,
242
self.base_is_other_ancestor = is_ancestor(self.other_basis,
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)
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.
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()
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)):
928
if tree.get_file_sha1(file_id) != rt.get_file_sha1(file_id):
929
raise WorkingTreeNotRevision(self.this_tree)
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 <<<<<<<
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)
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.
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,
1049
1043
from bzrlib import option
1050
1044
return option._merge_type_registry
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
1052
return 'new-a', text
1054
def status_b(revision, text):
1055
if revision in ancestors_a:
1056
return 'killed-a', text
1058
return 'new-b', text
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()
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)
1075
# and now the matched section
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