93
84
self.interesting_files = None
94
85
self.show_base = False
95
86
self.reprocess = False
100
89
self.recurse = recurse
101
90
self.change_reporter = change_reporter
102
91
self._cached_trees = {}
103
self._revision_graph = revision_graph
104
self._base_is_ancestor = None
105
self._base_is_other_ancestor = None
106
self._is_criss_cross = None
107
self._lca_trees = None
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:
115
rev_id = maybe_tree.get_revision_id()
116
except AttributeError:
118
self._cached_trees[rev_id] = maybe_tree
121
def revision_graph(self):
122
if self._revision_graph is None:
123
self._revision_graph = self.this_branch.repository.get_graph()
124
return self._revision_graph
126
def _set_base_is_ancestor(self, value):
127
self._base_is_ancestor = value
129
def _get_base_is_ancestor(self):
130
if self._base_is_ancestor is None:
131
self._base_is_ancestor = self.revision_graph.is_ancestor(
132
self.base_rev_id, self.this_basis)
133
return self._base_is_ancestor
135
base_is_ancestor = property(_get_base_is_ancestor, _set_base_is_ancestor)
137
def _set_base_is_other_ancestor(self, value):
138
self._base_is_other_ancestor = value
140
def _get_base_is_other_ancestor(self):
141
if self._base_is_other_ancestor is None:
142
if self.other_basis is None:
144
self._base_is_other_ancestor = self.revision_graph.is_ancestor(
145
self.base_rev_id, self.other_basis)
146
return self._base_is_other_ancestor
148
base_is_other_ancestor = property(_get_base_is_other_ancestor,
149
_set_base_is_other_ancestor)
152
def from_uncommitted(tree, other_tree, pb=None, base_tree=None):
94
def from_uncommitted(tree, other_tree, pb):
153
95
"""Return a Merger for uncommitted changes in other_tree.
155
97
:param tree: The tree to merge into
156
98
:param other_tree: The tree to get uncommitted changes from
157
99
: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.
161
if base_tree is None:
162
base_tree = other_tree.basis_tree()
163
merger = Merger(tree.branch, other_tree, base_tree, tree, pb)
101
merger = Merger(tree.branch, other_tree, other_tree.basis_tree(), tree,
164
103
merger.base_rev_id = merger.base_tree.get_revision_id()
165
104
merger.other_rev_id = None
166
merger.other_basis = merger.base_rev_id
177
115
mergeable.install_revisions(tree.branch.repository)
178
116
base_revision_id, other_revision_id, verified =\
179
117
mergeable.get_merge_request(tree.branch.repository)
180
revision_graph = tree.branch.repository.get_graph()
181
if base_revision_id is not None:
182
if (base_revision_id != _mod_revision.NULL_REVISION and
183
revision_graph.is_ancestor(
184
base_revision_id, tree.branch.last_revision())):
185
base_revision_id = None
187
warning('Performing cherrypick')
118
if (base_revision_id != _mod_revision.NULL_REVISION and
119
tree.branch.repository.get_graph().is_ancestor(
120
base_revision_id, tree.branch.last_revision())):
121
base_revision_id = None
188
122
merger = klass.from_revision_ids(pb, tree, other_revision_id,
189
base_revision_id, revision_graph=
191
124
return merger, verified
194
def from_revision_ids(pb, tree, other, base=None, other_branch=None,
195
base_branch=None, revision_graph=None,
127
def from_revision_ids(pb, this, other, base=None, other_branch=None,
197
129
"""Return a Merger for revision-ids.
199
:param pb: A progress indicator
200
131
:param tree: The tree to merge changes into
201
132
:param other: The revision-id to use as OTHER
202
133
:param base: The revision-id to use as BASE. If not specified, will
203
134
be auto-selected.
204
135
:param other_branch: A branch containing the other revision-id. If
205
not supplied, tree.branch is used.
136
not supplied, this.branch is used.
206
137
:param base_branch: A branch containing the base revision-id. If
207
not supplied, other_branch or tree.branch will be used.
208
:param revision_graph: If you have a revision_graph precomputed, pass
209
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.
138
not supplied, other_branch or this.branch will be used.
139
:param pb: A progress indicator
213
if tree_branch is None:
214
tree_branch = tree.branch
215
merger = Merger(tree_branch, this_tree=tree, pb=pb,
216
revision_graph=revision_graph)
141
merger = Merger(this.branch, this_tree=this, pb=pb)
217
142
if other_branch is None:
218
other_branch = tree.branch
143
other_branch = this.branch
219
144
merger.set_other_revision(other, other_branch)
221
146
merger.find_base()
369
297
self.base_branch = branch
370
298
self._maybe_fetch(branch, self.this_branch, revision_id)
371
299
self.base_tree = self.revision_tree(revision_id)
300
graph = self.this_branch.repository.get_graph()
301
self.base_is_ancestor = graph.is_ancestor(self.base_rev_id,
303
self.base_is_other_ancestor = graph.is_ancestor(self.base_rev_id,
373
306
def _maybe_fetch(self, source, target, revision_id):
374
307
if not source.repository.has_same_location(target.repository):
375
308
target.fetch(source, revision_id)
377
310
def find_base(self):
311
this_repo = self.this_branch.repository
312
graph = this_repo.get_graph()
378
313
revisions = [ensure_null(self.this_basis),
379
314
ensure_null(self.other_basis)]
380
315
if NULL_REVISION in revisions:
381
316
self.base_rev_id = NULL_REVISION
382
self.base_tree = self.revision_tree(self.base_rev_id)
383
self._is_criss_cross = False
385
lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
386
self._is_criss_cross = False
388
self.base_rev_id = NULL_REVISION
390
self.base_rev_id = list(lcas)[0]
391
else: # len(lcas) > 1
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
397
self.base_rev_id = self.revision_graph.find_unique_lca(
398
revisions[0], revisions[1])
400
self.base_rev_id = self.revision_graph.find_unique_lca(
402
self._is_criss_cross = True
318
self.base_rev_id, steps = graph.find_unique_lca(revisions[0],
319
revisions[1], count_steps=True)
403
320
if self.base_rev_id == NULL_REVISION:
404
321
raise UnrelatedBranches()
405
if self._is_criss_cross:
406
323
warning('Warning: criss-cross merge encountered. See bzr'
407
324
' 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(
418
self._lca_trees = [interesting_trees[key]
419
for key in sorted_lca_keys]
421
self.base_tree = self.revision_tree(self.base_rev_id)
325
self.base_tree = self.revision_tree(self.base_rev_id)
422
326
self.base_is_ancestor = True
423
327
self.base_is_other_ancestor = True
424
mutter('Base revid: %r' % self.base_rev_id)
426
329
def set_base(self, base_revision):
427
330
"""Set the base revision to use for the merge.
460
367
kwargs['show_base'] = self.show_base
461
368
elif self.show_base:
462
369
raise BzrError("Showing base is not supported for this"
463
" merge type. %s" % self.merge_type)
464
if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
465
and not self.base_is_other_ancestor):
466
raise errors.CannotReverseCherrypick()
467
if self.merge_type.supports_cherrypick:
468
kwargs['cherrypick'] = (not self.base_is_ancestor or
469
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
return self.merge_type(pb=self._pb,
474
change_reporter=self.change_reporter,
477
def _do_merge_to(self, merge):
478
if self.other_branch is not None:
479
self.other_branch.update_references(self.this_branch)
481
if self.recurse == 'down':
482
for relpath, file_id in self.this_tree.iter_references():
483
sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
484
other_revision = self.other_tree.get_reference_revision(
486
if other_revision == sub_tree.last_revision():
488
sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
489
sub_merge.merge_type = self.merge_type
490
other_branch = self.other_branch.reference_parent(file_id, relpath)
491
sub_merge.set_other_revision(other_revision, other_branch)
492
base_revision = self.base_tree.get_reference_revision(file_id)
493
sub_merge.base_tree = \
494
sub_tree.branch.repository.revision_tree(base_revision)
495
sub_merge.base_rev_id = base_revision
370
" merge type. %s" % self.merge_type)
499
371
self.this_tree.lock_tree_write()
372
if self.base_tree is not None:
373
self.base_tree.lock_read()
374
if self.other_tree is not None:
375
self.other_tree.lock_read()
377
merge = self.merge_type(pb=self._pb,
378
change_reporter=self.change_reporter,
380
if self.recurse == 'down':
381
for path, file_id in self.this_tree.iter_references():
382
sub_tree = self.this_tree.get_nested_tree(file_id, path)
383
other_revision = self.other_tree.get_reference_revision(
385
if other_revision == sub_tree.last_revision():
387
sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
388
sub_merge.merge_type = self.merge_type
389
relpath = self.this_tree.relpath(path)
390
other_branch = self.other_branch.reference_parent(file_id, relpath)
391
sub_merge.set_other_revision(other_revision, other_branch)
392
base_revision = self.base_tree.get_reference_revision(file_id)
393
sub_merge.base_tree = \
394
sub_tree.branch.repository.revision_tree(base_revision)
398
if self.other_tree is not None:
399
self.other_tree.unlock()
501
400
if self.base_tree is not None:
502
self.base_tree.lock_read()
504
if self.other_tree is not None:
505
self.other_tree.lock_read()
507
merge = self.make_merger()
508
self._do_merge_to(merge)
510
if self.other_tree is not None:
511
self.other_tree.unlock()
513
if self.base_tree is not None:
514
self.base_tree.unlock()
401
self.base_tree.unlock()
516
402
self.this_tree.unlock()
517
403
if len(merge.cooked_conflicts) == 0:
518
if not self.ignore_zero and not is_quiet():
404
if not self.ignore_zero:
519
405
note("All changes applied successfully.")
521
407
note("%d conflicts encountered." % len(merge.cooked_conflicts))
577
442
be combined with interesting_ids. If neither interesting_files nor
578
443
interesting_ids is specified, all files may participate in the
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.
584
446
object.__init__(self)
585
if interesting_files is not None and interesting_ids is not None:
587
'specify either interesting_ids or interesting_files')
447
if interesting_files is not None:
448
assert interesting_ids is None
588
449
self.interesting_ids = interesting_ids
589
450
self.interesting_files = interesting_files
590
451
self.this_tree = working_tree
452
self.this_tree.lock_tree_write()
591
453
self.base_tree = base_tree
454
self.base_tree.lock_read()
592
455
self.other_tree = other_tree
456
self.other_tree.lock_read()
593
457
self._raw_conflicts = []
594
458
self.cooked_conflicts = []
595
459
self.reprocess = reprocess
596
460
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]
605
463
self.change_reporter = change_reporter
606
self.cherrypick = cherrypick
607
464
if self.pp is None:
608
465
self.pp = ProgressPhase("Merge phase", 3, self.pb)
613
self.this_tree.lock_tree_write()
614
self.base_tree.lock_read()
615
self.other_tree.lock_read()
616
self.tt = TreeTransform(self.this_tree, self.pb)
467
self.tt = TreeTransform(working_tree, self.pb)
618
469
self.pp.next_phase()
619
self._compute_transform()
470
entries = self._entries3()
471
child_pb = ui.ui_factory.nested_progress_bar()
473
for num, (file_id, changed, parents3, names3,
474
executable3) in enumerate(entries):
475
child_pb.update('Preparing file merge', num, len(entries))
476
self._merge_names(file_id, parents3, names3)
478
file_status = self.merge_contents(file_id)
480
file_status = 'unmodified'
481
self._merge_executable(file_id,
482
executable3, file_status)
487
child_pb = ui.ui_factory.nested_progress_bar()
489
fs_conflicts = resolve_conflicts(self.tt, child_pb,
490
lambda t, c: conflict_pass(t, c, self.other_tree))
493
if change_reporter is not None:
494
from bzrlib import delta
495
delta.report_changes(self.tt._iter_changes(), change_reporter)
496
self.cook_conflicts(fs_conflicts)
497
for conflict in self.cooked_conflicts:
620
499
self.pp.next_phase()
621
500
results = self.tt.apply(no_conflicts=True)
622
501
self.write_modified(results)
624
self.this_tree.add_conflicts(self.cooked_conflicts)
503
working_tree.add_conflicts(self.cooked_conflicts)
625
504
except UnsupportedOperation:
718
543
result.append((file_id, changed, parents3, names3, executable3))
721
def _entries_lca(self):
722
"""Gather data about files modified between multiple trees.
724
This compares OTHER versus all LCA trees, and for interesting entries,
725
it then compares with THIS and BASE.
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
732
parents ((base, [parent_id, in, lcas]), parent_id_other,
734
names ((base, [name, in, lcas]), name_in_other, name_in_this)
735
executable ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
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,
744
interesting_ids = self.interesting_ids
746
walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
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?
753
other_ie = _none_entry
754
if interesting_ids is not None and file_id not in interesting_ids:
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
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:
777
for lca_path, lca_ie in lca_values:
779
lca_entries.append(_none_entry)
781
lca_entries.append(lca_ie)
783
if file_id in base_inventory:
784
base_ie = base_inventory[file_id]
786
base_ie = _none_entry
788
if file_id in this_inventory:
789
this_ie = this_inventory[file_id]
791
this_ie = _none_entry
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)
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)
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
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':
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
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':
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
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
873
raise AssertionError('unhandled kind: %s' % other_ie.kind)
874
# XXX: We need to handle kind == 'symlink'
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)
888
546
def fix_root(self):
890
548
self.tt.final_kind(self.tt.root)
891
549
except NoSuchFile:
892
550
self.tt.cancel_deletion(self.tt.root)
893
551
if self.tt.final_file_id(self.tt.root) is None:
894
self.tt.version_file(self.tt.tree_file_id(self.tt.root),
552
self.tt.version_file(self.tt.tree_file_id(self.tt.root),
554
if self.other_tree.inventory.root is None:
896
556
other_root_file_id = self.other_tree.get_root_id()
897
if other_root_file_id is None:
899
557
other_root = self.tt.trans_id_file_id(other_root_file_id)
900
558
if other_root == self.tt.root:
1136
741
base_pair = contents_pair(self.base_tree)
1137
742
other_pair = contents_pair(self.other_tree)
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)
1144
if base_pair == other_pair:
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
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
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)
1169
# OTHER deleted the file
1170
self.tt.unversion_file(trans_id)
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':
743
if base_pair == other_pair:
744
# OTHER introduced no changes
746
this_pair = contents_pair(self.this_tree)
747
if this_pair == other_pair:
748
# THIS and OTHER introduced the same changes
751
trans_id = self.tt.trans_id_file_id(file_id)
752
if this_pair == base_pair:
753
# only OTHER introduced changes
754
if file_id in self.this_tree:
755
# Remove any existing contents
756
self.tt.delete_contents(trans_id)
757
if file_id in self.other_tree:
758
# OTHER changed the file
759
create_by_entry(self.tt,
760
self.other_tree.inventory[file_id],
761
self.other_tree, trans_id)
762
if file_id not in self.this_tree.inventory:
763
self.tt.version_file(file_id, trans_id)
765
elif file_id in self.this_tree.inventory:
766
# OTHER deleted the file
767
self.tt.unversion_file(trans_id)
769
#BOTH THIS and OTHER introduced changes; scalar conflict
770
elif this_pair[0] == "file" and other_pair[0] == "file":
1176
771
# THIS and OTHER are both files, so text merge. Either
1177
772
# BASE is a file, or both converted to files, so at least we
1178
773
# have agreement that output should be a file.
1698
1239
return unique_left, unique_right
1701
def _subtract_plans(old_plan, new_plan):
1702
"""Remove changes from new_plan that came from old_plan.
1704
It is assumed that the difference between the old_plan and new_plan
1705
is their choice of 'b' text.
1707
All lines from new_plan that differ from old_plan are emitted
1708
verbatim. All lines from new_plan that match old_plan but are
1709
not about the 'b' revision are emitted verbatim.
1711
Lines that match and are about the 'b' revision are the lines we
1712
don't want, so we convert 'killed-b' -> 'unchanged', and 'new-b'
1713
is skipped entirely.
1715
matcher = patiencediff.PatienceSequenceMatcher(None, old_plan,
1718
for i, j, n in matcher.get_matching_blocks():
1719
for jj in range(last_j, j):
1721
for jj in range(j, j+n):
1722
plan_line = new_plan[jj]
1723
if plan_line[0] == 'new-b':
1725
elif plan_line[0] == 'killed-b':
1726
yield 'unchanged', plan_line[1]
1732
class _PlanMerge(_PlanMergeBase):
1733
"""Plan an annotate merge using on-the-fly annotation"""
1735
def __init__(self, a_rev, b_rev, vf, key_prefix):
1736
super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1737
self.a_key = self._key_prefix + (self.a_rev,)
1738
self.b_key = self._key_prefix + (self.b_rev,)
1739
self.graph = Graph(self.vf)
1740
heads = self.graph.heads((self.a_key, self.b_key))
1742
# one side dominates, so we can just return its values, yay for
1744
# Ideally we would know that before we get this far
1745
self._head_key = heads.pop()
1746
if self._head_key == self.a_key:
1750
mutter('found dominating revision for %s\n%s > %s', self.vf,
1751
self._head_key[-1], other)
1754
self._head_key = None
1757
def _precache_tip_lines(self):
1758
# Turn this into a no-op, because we will do this later
1761
def _find_recursive_lcas(self):
1762
"""Find all the ancestors back to a unique lca"""
1763
cur_ancestors = (self.a_key, self.b_key)
1764
# graph.find_lca(uncommon, keys) now returns plain NULL_REVISION,
1765
# rather than a key tuple. We will just map that directly to no common
1769
next_lcas = self.graph.find_lca(*cur_ancestors)
1770
# Map a plain NULL_REVISION to a simple no-ancestors
1771
if next_lcas == set([NULL_REVISION]):
1773
# Order the lca's based on when they were merged into the tip
1774
# While the actual merge portion of weave merge uses a set() of
1775
# active revisions, the order of insertion *does* effect the
1776
# implicit ordering of the texts.
1777
for rev_key in cur_ancestors:
1778
ordered_parents = tuple(self.graph.find_merge_order(rev_key,
1780
parent_map[rev_key] = ordered_parents
1781
if len(next_lcas) == 0:
1783
elif len(next_lcas) == 1:
1784
parent_map[list(next_lcas)[0]] = ()
1786
elif len(next_lcas) > 2:
1787
# More than 2 lca's, fall back to grabbing all nodes between
1788
# this and the unique lca.
1789
mutter('More than 2 LCAs, falling back to all nodes for:'
1790
' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
1791
cur_lcas = next_lcas
1792
while len(cur_lcas) > 1:
1793
cur_lcas = self.graph.find_lca(*cur_lcas)
1794
if len(cur_lcas) == 0:
1795
# No common base to find, use the full ancestry
1798
unique_lca = list(cur_lcas)[0]
1799
if unique_lca == NULL_REVISION:
1800
# find_lca will return a plain 'NULL_REVISION' rather
1801
# than a key tuple when there is no common ancestor, we
1802
# prefer to just use None, because it doesn't confuse
1803
# _get_interesting_texts()
1805
parent_map.update(self._find_unique_parents(next_lcas,
1808
cur_ancestors = next_lcas
1811
def _find_unique_parents(self, tip_keys, base_key):
1812
"""Find ancestors of tip that aren't ancestors of base.
1814
:param tip_keys: Nodes that are interesting
1815
:param base_key: Cull all ancestors of this node
1816
:return: The parent map for all revisions between tip_keys and
1817
base_key. base_key will be included. References to nodes outside of
1818
the ancestor set will also be removed.
1820
# TODO: this would be simpler if find_unique_ancestors took a list
1821
# instead of a single tip, internally it supports it, but it
1822
# isn't a "backwards compatible" api change.
1823
if base_key is None:
1824
parent_map = dict(self.graph.iter_ancestry(tip_keys))
1825
# We remove NULL_REVISION because it isn't a proper tuple key, and
1826
# thus confuses things like _get_interesting_texts, and our logic
1827
# to add the texts into the memory weave.
1828
if NULL_REVISION in parent_map:
1829
parent_map.pop(NULL_REVISION)
1832
for tip in tip_keys:
1834
self.graph.find_unique_ancestors(tip, [base_key]))
1835
parent_map = self.graph.get_parent_map(interesting)
1836
parent_map[base_key] = ()
1837
culled_parent_map, child_map, tails = self._remove_external_references(
1839
# Remove all the tails but base_key
1840
if base_key is not None:
1841
tails.remove(base_key)
1842
self._prune_tails(culled_parent_map, child_map, tails)
1843
# Now remove all the uninteresting 'linear' regions
1844
simple_map = _mod_graph.collapse_linear_regions(culled_parent_map)
1848
def _remove_external_references(parent_map):
1849
"""Remove references that go outside of the parent map.
1851
:param parent_map: Something returned from Graph.get_parent_map(keys)
1852
:return: (filtered_parent_map, child_map, tails)
1853
filtered_parent_map is parent_map without external references
1854
child_map is the {parent_key: [child_keys]} mapping
1855
tails is a list of nodes that do not have any parents in the map
1857
# TODO: The basic effect of this function seems more generic than
1858
# _PlanMerge. But the specific details of building a child_map,
1859
# and computing tails seems very specific to _PlanMerge.
1860
# Still, should this be in Graph land?
1861
filtered_parent_map = {}
1864
for key, parent_keys in parent_map.iteritems():
1865
culled_parent_keys = [p for p in parent_keys if p in parent_map]
1866
if not culled_parent_keys:
1868
for parent_key in culled_parent_keys:
1869
child_map.setdefault(parent_key, []).append(key)
1870
# TODO: Do we want to do this, it adds overhead for every node,
1871
# just to say that the node has no children
1872
child_map.setdefault(key, [])
1873
filtered_parent_map[key] = culled_parent_keys
1874
return filtered_parent_map, child_map, tails
1877
def _prune_tails(parent_map, child_map, tails_to_remove):
1878
"""Remove tails from the parent map.
1880
This will remove the supplied revisions until no more children have 0
1883
:param parent_map: A dict of {child: [parents]}, this dictionary will
1884
be modified in place.
1885
:param tails_to_remove: A list of tips that should be removed,
1886
this list will be consumed
1887
:param child_map: The reverse dict of parent_map ({parent: [children]})
1888
this dict will be modified
1889
:return: None, parent_map will be modified in place.
1891
while tails_to_remove:
1892
next = tails_to_remove.pop()
1893
parent_map.pop(next)
1894
children = child_map.pop(next)
1895
for child in children:
1896
child_parents = parent_map[child]
1897
child_parents.remove(next)
1898
if len(child_parents) == 0:
1899
tails_to_remove.append(child)
1901
def _get_interesting_texts(self, parent_map):
1902
"""Return a dict of texts we are interested in.
1904
Note that the input is in key tuples, but the output is in plain
1907
:param parent_map: The output from _find_recursive_lcas
1908
:return: A dict of {'revision_id':lines} as returned by
1909
_PlanMergeBase.get_lines()
1911
all_revision_keys = set(parent_map)
1912
all_revision_keys.add(self.a_key)
1913
all_revision_keys.add(self.b_key)
1915
# Everything else is in 'keys' but get_lines is in 'revision_ids'
1916
all_texts = self.get_lines([k[-1] for k in all_revision_keys])
1919
def _build_weave(self):
1920
from bzrlib import weave
1921
self._weave = weave.Weave(weave_name='in_memory_weave',
1922
allow_reserved=True)
1923
parent_map = self._find_recursive_lcas()
1925
all_texts = self._get_interesting_texts(parent_map)
1927
# Note: Unfortunately, the order given by topo_sort will effect the
1928
# ordering resolution in the output. Specifically, if you add A then B,
1929
# then in the output text A lines will show up before B lines. And, of
1930
# course, topo_sort doesn't guarantee any real ordering.
1931
# So we use merge_sort, and add a fake node on the tip.
1932
# This ensures that left-hand parents will always be inserted into the
1933
# weave before right-hand parents.
1934
tip_key = self._key_prefix + (_mod_revision.CURRENT_REVISION,)
1935
parent_map[tip_key] = (self.a_key, self.b_key)
1937
for seq_num, key, depth, eom in reversed(tsort.merge_sort(parent_map,
1941
# for key in tsort.topo_sort(parent_map):
1942
parent_keys = parent_map[key]
1943
revision_id = key[-1]
1944
parent_ids = [k[-1] for k in parent_keys]
1945
self._weave.add_lines(revision_id, parent_ids,
1946
all_texts[revision_id])
1948
def plan_merge(self):
1949
"""Generate a 'plan' for merging the two revisions.
1951
This involves comparing their texts and determining the cause of
1952
differences. If text A has a line and text B does not, then either the
1953
line was added to text A, or it was deleted from B. Once the causes
1954
are combined, they are written out in the format described in
1955
VersionedFile.plan_merge
1957
if self._head_key is not None: # There was a single head
1958
if self._head_key == self.a_key:
1961
if self._head_key != self.b_key:
1962
raise AssertionError('There was an invalid head: %s != %s'
1963
% (self.b_key, self._head_key))
1965
head_rev = self._head_key[-1]
1966
lines = self.get_lines([head_rev])[head_rev]
1967
return ((plan, line) for line in lines)
1968
return self._weave.plan_merge(self.a_rev, self.b_rev)
1971
class _PlanLCAMerge(_PlanMergeBase):
1973
This merge algorithm differs from _PlanMerge in that:
1974
1. comparisons are done against LCAs only
1975
2. cases where a contested line is new versus one LCA but old versus
1976
another are marked as conflicts, by emitting the line as conflicted-a
1979
This is faster, and hopefully produces more useful output.
1982
def __init__(self, a_rev, b_rev, vf, key_prefix, graph):
1983
_PlanMergeBase.__init__(self, a_rev, b_rev, vf, key_prefix)
1984
lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
1987
if lca == NULL_REVISION:
1990
self.lcas.add(lca[-1])
1991
for lca in self.lcas:
1992
if _mod_revision.is_null(lca):
1995
lca_lines = self.get_lines([lca])[lca]
1996
matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_a,
1998
blocks = list(matcher.get_matching_blocks())
1999
self._cached_matching_blocks[(a_rev, lca)] = blocks
2000
matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_b,
2002
blocks = list(matcher.get_matching_blocks())
2003
self._cached_matching_blocks[(b_rev, lca)] = blocks
2005
def _determine_status(self, revision_id, unique_line_numbers):
2006
"""Determines the status unique lines versus all lcas.
2008
Basically, determines why the line is unique to this revision.
2010
A line may be determined new, killed, or both.
2012
If a line is determined new, that means it was not present in at least
2013
one LCA, and is not present in the other merge revision.
2015
If a line is determined killed, that means the line was present in
2018
If a line is killed and new, this indicates that the two merge
2019
revisions contain differing conflict resolutions.
2020
:param revision_id: The id of the revision in which the lines are
2022
:param unique_line_numbers: The line numbers of unique lines.
2023
:return a tuple of (new_this, killed_other):
2027
unique_line_numbers = set(unique_line_numbers)
2028
for lca in self.lcas:
2029
blocks = self._get_matching_blocks(revision_id, lca)
2030
unique_vs_lca, _ignored = self._unique_lines(blocks)
2031
new.update(unique_line_numbers.intersection(unique_vs_lca))
2032
killed.update(unique_line_numbers.difference(unique_vs_lca))
1241
def _find_new(self, version_id):
1242
"""Determine which lines are new in the ancestry of this version.
1244
If a lines is present in this version, and not present in any
1245
common ancestor, it is considered new.
1247
if version_id not in self.uncommon:
1249
parents = self.vf.get_parents(version_id)
1250
if len(parents) == 0:
1251
return set(range(len(self.vf.get_lines(version_id))))
1253
for parent in parents:
1254
blocks = self._get_matching_blocks(version_id, parent)
1255
result, unused = self._unique_lines(blocks)
1256
parent_new = self._find_new(parent)
1257
for i, j, n in blocks:
1258
for ii, jj in [(i+r, j+r) for r in range(n)]:
1259
if jj in parent_new:
1264
new.intersection_update(result)