1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
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
19
from itertools import chain
30
revision as _mod_revision,
34
from bzrlib.branch import Branch
35
from bzrlib.conflicts import ConflictList, Conflict
36
from bzrlib.errors import (BzrCommandError,
46
WorkingTreeNotRevision,
49
from bzrlib.graph import Graph
50
from bzrlib.merge3 import Merge3
51
from bzrlib.osutils import rename, pathjoin
52
from progress import DummyProgress, ProgressPhase
53
from bzrlib.revision import (NULL_REVISION, ensure_null)
54
from bzrlib.textfile import check_text_lines
55
from bzrlib.trace import mutter, warning, note, is_quiet
56
from bzrlib.transform import (TransformPreview, TreeTransform,
57
resolve_conflicts, cook_conflicts,
58
conflict_pass, FinalPaths, create_from_tree,
59
unique_add, ROOT_PARENT)
60
from bzrlib.versionedfile import PlanWeaveMerge
63
# TODO: Report back as changes are merged in
66
def transform_tree(from_tree, to_tree, interesting_ids=None):
67
from_tree.lock_tree_write()
69
merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
70
interesting_ids=interesting_ids, this_tree=from_tree)
76
def __init__(self, this_branch, other_tree=None, base_tree=None,
77
this_tree=None, pb=None, change_reporter=None,
78
recurse='down', revision_graph=None):
80
self.this_branch = this_branch
81
self.this_basis = _mod_revision.ensure_null(
82
this_branch.last_revision())
83
self.this_rev_id = None
84
self.this_tree = this_tree
85
self.this_revision_tree = None
86
self.this_basis_tree = None
87
self.other_tree = other_tree
88
self.other_branch = None
89
self.base_tree = base_tree
90
self.ignore_zero = False
91
self.backup_files = False
92
self.interesting_ids = None
93
self.interesting_files = None
94
self.show_base = False
95
self.reprocess = False
100
self.recurse = recurse
101
self.change_reporter = change_reporter
102
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
110
def revision_graph(self):
111
if self._revision_graph is None:
112
self._revision_graph = self.this_branch.repository.get_graph()
113
return self._revision_graph
115
def _set_base_is_ancestor(self, value):
116
self._base_is_ancestor = value
118
def _get_base_is_ancestor(self):
119
if self._base_is_ancestor is None:
120
self._base_is_ancestor = self.revision_graph.is_ancestor(
121
self.base_rev_id, self.this_basis)
122
return self._base_is_ancestor
124
base_is_ancestor = property(_get_base_is_ancestor, _set_base_is_ancestor)
126
def _set_base_is_other_ancestor(self, value):
127
self._base_is_other_ancestor = value
129
def _get_base_is_other_ancestor(self):
130
if self._base_is_other_ancestor is None:
131
if self.other_basis is None:
133
self._base_is_other_ancestor = self.revision_graph.is_ancestor(
134
self.base_rev_id, self.other_basis)
135
return self._base_is_other_ancestor
137
base_is_other_ancestor = property(_get_base_is_other_ancestor,
138
_set_base_is_other_ancestor)
141
def from_uncommitted(tree, other_tree, pb=None, base_tree=None):
142
"""Return a Merger for uncommitted changes in other_tree.
144
:param tree: The tree to merge into
145
:param other_tree: The tree to get uncommitted changes from
146
:param pb: A progress indicator
147
:param base_tree: The basis to use for the merge. If unspecified,
148
other_tree.basis_tree() will be used.
150
if base_tree is None:
151
base_tree = other_tree.basis_tree()
152
merger = Merger(tree.branch, other_tree, base_tree, tree, pb)
153
merger.base_rev_id = merger.base_tree.get_revision_id()
154
merger.other_rev_id = None
155
merger.other_basis = merger.base_rev_id
159
def from_mergeable(klass, tree, mergeable, pb):
160
"""Return a Merger for a bundle or merge directive.
162
:param tree: The tree to merge changes into
163
:param mergeable: A merge directive or bundle
164
:param pb: A progress indicator
166
mergeable.install_revisions(tree.branch.repository)
167
base_revision_id, other_revision_id, verified =\
168
mergeable.get_merge_request(tree.branch.repository)
169
revision_graph = tree.branch.repository.get_graph()
170
if base_revision_id is not None:
171
if (base_revision_id != _mod_revision.NULL_REVISION and
172
revision_graph.is_ancestor(
173
base_revision_id, tree.branch.last_revision())):
174
base_revision_id = None
176
warning('Performing cherrypick')
177
merger = klass.from_revision_ids(pb, tree, other_revision_id,
178
base_revision_id, revision_graph=
180
return merger, verified
183
def from_revision_ids(pb, tree, other, base=None, other_branch=None,
184
base_branch=None, revision_graph=None,
186
"""Return a Merger for revision-ids.
188
:param pb: A progress indicator
189
:param tree: The tree to merge changes into
190
:param other: The revision-id to use as OTHER
191
:param base: The revision-id to use as BASE. If not specified, will
193
:param other_branch: A branch containing the other revision-id. If
194
not supplied, tree.branch is used.
195
:param base_branch: A branch containing the base revision-id. If
196
not supplied, other_branch or tree.branch will be used.
197
:param revision_graph: If you have a revision_graph precomputed, pass
198
it in, otherwise it will be created for you.
199
:param tree_branch: The branch associated with tree. If not supplied,
200
tree.branch will be used.
202
if tree_branch is None:
203
tree_branch = tree.branch
204
merger = Merger(tree_branch, this_tree=tree, pb=pb,
205
revision_graph=revision_graph)
206
if other_branch is None:
207
other_branch = tree.branch
208
merger.set_other_revision(other, other_branch)
212
if base_branch is None:
213
base_branch = other_branch
214
merger.set_base_revision(base, base_branch)
217
def revision_tree(self, revision_id, branch=None):
218
if revision_id not in self._cached_trees:
220
branch = self.this_branch
222
tree = self.this_tree.revision_tree(revision_id)
223
except errors.NoSuchRevisionInTree:
224
tree = branch.repository.revision_tree(revision_id)
225
self._cached_trees[revision_id] = tree
226
return self._cached_trees[revision_id]
228
def _get_tree(self, treespec, possible_transports=None):
229
from bzrlib import workingtree
230
location, revno = treespec
232
tree = workingtree.WorkingTree.open_containing(location)[0]
233
return tree.branch, tree
234
branch = Branch.open_containing(location, possible_transports)[0]
236
revision_id = branch.last_revision()
238
revision_id = branch.get_rev_id(revno)
239
revision_id = ensure_null(revision_id)
240
return branch, self.revision_tree(revision_id, branch)
242
def ensure_revision_trees(self):
243
if self.this_revision_tree is None:
244
self.this_basis_tree = self.revision_tree(self.this_basis)
245
if self.this_basis == self.this_rev_id:
246
self.this_revision_tree = self.this_basis_tree
248
if self.other_rev_id is None:
249
other_basis_tree = self.revision_tree(self.other_basis)
250
if other_basis_tree.has_changes(self.other_tree):
251
raise WorkingTreeNotRevision(self.this_tree)
252
other_rev_id = self.other_basis
253
self.other_tree = other_basis_tree
255
def file_revisions(self, file_id):
256
self.ensure_revision_trees()
257
def get_id(tree, file_id):
258
revision_id = tree.inventory[file_id].revision
260
if self.this_rev_id is None:
261
if self.this_basis_tree.get_file_sha1(file_id) != \
262
self.this_tree.get_file_sha1(file_id):
263
raise WorkingTreeNotRevision(self.this_tree)
265
trees = (self.this_basis_tree, self.other_tree)
266
return [get_id(tree, file_id) for tree in trees]
268
def check_basis(self, check_clean, require_commits=True):
269
if self.this_basis is None and require_commits is True:
270
raise BzrCommandError("This branch has no commits."
271
" (perhaps you would prefer 'bzr pull')")
274
if self.this_basis != self.this_rev_id:
275
raise errors.UncommittedChanges(self.this_tree)
277
def compare_basis(self):
279
basis_tree = self.revision_tree(self.this_tree.last_revision())
280
except errors.NoSuchRevision:
281
basis_tree = self.this_tree.basis_tree()
282
if not self.this_tree.has_changes(basis_tree):
283
self.this_rev_id = self.this_basis
285
def set_interesting_files(self, file_list):
286
self.interesting_files = file_list
288
def set_pending(self):
289
if not self.base_is_ancestor or not self.base_is_other_ancestor or self.other_rev_id is None:
293
def _add_parent(self):
294
new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
295
new_parent_trees = []
296
for revision_id in new_parents:
298
tree = self.revision_tree(revision_id)
299
except errors.NoSuchRevision:
303
new_parent_trees.append((revision_id, tree))
305
self.this_tree.set_parent_trees(new_parent_trees,
306
allow_leftmost_as_ghost=True)
308
for _revision_id, tree in new_parent_trees:
312
def set_other(self, other_revision, possible_transports=None):
313
"""Set the revision and tree to merge from.
315
This sets the other_tree, other_rev_id, other_basis attributes.
317
:param other_revision: The [path, revision] list to merge from.
319
self.other_branch, self.other_tree = self._get_tree(other_revision,
321
if other_revision[1] == -1:
322
self.other_rev_id = _mod_revision.ensure_null(
323
self.other_branch.last_revision())
324
if _mod_revision.is_null(self.other_rev_id):
325
raise NoCommits(self.other_branch)
326
self.other_basis = self.other_rev_id
327
elif other_revision[1] is not None:
328
self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
329
self.other_basis = self.other_rev_id
331
self.other_rev_id = None
332
self.other_basis = self.other_branch.last_revision()
333
if self.other_basis is None:
334
raise NoCommits(self.other_branch)
335
if self.other_rev_id is not None:
336
self._cached_trees[self.other_rev_id] = self.other_tree
337
self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
339
def set_other_revision(self, revision_id, other_branch):
340
"""Set 'other' based on a branch and revision id
342
:param revision_id: The revision to use for a tree
343
:param other_branch: The branch containing this tree
345
self.other_rev_id = revision_id
346
self.other_branch = other_branch
347
self._maybe_fetch(other_branch, self.this_branch, self.other_rev_id)
348
self.other_tree = self.revision_tree(revision_id)
349
self.other_basis = revision_id
351
def set_base_revision(self, revision_id, branch):
352
"""Set 'base' based on a branch and revision id
354
:param revision_id: The revision to use for a tree
355
:param branch: The branch containing this tree
357
self.base_rev_id = revision_id
358
self.base_branch = branch
359
self._maybe_fetch(branch, self.this_branch, revision_id)
360
self.base_tree = self.revision_tree(revision_id)
362
def _maybe_fetch(self, source, target, revision_id):
363
if not source.repository.has_same_location(target.repository):
364
target.fetch(source, revision_id)
367
revisions = [ensure_null(self.this_basis),
368
ensure_null(self.other_basis)]
369
if NULL_REVISION in revisions:
370
self.base_rev_id = NULL_REVISION
371
self.base_tree = self.revision_tree(self.base_rev_id)
372
self._is_criss_cross = False
374
lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
375
self._is_criss_cross = False
377
self.base_rev_id = NULL_REVISION
379
self.base_rev_id = list(lcas)[0]
380
else: # len(lcas) > 1
382
# find_unique_lca can only handle 2 nodes, so we have to
383
# start back at the beginning. It is a shame to traverse
384
# the graph again, but better than re-implementing
386
self.base_rev_id = self.revision_graph.find_unique_lca(
387
revisions[0], revisions[1])
389
self.base_rev_id = self.revision_graph.find_unique_lca(
391
self._is_criss_cross = True
392
if self.base_rev_id == NULL_REVISION:
393
raise UnrelatedBranches()
394
if self._is_criss_cross:
395
warning('Warning: criss-cross merge encountered. See bzr'
396
' help criss-cross.')
397
mutter('Criss-cross lcas: %r' % lcas)
398
interesting_revision_ids = [self.base_rev_id]
399
interesting_revision_ids.extend(lcas)
400
interesting_trees = dict((t.get_revision_id(), t)
401
for t in self.this_branch.repository.revision_trees(
402
interesting_revision_ids))
403
self._cached_trees.update(interesting_trees)
404
self.base_tree = interesting_trees.pop(self.base_rev_id)
405
sorted_lca_keys = self.revision_graph.find_merge_order(
407
self._lca_trees = [interesting_trees[key]
408
for key in sorted_lca_keys]
410
self.base_tree = self.revision_tree(self.base_rev_id)
411
self.base_is_ancestor = True
412
self.base_is_other_ancestor = True
413
mutter('Base revid: %r' % self.base_rev_id)
415
def set_base(self, base_revision):
416
"""Set the base revision to use for the merge.
418
:param base_revision: A 2-list containing a path and revision number.
420
mutter("doing merge() with no base_revision specified")
421
if base_revision == [None, None]:
424
base_branch, self.base_tree = self._get_tree(base_revision)
425
if base_revision[1] == -1:
426
self.base_rev_id = base_branch.last_revision()
427
elif base_revision[1] is None:
428
self.base_rev_id = _mod_revision.NULL_REVISION
430
self.base_rev_id = _mod_revision.ensure_null(
431
base_branch.get_rev_id(base_revision[1]))
432
self._maybe_fetch(base_branch, self.this_branch, self.base_rev_id)
434
def make_merger(self):
435
kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
436
'other_tree': self.other_tree,
437
'interesting_ids': self.interesting_ids,
438
'interesting_files': self.interesting_files,
441
if self.merge_type.requires_base:
442
kwargs['base_tree'] = self.base_tree
443
if self.merge_type.supports_reprocess:
444
kwargs['reprocess'] = self.reprocess
446
raise BzrError("Conflict reduction is not supported for merge"
447
" type %s." % self.merge_type)
448
if self.merge_type.supports_show_base:
449
kwargs['show_base'] = self.show_base
451
raise BzrError("Showing base is not supported for this"
452
" merge type. %s" % self.merge_type)
453
if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
454
and not self.base_is_other_ancestor):
455
raise errors.CannotReverseCherrypick()
456
if self.merge_type.supports_cherrypick:
457
kwargs['cherrypick'] = (not self.base_is_ancestor or
458
not self.base_is_other_ancestor)
459
if self._is_criss_cross and getattr(self.merge_type,
460
'supports_lca_trees', False):
461
kwargs['lca_trees'] = self._lca_trees
462
return self.merge_type(pb=self._pb,
463
change_reporter=self.change_reporter,
466
def _do_merge_to(self, merge):
467
if self.other_branch is not None:
468
self.other_branch.update_references(self.this_branch)
470
if self.recurse == 'down':
471
for relpath, file_id in self.this_tree.iter_references():
472
sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
473
other_revision = self.other_tree.get_reference_revision(
475
if other_revision == sub_tree.last_revision():
477
sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
478
sub_merge.merge_type = self.merge_type
479
other_branch = self.other_branch.reference_parent(file_id, relpath)
480
sub_merge.set_other_revision(other_revision, other_branch)
481
base_revision = self.base_tree.get_reference_revision(file_id)
482
sub_merge.base_tree = \
483
sub_tree.branch.repository.revision_tree(base_revision)
484
sub_merge.base_rev_id = base_revision
488
self.this_tree.lock_tree_write()
490
if self.base_tree is not None:
491
self.base_tree.lock_read()
493
if self.other_tree is not None:
494
self.other_tree.lock_read()
496
merge = self.make_merger()
497
self._do_merge_to(merge)
499
if self.other_tree is not None:
500
self.other_tree.unlock()
502
if self.base_tree is not None:
503
self.base_tree.unlock()
505
self.this_tree.unlock()
506
if len(merge.cooked_conflicts) == 0:
507
if not self.ignore_zero and not is_quiet():
508
note("All changes applied successfully.")
510
note("%d conflicts encountered." % len(merge.cooked_conflicts))
512
return len(merge.cooked_conflicts)
515
class _InventoryNoneEntry(object):
516
"""This represents an inventory entry which *isn't there*.
518
It simplifies the merging logic if we always have an InventoryEntry, even
519
if it isn't actually present
526
symlink_target = None
529
_none_entry = _InventoryNoneEntry()
532
class Merge3Merger(object):
533
"""Three-way merger that uses the merge3 text merger"""
535
supports_reprocess = True
536
supports_show_base = True
537
history_based = False
538
supports_cherrypick = True
539
supports_reverse_cherrypick = True
540
winner_idx = {"this": 2, "other": 1, "conflict": 1}
541
supports_lca_trees = True
543
def __init__(self, working_tree, this_tree, base_tree, other_tree,
544
interesting_ids=None, reprocess=False, show_base=False,
545
pb=DummyProgress(), pp=None, change_reporter=None,
546
interesting_files=None, do_merge=True,
547
cherrypick=False, lca_trees=None):
548
"""Initialize the merger object and perform the merge.
550
:param working_tree: The working tree to apply the merge to
551
:param this_tree: The local tree in the merge operation
552
:param base_tree: The common tree in the merge operation
553
:param other_tree: The other tree to merge changes from
554
:param interesting_ids: The file_ids of files that should be
555
participate in the merge. May not be combined with
557
:param: reprocess If True, perform conflict-reduction processing.
558
:param show_base: If True, show the base revision in text conflicts.
559
(incompatible with reprocess)
560
:param pb: A Progress bar
561
:param pp: A ProgressPhase object
562
:param change_reporter: An object that should report changes made
563
:param interesting_files: The tree-relative paths of files that should
564
participate in the merge. If these paths refer to directories,
565
the contents of those directories will also be included. May not
566
be combined with interesting_ids. If neither interesting_files nor
567
interesting_ids is specified, all files may participate in the
569
:param lca_trees: Can be set to a dictionary of {revision_id:rev_tree}
570
if the ancestry was found to include a criss-cross merge.
571
Otherwise should be None.
573
object.__init__(self)
574
if interesting_files is not None and interesting_ids is not None:
576
'specify either interesting_ids or interesting_files')
577
self.interesting_ids = interesting_ids
578
self.interesting_files = interesting_files
579
self.this_tree = working_tree
580
self.base_tree = base_tree
581
self.other_tree = other_tree
582
self._raw_conflicts = []
583
self.cooked_conflicts = []
584
self.reprocess = reprocess
585
self.show_base = show_base
586
self._lca_trees = lca_trees
587
# Uncommenting this will change the default algorithm to always use
588
# _entries_lca. This can be useful for running the test suite and
589
# making sure we haven't missed any corner cases.
590
# if lca_trees is None:
591
# self._lca_trees = [self.base_tree]
594
self.change_reporter = change_reporter
595
self.cherrypick = cherrypick
597
self.pp = ProgressPhase("Merge phase", 3, self.pb)
602
self.this_tree.lock_tree_write()
603
self.base_tree.lock_read()
604
self.other_tree.lock_read()
605
self.tt = TreeTransform(self.this_tree, self.pb)
608
self._compute_transform()
610
results = self.tt.apply(no_conflicts=True)
611
self.write_modified(results)
613
self.this_tree.add_conflicts(self.cooked_conflicts)
614
except UnsupportedOperation:
618
self.other_tree.unlock()
619
self.base_tree.unlock()
620
self.this_tree.unlock()
623
def make_preview_transform(self):
624
self.base_tree.lock_read()
625
self.other_tree.lock_read()
626
self.tt = TransformPreview(self.this_tree)
629
self._compute_transform()
632
self.other_tree.unlock()
633
self.base_tree.unlock()
637
def _compute_transform(self):
638
if self._lca_trees is None:
639
entries = self._entries3()
640
resolver = self._three_way
642
entries = self._entries_lca()
643
resolver = self._lca_multi_way
644
child_pb = ui.ui_factory.nested_progress_bar()
646
for num, (file_id, changed, parents3, names3,
647
executable3) in enumerate(entries):
648
child_pb.update('Preparing file merge', num, len(entries))
649
self._merge_names(file_id, parents3, names3, resolver=resolver)
651
file_status = self.merge_contents(file_id)
653
file_status = 'unmodified'
654
self._merge_executable(file_id,
655
executable3, file_status, resolver=resolver)
660
child_pb = ui.ui_factory.nested_progress_bar()
662
fs_conflicts = resolve_conflicts(self.tt, child_pb,
663
lambda t, c: conflict_pass(t, c, self.other_tree))
666
if self.change_reporter is not None:
667
from bzrlib import delta
668
delta.report_changes(
669
self.tt.iter_changes(), self.change_reporter)
670
self.cook_conflicts(fs_conflicts)
671
for conflict in self.cooked_conflicts:
675
"""Gather data about files modified between three trees.
677
Return a list of tuples of file_id, changed, parents3, names3,
678
executable3. changed is a boolean indicating whether the file contents
679
or kind were changed. parents3 is a tuple of parent ids for base,
680
other and this. names3 is a tuple of names for base, other and this.
681
executable3 is a tuple of execute-bit values for base, other and this.
684
iterator = self.other_tree.iter_changes(self.base_tree,
685
include_unchanged=True, specific_files=self.interesting_files,
686
extra_trees=[self.this_tree])
687
this_entries = dict((e.file_id, e) for p, e in
688
self.this_tree.iter_entries_by_dir(
689
self.interesting_ids))
690
for (file_id, paths, changed, versioned, parents, names, kind,
691
executable) in iterator:
692
if (self.interesting_ids is not None and
693
file_id not in self.interesting_ids):
695
entry = this_entries.get(file_id)
696
if entry is not None:
697
this_name = entry.name
698
this_parent = entry.parent_id
699
this_executable = entry.executable
703
this_executable = None
704
parents3 = parents + (this_parent,)
705
names3 = names + (this_name,)
706
executable3 = executable + (this_executable,)
707
result.append((file_id, changed, parents3, names3, executable3))
710
def _entries_lca(self):
711
"""Gather data about files modified between multiple trees.
713
This compares OTHER versus all LCA trees, and for interesting entries,
714
it then compares with THIS and BASE.
716
For the multi-valued entries, the format will be (BASE, [lca1, lca2])
717
:return: [(file_id, changed, parents, names, executable)]
718
file_id Simple file_id of the entry
719
changed Boolean, True if the kind or contents changed
721
parents ((base, [parent_id, in, lcas]), parent_id_other,
723
names ((base, [name, in, lcas]), name_in_other, name_in_this)
724
executable ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
726
if self.interesting_files is not None:
727
lookup_trees = [self.this_tree, self.base_tree]
728
lookup_trees.extend(self._lca_trees)
729
# I think we should include the lca trees as well
730
interesting_ids = self.other_tree.paths2ids(self.interesting_files,
733
interesting_ids = self.interesting_ids
735
walker = _mod_tree.MultiWalker(self.other_tree, self._lca_trees)
737
base_inventory = self.base_tree.inventory
738
this_inventory = self.this_tree.inventory
739
for path, file_id, other_ie, lca_values in walker.iter_all():
740
# Is this modified at all from any of the other trees?
742
other_ie = _none_entry
743
if interesting_ids is not None and file_id not in interesting_ids:
746
# If other_revision is found in any of the lcas, that means this
747
# node is uninteresting. This is because when merging, if there are
748
# multiple heads(), we have to create a new node. So if we didn't,
749
# we know that the ancestry is linear, and that OTHER did not
751
# See doc/developers/lca_merge_resolution.txt for details
752
other_revision = other_ie.revision
753
if other_revision is not None:
754
# We can't use this shortcut when other_revision is None,
755
# because it may be None because things are WorkingTrees, and
756
# not because it is *actually* None.
757
is_unmodified = False
758
for lca_path, ie in lca_values:
759
if ie is not None and ie.revision == other_revision:
766
for lca_path, lca_ie in lca_values:
768
lca_entries.append(_none_entry)
770
lca_entries.append(lca_ie)
772
if file_id in base_inventory:
773
base_ie = base_inventory[file_id]
775
base_ie = _none_entry
777
if file_id in this_inventory:
778
this_ie = this_inventory[file_id]
780
this_ie = _none_entry
786
for lca_ie in lca_entries:
787
lca_kinds.append(lca_ie.kind)
788
lca_parent_ids.append(lca_ie.parent_id)
789
lca_names.append(lca_ie.name)
790
lca_executable.append(lca_ie.executable)
792
kind_winner = self._lca_multi_way(
793
(base_ie.kind, lca_kinds),
794
other_ie.kind, this_ie.kind)
795
parent_id_winner = self._lca_multi_way(
796
(base_ie.parent_id, lca_parent_ids),
797
other_ie.parent_id, this_ie.parent_id)
798
name_winner = self._lca_multi_way(
799
(base_ie.name, lca_names),
800
other_ie.name, this_ie.name)
802
content_changed = True
803
if kind_winner == 'this':
804
# No kind change in OTHER, see if there are *any* changes
805
if other_ie.kind == 'directory':
806
if parent_id_winner == 'this' and name_winner == 'this':
807
# No change for this directory in OTHER, skip
809
content_changed = False
810
elif other_ie.kind is None or other_ie.kind == 'file':
811
def get_sha1(ie, tree):
812
if ie.kind != 'file':
814
return tree.get_file_sha1(file_id)
815
base_sha1 = get_sha1(base_ie, self.base_tree)
816
lca_sha1s = [get_sha1(ie, tree) for ie, tree
817
in zip(lca_entries, self._lca_trees)]
818
this_sha1 = get_sha1(this_ie, self.this_tree)
819
other_sha1 = get_sha1(other_ie, self.other_tree)
820
sha1_winner = self._lca_multi_way(
821
(base_sha1, lca_sha1s), other_sha1, this_sha1,
822
allow_overriding_lca=False)
823
exec_winner = self._lca_multi_way(
824
(base_ie.executable, lca_executable),
825
other_ie.executable, this_ie.executable)
826
if (parent_id_winner == 'this' and name_winner == 'this'
827
and sha1_winner == 'this' and exec_winner == 'this'):
828
# No kind, parent, name, exec, or content change for
829
# OTHER, so this node is not considered interesting
831
if sha1_winner == 'this':
832
content_changed = False
833
elif other_ie.kind == 'symlink':
834
def get_target(ie, tree):
835
if ie.kind != 'symlink':
837
return tree.get_symlink_target(file_id)
838
base_target = get_target(base_ie, self.base_tree)
839
lca_targets = [get_target(ie, tree) for ie, tree
840
in zip(lca_entries, self._lca_trees)]
841
this_target = get_target(this_ie, self.this_tree)
842
other_target = get_target(other_ie, self.other_tree)
843
target_winner = self._lca_multi_way(
844
(base_target, lca_targets),
845
other_target, this_target)
846
if (parent_id_winner == 'this' and name_winner == 'this'
847
and target_winner == 'this'):
848
# No kind, parent, name, or symlink target change
851
if target_winner == 'this':
852
content_changed = False
853
elif other_ie.kind == 'tree-reference':
854
# The 'changed' information seems to be handled at a higher
855
# level. At least, _entries3 returns False for content
856
# changed, even when at a new revision_id.
857
content_changed = False
858
if (parent_id_winner == 'this' and name_winner == 'this'):
859
# Nothing interesting
862
raise AssertionError('unhandled kind: %s' % other_ie.kind)
863
# XXX: We need to handle kind == 'symlink'
865
# If we have gotten this far, that means something has changed
866
result.append((file_id, content_changed,
867
((base_ie.parent_id, lca_parent_ids),
868
other_ie.parent_id, this_ie.parent_id),
869
((base_ie.name, lca_names),
870
other_ie.name, this_ie.name),
871
((base_ie.executable, lca_executable),
872
other_ie.executable, this_ie.executable)
879
self.tt.final_kind(self.tt.root)
881
self.tt.cancel_deletion(self.tt.root)
882
if self.tt.final_file_id(self.tt.root) is None:
883
self.tt.version_file(self.tt.tree_file_id(self.tt.root),
885
other_root_file_id = self.other_tree.get_root_id()
886
if other_root_file_id is None:
888
other_root = self.tt.trans_id_file_id(other_root_file_id)
889
if other_root == self.tt.root:
892
self.tt.final_kind(other_root)
895
if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
896
# the other tree's root is a non-root in the current tree
898
self.reparent_children(self.other_tree.inventory.root, self.tt.root)
899
self.tt.cancel_creation(other_root)
900
self.tt.cancel_versioning(other_root)
902
def reparent_children(self, ie, target):
903
for thing, child in ie.children.iteritems():
904
trans_id = self.tt.trans_id_file_id(child.file_id)
905
self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
907
def write_modified(self, results):
909
for path in results.modified_paths:
910
file_id = self.this_tree.path2id(self.this_tree.relpath(path))
913
hash = self.this_tree.get_file_sha1(file_id)
916
modified_hashes[file_id] = hash
917
self.this_tree.set_merge_modified(modified_hashes)
920
def parent(entry, file_id):
921
"""Determine the parent for a file_id (used as a key method)"""
924
return entry.parent_id
927
def name(entry, file_id):
928
"""Determine the name for a file_id (used as a key method)"""
934
def contents_sha1(tree, file_id):
935
"""Determine the sha1 of the file contents (used as a key method)."""
936
if file_id not in tree:
938
return tree.get_file_sha1(file_id)
941
def executable(tree, file_id):
942
"""Determine the executability of a file-id (used as a key method)."""
943
if file_id not in tree:
945
if tree.kind(file_id) != "file":
947
return tree.is_executable(file_id)
950
def kind(tree, file_id):
951
"""Determine the kind of a file-id (used as a key method)."""
952
if file_id not in tree:
954
return tree.kind(file_id)
957
def _three_way(base, other, this):
958
#if base == other, either they all agree, or only THIS has changed.
961
elif this not in (base, other):
963
# "Ambiguous clean merge" -- both sides have made the same change.
966
# this == base: only other has changed.
971
def _lca_multi_way(bases, other, this, allow_overriding_lca=True):
972
"""Consider LCAs when determining whether a change has occurred.
974
If LCAS are all identical, this is the same as a _three_way comparison.
976
:param bases: value in (BASE, [LCAS])
977
:param other: value in OTHER
978
:param this: value in THIS
979
:param allow_overriding_lca: If there is more than one unique lca
980
value, allow OTHER to override THIS if it has a new value, and
981
THIS only has an lca value, or vice versa. This is appropriate for
982
truly scalar values, not as much for non-scalars.
983
:return: 'this', 'other', or 'conflict' depending on whether an entry
986
# See doc/developers/lca_tree_merging.txt for details about this
989
# Either Ambiguously clean, or nothing was actually changed. We
992
base_val, lca_vals = bases
993
# Remove 'base_val' from the lca_vals, because it is not interesting
994
filtered_lca_vals = [lca_val for lca_val in lca_vals
995
if lca_val != base_val]
996
if len(filtered_lca_vals) == 0:
997
return Merge3Merger._three_way(base_val, other, this)
999
unique_lca_vals = set(filtered_lca_vals)
1000
if len(unique_lca_vals) == 1:
1001
return Merge3Merger._three_way(unique_lca_vals.pop(), other, this)
1003
if allow_overriding_lca:
1004
if other in unique_lca_vals:
1005
if this in unique_lca_vals:
1006
# Each side picked a different lca, conflict
1009
# This has a value which supersedes both lca values, and
1010
# other only has an lca value
1012
elif this in unique_lca_vals:
1013
# OTHER has a value which supersedes both lca values, and this
1014
# only has an lca value
1017
# At this point, the lcas disagree, and the tips disagree
1021
def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1022
"""Do a three-way test on a scalar.
1023
Return "this", "other" or "conflict", depending whether a value wins.
1025
key_base = key(base_tree, file_id)
1026
key_other = key(other_tree, file_id)
1027
#if base == other, either they all agree, or only THIS has changed.
1028
if key_base == key_other:
1030
key_this = key(this_tree, file_id)
1031
# "Ambiguous clean merge"
1032
if key_this == key_other:
1034
elif key_this == key_base:
1039
def merge_names(self, file_id):
1040
def get_entry(tree):
1041
if file_id in tree.inventory:
1042
return tree.inventory[file_id]
1045
this_entry = get_entry(self.this_tree)
1046
other_entry = get_entry(self.other_tree)
1047
base_entry = get_entry(self.base_tree)
1048
entries = (base_entry, other_entry, this_entry)
1051
for entry in entries:
1054
parents.append(None)
1056
names.append(entry.name)
1057
parents.append(entry.parent_id)
1058
return self._merge_names(file_id, parents, names,
1059
resolver=self._three_way)
1061
def _merge_names(self, file_id, parents, names, resolver):
1062
"""Perform a merge on file_id names and parents"""
1063
base_name, other_name, this_name = names
1064
base_parent, other_parent, this_parent = parents
1066
name_winner = resolver(*names)
1068
parent_id_winner = resolver(*parents)
1069
if this_name is None:
1070
if name_winner == "this":
1071
name_winner = "other"
1072
if parent_id_winner == "this":
1073
parent_id_winner = "other"
1074
if name_winner == "this" and parent_id_winner == "this":
1076
if name_winner == "conflict":
1077
trans_id = self.tt.trans_id_file_id(file_id)
1078
self._raw_conflicts.append(('name conflict', trans_id,
1079
this_name, other_name))
1080
if parent_id_winner == "conflict":
1081
trans_id = self.tt.trans_id_file_id(file_id)
1082
self._raw_conflicts.append(('parent conflict', trans_id,
1083
this_parent, other_parent))
1084
if other_name is None:
1085
# it doesn't matter whether the result was 'other' or
1086
# 'conflict'-- if there's no 'other', we leave it alone.
1088
# if we get here, name_winner and parent_winner are set to safe values.
1089
trans_id = self.tt.trans_id_file_id(file_id)
1090
parent_id = parents[self.winner_idx[parent_id_winner]]
1091
if parent_id is not None:
1092
parent_trans_id = self.tt.trans_id_file_id(parent_id)
1093
self.tt.adjust_path(names[self.winner_idx[name_winner]],
1094
parent_trans_id, trans_id)
1096
def merge_contents(self, file_id):
1097
"""Performs a merge on file_id contents."""
1098
def contents_pair(tree):
1099
if file_id not in tree:
1101
kind = tree.kind(file_id)
1103
contents = tree.get_file_sha1(file_id)
1104
elif kind == "symlink":
1105
contents = tree.get_symlink_target(file_id)
1108
return kind, contents
1110
def contents_conflict():
1111
trans_id = self.tt.trans_id_file_id(file_id)
1112
name = self.tt.final_name(trans_id)
1113
parent_id = self.tt.final_parent(trans_id)
1114
if file_id in self.this_tree.inventory:
1115
self.tt.unversion_file(trans_id)
1116
if file_id in self.this_tree:
1117
self.tt.delete_contents(trans_id)
1118
file_group = self._dump_conflicts(name, parent_id, file_id,
1120
self._raw_conflicts.append(('contents conflict', file_group))
1122
# See SPOT run. run, SPOT, run.
1123
# So we're not QUITE repeating ourselves; we do tricky things with
1125
base_pair = contents_pair(self.base_tree)
1126
other_pair = contents_pair(self.other_tree)
1128
this_pair = contents_pair(self.this_tree)
1129
lca_pairs = [contents_pair(tree) for tree in self._lca_trees]
1130
winner = self._lca_multi_way((base_pair, lca_pairs), other_pair,
1131
this_pair, allow_overriding_lca=False)
1133
if base_pair == other_pair:
1136
# We delayed evaluating this_pair as long as we can to avoid
1137
# unnecessary sha1 calculation
1138
this_pair = contents_pair(self.this_tree)
1139
winner = self._three_way(base_pair, other_pair, this_pair)
1140
if winner == 'this':
1141
# No interesting changes introduced by OTHER
1143
trans_id = self.tt.trans_id_file_id(file_id)
1144
if winner == 'other':
1145
# OTHER is a straight winner, so replace this contents with other
1146
file_in_this = file_id in self.this_tree
1148
# Remove any existing contents
1149
self.tt.delete_contents(trans_id)
1150
if file_id in self.other_tree:
1151
# OTHER changed the file
1152
create_from_tree(self.tt, trans_id,
1153
self.other_tree, file_id)
1154
if not file_in_this:
1155
self.tt.version_file(file_id, trans_id)
1158
# OTHER deleted the file
1159
self.tt.unversion_file(trans_id)
1162
# We have a hypothetical conflict, but if we have files, then we
1163
# can try to merge the content
1164
if this_pair[0] == 'file' and other_pair[0] == 'file':
1165
# THIS and OTHER are both files, so text merge. Either
1166
# BASE is a file, or both converted to files, so at least we
1167
# have agreement that output should be a file.
1169
self.text_merge(file_id, trans_id)
1171
return contents_conflict()
1172
if file_id not in self.this_tree:
1173
self.tt.version_file(file_id, trans_id)
1175
self.tt.tree_kind(trans_id)
1176
self.tt.delete_contents(trans_id)
1181
return contents_conflict()
1183
def get_lines(self, tree, file_id):
1184
"""Return the lines in a file, or an empty list."""
1186
return tree.get_file(file_id).readlines()
1190
def text_merge(self, file_id, trans_id):
1191
"""Perform a three-way text merge on a file_id"""
1192
# it's possible that we got here with base as a different type.
1193
# if so, we just want two-way text conflicts.
1194
if file_id in self.base_tree and \
1195
self.base_tree.kind(file_id) == "file":
1196
base_lines = self.get_lines(self.base_tree, file_id)
1199
other_lines = self.get_lines(self.other_tree, file_id)
1200
this_lines = self.get_lines(self.this_tree, file_id)
1201
m3 = Merge3(base_lines, this_lines, other_lines,
1202
is_cherrypick=self.cherrypick)
1203
start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
1204
if self.show_base is True:
1205
base_marker = '|' * 7
1209
def iter_merge3(retval):
1210
retval["text_conflicts"] = False
1211
for line in m3.merge_lines(name_a = "TREE",
1212
name_b = "MERGE-SOURCE",
1213
name_base = "BASE-REVISION",
1214
start_marker=start_marker,
1215
base_marker=base_marker,
1216
reprocess=self.reprocess):
1217
if line.startswith(start_marker):
1218
retval["text_conflicts"] = True
1219
yield line.replace(start_marker, '<' * 7)
1223
merge3_iterator = iter_merge3(retval)
1224
self.tt.create_file(merge3_iterator, trans_id)
1225
if retval["text_conflicts"] is True:
1226
self._raw_conflicts.append(('text conflict', trans_id))
1227
name = self.tt.final_name(trans_id)
1228
parent_id = self.tt.final_parent(trans_id)
1229
file_group = self._dump_conflicts(name, parent_id, file_id,
1230
this_lines, base_lines,
1232
file_group.append(trans_id)
1234
def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1235
base_lines=None, other_lines=None, set_version=False,
1237
"""Emit conflict files.
1238
If this_lines, base_lines, or other_lines are omitted, they will be
1239
determined automatically. If set_version is true, the .OTHER, .THIS
1240
or .BASE (in that order) will be created as versioned files.
1242
data = [('OTHER', self.other_tree, other_lines),
1243
('THIS', self.this_tree, this_lines)]
1245
data.append(('BASE', self.base_tree, base_lines))
1248
for suffix, tree, lines in data:
1250
trans_id = self._conflict_file(name, parent_id, tree, file_id,
1252
file_group.append(trans_id)
1253
if set_version and not versioned:
1254
self.tt.version_file(file_id, trans_id)
1258
def _conflict_file(self, name, parent_id, tree, file_id, suffix,
1260
"""Emit a single conflict file."""
1261
name = name + '.' + suffix
1262
trans_id = self.tt.create_path(name, parent_id)
1263
create_from_tree(self.tt, trans_id, tree, file_id, lines)
1266
def merge_executable(self, file_id, file_status):
1267
"""Perform a merge on the execute bit."""
1268
executable = [self.executable(t, file_id) for t in (self.base_tree,
1269
self.other_tree, self.this_tree)]
1270
self._merge_executable(file_id, executable, file_status,
1271
resolver=self._three_way)
1273
def _merge_executable(self, file_id, executable, file_status,
1275
"""Perform a merge on the execute bit."""
1276
base_executable, other_executable, this_executable = executable
1277
if file_status == "deleted":
1279
winner = resolver(*executable)
1280
if winner == "conflict":
1281
# There must be a None in here, if we have a conflict, but we
1282
# need executability since file status was not deleted.
1283
if self.executable(self.other_tree, file_id) is None:
1287
if winner == 'this' and file_status != "modified":
1289
trans_id = self.tt.trans_id_file_id(file_id)
1291
if self.tt.final_kind(trans_id) != "file":
1295
if winner == "this":
1296
executability = this_executable
1298
if file_id in self.other_tree:
1299
executability = other_executable
1300
elif file_id in self.this_tree:
1301
executability = this_executable
1302
elif file_id in self.base_tree:
1303
executability = base_executable
1304
if executability is not None:
1305
trans_id = self.tt.trans_id_file_id(file_id)
1306
self.tt.set_executability(executability, trans_id)
1308
def cook_conflicts(self, fs_conflicts):
1309
"""Convert all conflicts into a form that doesn't depend on trans_id"""
1310
from conflicts import Conflict
1312
self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
1313
fp = FinalPaths(self.tt)
1314
for conflict in self._raw_conflicts:
1315
conflict_type = conflict[0]
1316
if conflict_type in ('name conflict', 'parent conflict'):
1317
trans_id = conflict[1]
1318
conflict_args = conflict[2:]
1319
if trans_id not in name_conflicts:
1320
name_conflicts[trans_id] = {}
1321
unique_add(name_conflicts[trans_id], conflict_type,
1323
if conflict_type == 'contents conflict':
1324
for trans_id in conflict[1]:
1325
file_id = self.tt.final_file_id(trans_id)
1326
if file_id is not None:
1328
path = fp.get_path(trans_id)
1329
for suffix in ('.BASE', '.THIS', '.OTHER'):
1330
if path.endswith(suffix):
1331
path = path[:-len(suffix)]
1333
c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1334
self.cooked_conflicts.append(c)
1335
if conflict_type == 'text conflict':
1336
trans_id = conflict[1]
1337
path = fp.get_path(trans_id)
1338
file_id = self.tt.final_file_id(trans_id)
1339
c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1340
self.cooked_conflicts.append(c)
1342
for trans_id, conflicts in name_conflicts.iteritems():
1344
this_parent, other_parent = conflicts['parent conflict']
1345
if this_parent == other_parent:
1346
raise AssertionError()
1348
this_parent = other_parent = \
1349
self.tt.final_file_id(self.tt.final_parent(trans_id))
1351
this_name, other_name = conflicts['name conflict']
1352
if this_name == other_name:
1353
raise AssertionError()
1355
this_name = other_name = self.tt.final_name(trans_id)
1356
other_path = fp.get_path(trans_id)
1357
if this_parent is not None and this_name is not None:
1358
this_parent_path = \
1359
fp.get_path(self.tt.trans_id_file_id(this_parent))
1360
this_path = pathjoin(this_parent_path, this_name)
1362
this_path = "<deleted>"
1363
file_id = self.tt.final_file_id(trans_id)
1364
c = Conflict.factory('path conflict', path=this_path,
1365
conflict_path=other_path, file_id=file_id)
1366
self.cooked_conflicts.append(c)
1367
self.cooked_conflicts.sort(key=Conflict.sort_key)
1370
class WeaveMerger(Merge3Merger):
1371
"""Three-way tree merger, text weave merger."""
1372
supports_reprocess = True
1373
supports_show_base = False
1374
supports_reverse_cherrypick = False
1375
history_based = True
1377
def _merged_lines(self, file_id):
1378
"""Generate the merged lines.
1379
There is no distinction between lines that are meant to contain <<<<<<<
1383
base = self.base_tree
1386
plan = self.this_tree.plan_file_merge(file_id, self.other_tree,
1388
if 'merge' in debug.debug_flags:
1390
trans_id = self.tt.trans_id_file_id(file_id)
1391
name = self.tt.final_name(trans_id) + '.plan'
1392
contents = ('%10s|%s' % l for l in plan)
1393
self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1394
textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1395
'>>>>>>> MERGE-SOURCE\n')
1396
return textmerge.merge_lines(self.reprocess)
1398
def text_merge(self, file_id, trans_id):
1399
"""Perform a (weave) text merge for a given file and file-id.
1400
If conflicts are encountered, .THIS and .OTHER files will be emitted,
1401
and a conflict will be noted.
1403
lines, conflicts = self._merged_lines(file_id)
1405
# Note we're checking whether the OUTPUT is binary in this case,
1406
# because we don't want to get into weave merge guts.
1407
check_text_lines(lines)
1408
self.tt.create_file(lines, trans_id)
1410
self._raw_conflicts.append(('text conflict', trans_id))
1411
name = self.tt.final_name(trans_id)
1412
parent_id = self.tt.final_parent(trans_id)
1413
file_group = self._dump_conflicts(name, parent_id, file_id,
1415
file_group.append(trans_id)
1418
class LCAMerger(WeaveMerger):
1420
def _merged_lines(self, file_id):
1421
"""Generate the merged lines.
1422
There is no distinction between lines that are meant to contain <<<<<<<
1426
base = self.base_tree
1429
plan = self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
1431
if 'merge' in debug.debug_flags:
1433
trans_id = self.tt.trans_id_file_id(file_id)
1434
name = self.tt.final_name(trans_id) + '.plan'
1435
contents = ('%10s|%s' % l for l in plan)
1436
self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1437
textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1438
'>>>>>>> MERGE-SOURCE\n')
1439
return textmerge.merge_lines(self.reprocess)
1442
class Diff3Merger(Merge3Merger):
1443
"""Three-way merger using external diff3 for text merging"""
1445
def dump_file(self, temp_dir, name, tree, file_id):
1446
out_path = pathjoin(temp_dir, name)
1447
out_file = open(out_path, "wb")
1449
in_file = tree.get_file(file_id)
1450
for line in in_file:
1451
out_file.write(line)
1456
def text_merge(self, file_id, trans_id):
1457
"""Perform a diff3 merge using a specified file-id and trans-id.
1458
If conflicts are encountered, .BASE, .THIS. and .OTHER conflict files
1459
will be dumped, and a will be conflict noted.
1462
temp_dir = osutils.mkdtemp(prefix="bzr-")
1464
new_file = pathjoin(temp_dir, "new")
1465
this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
1466
base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
1467
other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
1468
status = bzrlib.patch.diff3(new_file, this, base, other)
1469
if status not in (0, 1):
1470
raise BzrError("Unhandled diff3 exit code")
1471
f = open(new_file, 'rb')
1473
self.tt.create_file(f, trans_id)
1477
name = self.tt.final_name(trans_id)
1478
parent_id = self.tt.final_parent(trans_id)
1479
self._dump_conflicts(name, parent_id, file_id)
1480
self._raw_conflicts.append(('text conflict', trans_id))
1482
osutils.rmtree(temp_dir)
1485
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1487
merge_type=Merge3Merger,
1488
interesting_ids=None,
1492
interesting_files=None,
1495
change_reporter=None):
1496
"""Primary interface for merging.
1498
typical use is probably
1499
'merge_inner(branch, branch.get_revision_tree(other_revision),
1500
branch.get_revision_tree(base_revision))'
1502
if this_tree is None:
1503
raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1504
"parameter as of bzrlib version 0.8.")
1505
merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1506
pb=pb, change_reporter=change_reporter)
1507
merger.backup_files = backup_files
1508
merger.merge_type = merge_type
1509
merger.interesting_ids = interesting_ids
1510
merger.ignore_zero = ignore_zero
1511
if interesting_files:
1513
raise ValueError('Only supply interesting_ids'
1514
' or interesting_files')
1515
merger.interesting_files = interesting_files
1516
merger.show_base = show_base
1517
merger.reprocess = reprocess
1518
merger.other_rev_id = other_rev_id
1519
merger.other_basis = other_rev_id
1520
get_revision_id = getattr(base_tree, 'get_revision_id', None)
1521
if get_revision_id is None:
1522
get_revision_id = base_tree.last_revision
1523
merger.set_base_revision(get_revision_id(), this_branch)
1524
return merger.do_merge()
1526
def get_merge_type_registry():
1527
"""Merge type registry is in bzrlib.option to avoid circular imports.
1529
This method provides a sanctioned way to retrieve it.
1531
from bzrlib import option
1532
return option._merge_type_registry
1535
def _plan_annotate_merge(annotated_a, annotated_b, ancestors_a, ancestors_b):
1536
def status_a(revision, text):
1537
if revision in ancestors_b:
1538
return 'killed-b', text
1540
return 'new-a', text
1542
def status_b(revision, text):
1543
if revision in ancestors_a:
1544
return 'killed-a', text
1546
return 'new-b', text
1548
plain_a = [t for (a, t) in annotated_a]
1549
plain_b = [t for (a, t) in annotated_b]
1550
matcher = patiencediff.PatienceSequenceMatcher(None, plain_a, plain_b)
1551
blocks = matcher.get_matching_blocks()
1554
for ai, bi, l in blocks:
1555
# process all mismatched sections
1556
# (last mismatched section is handled because blocks always
1557
# includes a 0-length last block)
1558
for revision, text in annotated_a[a_cur:ai]:
1559
yield status_a(revision, text)
1560
for revision, text in annotated_b[b_cur:bi]:
1561
yield status_b(revision, text)
1562
# and now the matched section
1565
for text_a in plain_a[ai:a_cur]:
1566
yield "unchanged", text_a
1569
class _PlanMergeBase(object):
1571
def __init__(self, a_rev, b_rev, vf, key_prefix):
1574
:param a_rev: Revision-id of one revision to merge
1575
:param b_rev: Revision-id of the other revision to merge
1576
:param vf: A VersionedFiles containing both revisions
1577
:param key_prefix: A prefix for accessing keys in vf, typically
1583
self._last_lines = None
1584
self._last_lines_revision_id = None
1585
self._cached_matching_blocks = {}
1586
self._key_prefix = key_prefix
1587
self._precache_tip_lines()
1589
def _precache_tip_lines(self):
1590
lines = self.get_lines([self.a_rev, self.b_rev])
1591
self.lines_a = lines[self.a_rev]
1592
self.lines_b = lines[self.b_rev]
1594
def get_lines(self, revisions):
1595
"""Get lines for revisions from the backing VersionedFiles.
1597
:raises RevisionNotPresent: on absent texts.
1599
keys = [(self._key_prefix + (rev,)) for rev in revisions]
1601
for record in self.vf.get_record_stream(keys, 'unordered', True):
1602
if record.storage_kind == 'absent':
1603
raise errors.RevisionNotPresent(record.key, self.vf)
1604
result[record.key[-1]] = osutils.chunks_to_lines(
1605
record.get_bytes_as('chunked'))
1608
def plan_merge(self):
1609
"""Generate a 'plan' for merging the two revisions.
1611
This involves comparing their texts and determining the cause of
1612
differences. If text A has a line and text B does not, then either the
1613
line was added to text A, or it was deleted from B. Once the causes
1614
are combined, they are written out in the format described in
1615
VersionedFile.plan_merge
1617
blocks = self._get_matching_blocks(self.a_rev, self.b_rev)
1618
unique_a, unique_b = self._unique_lines(blocks)
1619
new_a, killed_b = self._determine_status(self.a_rev, unique_a)
1620
new_b, killed_a = self._determine_status(self.b_rev, unique_b)
1621
return self._iter_plan(blocks, new_a, killed_b, new_b, killed_a)
1623
def _iter_plan(self, blocks, new_a, killed_b, new_b, killed_a):
1626
for i, j, n in blocks:
1627
for a_index in range(last_i, i):
1628
if a_index in new_a:
1629
if a_index in killed_b:
1630
yield 'conflicted-a', self.lines_a[a_index]
1632
yield 'new-a', self.lines_a[a_index]
1634
yield 'killed-b', self.lines_a[a_index]
1635
for b_index in range(last_j, j):
1636
if b_index in new_b:
1637
if b_index in killed_a:
1638
yield 'conflicted-b', self.lines_b[b_index]
1640
yield 'new-b', self.lines_b[b_index]
1642
yield 'killed-a', self.lines_b[b_index]
1643
# handle common lines
1644
for a_index in range(i, i+n):
1645
yield 'unchanged', self.lines_a[a_index]
1649
def _get_matching_blocks(self, left_revision, right_revision):
1650
"""Return a description of which sections of two revisions match.
1652
See SequenceMatcher.get_matching_blocks
1654
cached = self._cached_matching_blocks.get((left_revision,
1656
if cached is not None:
1658
if self._last_lines_revision_id == left_revision:
1659
left_lines = self._last_lines
1660
right_lines = self.get_lines([right_revision])[right_revision]
1662
lines = self.get_lines([left_revision, right_revision])
1663
left_lines = lines[left_revision]
1664
right_lines = lines[right_revision]
1665
self._last_lines = right_lines
1666
self._last_lines_revision_id = right_revision
1667
matcher = patiencediff.PatienceSequenceMatcher(None, left_lines,
1669
return matcher.get_matching_blocks()
1671
def _unique_lines(self, matching_blocks):
1672
"""Analyse matching_blocks to determine which lines are unique
1674
:return: a tuple of (unique_left, unique_right), where the values are
1675
sets of line numbers of unique lines.
1681
for i, j, n in matching_blocks:
1682
unique_left.extend(range(last_i, i))
1683
unique_right.extend(range(last_j, j))
1686
return unique_left, unique_right
1689
def _subtract_plans(old_plan, new_plan):
1690
"""Remove changes from new_plan that came from old_plan.
1692
It is assumed that the difference between the old_plan and new_plan
1693
is their choice of 'b' text.
1695
All lines from new_plan that differ from old_plan are emitted
1696
verbatim. All lines from new_plan that match old_plan but are
1697
not about the 'b' revision are emitted verbatim.
1699
Lines that match and are about the 'b' revision are the lines we
1700
don't want, so we convert 'killed-b' -> 'unchanged', and 'new-b'
1701
is skipped entirely.
1703
matcher = patiencediff.PatienceSequenceMatcher(None, old_plan,
1706
for i, j, n in matcher.get_matching_blocks():
1707
for jj in range(last_j, j):
1709
for jj in range(j, j+n):
1710
plan_line = new_plan[jj]
1711
if plan_line[0] == 'new-b':
1713
elif plan_line[0] == 'killed-b':
1714
yield 'unchanged', plan_line[1]
1720
class _PlanMerge(_PlanMergeBase):
1721
"""Plan an annotate merge using on-the-fly annotation"""
1723
def __init__(self, a_rev, b_rev, vf, key_prefix):
1724
super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1725
self.a_key = self._key_prefix + (self.a_rev,)
1726
self.b_key = self._key_prefix + (self.b_rev,)
1727
self.graph = Graph(self.vf)
1728
heads = self.graph.heads((self.a_key, self.b_key))
1730
# one side dominates, so we can just return its values, yay for
1732
# Ideally we would know that before we get this far
1733
self._head_key = heads.pop()
1734
if self._head_key == self.a_key:
1738
mutter('found dominating revision for %s\n%s > %s', self.vf,
1739
self._head_key[-1], other)
1742
self._head_key = None
1745
def _precache_tip_lines(self):
1746
# Turn this into a no-op, because we will do this later
1749
def _find_recursive_lcas(self):
1750
"""Find all the ancestors back to a unique lca"""
1751
cur_ancestors = (self.a_key, self.b_key)
1752
# graph.find_lca(uncommon, keys) now returns plain NULL_REVISION,
1753
# rather than a key tuple. We will just map that directly to no common
1757
next_lcas = self.graph.find_lca(*cur_ancestors)
1758
# Map a plain NULL_REVISION to a simple no-ancestors
1759
if next_lcas == set([NULL_REVISION]):
1761
# Order the lca's based on when they were merged into the tip
1762
# While the actual merge portion of weave merge uses a set() of
1763
# active revisions, the order of insertion *does* effect the
1764
# implicit ordering of the texts.
1765
for rev_key in cur_ancestors:
1766
ordered_parents = tuple(self.graph.find_merge_order(rev_key,
1768
parent_map[rev_key] = ordered_parents
1769
if len(next_lcas) == 0:
1771
elif len(next_lcas) == 1:
1772
parent_map[list(next_lcas)[0]] = ()
1774
elif len(next_lcas) > 2:
1775
# More than 2 lca's, fall back to grabbing all nodes between
1776
# this and the unique lca.
1777
mutter('More than 2 LCAs, falling back to all nodes for:'
1778
' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
1779
cur_lcas = next_lcas
1780
while len(cur_lcas) > 1:
1781
cur_lcas = self.graph.find_lca(*cur_lcas)
1782
if len(cur_lcas) == 0:
1783
# No common base to find, use the full ancestry
1786
unique_lca = list(cur_lcas)[0]
1787
if unique_lca == NULL_REVISION:
1788
# find_lca will return a plain 'NULL_REVISION' rather
1789
# than a key tuple when there is no common ancestor, we
1790
# prefer to just use None, because it doesn't confuse
1791
# _get_interesting_texts()
1793
parent_map.update(self._find_unique_parents(next_lcas,
1796
cur_ancestors = next_lcas
1799
def _find_unique_parents(self, tip_keys, base_key):
1800
"""Find ancestors of tip that aren't ancestors of base.
1802
:param tip_keys: Nodes that are interesting
1803
:param base_key: Cull all ancestors of this node
1804
:return: The parent map for all revisions between tip_keys and
1805
base_key. base_key will be included. References to nodes outside of
1806
the ancestor set will also be removed.
1808
# TODO: this would be simpler if find_unique_ancestors took a list
1809
# instead of a single tip, internally it supports it, but it
1810
# isn't a "backwards compatible" api change.
1811
if base_key is None:
1812
parent_map = dict(self.graph.iter_ancestry(tip_keys))
1813
# We remove NULL_REVISION because it isn't a proper tuple key, and
1814
# thus confuses things like _get_interesting_texts, and our logic
1815
# to add the texts into the memory weave.
1816
if NULL_REVISION in parent_map:
1817
parent_map.pop(NULL_REVISION)
1820
for tip in tip_keys:
1822
self.graph.find_unique_ancestors(tip, [base_key]))
1823
parent_map = self.graph.get_parent_map(interesting)
1824
parent_map[base_key] = ()
1825
culled_parent_map, child_map, tails = self._remove_external_references(
1827
# Remove all the tails but base_key
1828
if base_key is not None:
1829
tails.remove(base_key)
1830
self._prune_tails(culled_parent_map, child_map, tails)
1831
# Now remove all the uninteresting 'linear' regions
1832
simple_map = _mod_graph.collapse_linear_regions(culled_parent_map)
1836
def _remove_external_references(parent_map):
1837
"""Remove references that go outside of the parent map.
1839
:param parent_map: Something returned from Graph.get_parent_map(keys)
1840
:return: (filtered_parent_map, child_map, tails)
1841
filtered_parent_map is parent_map without external references
1842
child_map is the {parent_key: [child_keys]} mapping
1843
tails is a list of nodes that do not have any parents in the map
1845
# TODO: The basic effect of this function seems more generic than
1846
# _PlanMerge. But the specific details of building a child_map,
1847
# and computing tails seems very specific to _PlanMerge.
1848
# Still, should this be in Graph land?
1849
filtered_parent_map = {}
1852
for key, parent_keys in parent_map.iteritems():
1853
culled_parent_keys = [p for p in parent_keys if p in parent_map]
1854
if not culled_parent_keys:
1856
for parent_key in culled_parent_keys:
1857
child_map.setdefault(parent_key, []).append(key)
1858
# TODO: Do we want to do this, it adds overhead for every node,
1859
# just to say that the node has no children
1860
child_map.setdefault(key, [])
1861
filtered_parent_map[key] = culled_parent_keys
1862
return filtered_parent_map, child_map, tails
1865
def _prune_tails(parent_map, child_map, tails_to_remove):
1866
"""Remove tails from the parent map.
1868
This will remove the supplied revisions until no more children have 0
1871
:param parent_map: A dict of {child: [parents]}, this dictionary will
1872
be modified in place.
1873
:param tails_to_remove: A list of tips that should be removed,
1874
this list will be consumed
1875
:param child_map: The reverse dict of parent_map ({parent: [children]})
1876
this dict will be modified
1877
:return: None, parent_map will be modified in place.
1879
while tails_to_remove:
1880
next = tails_to_remove.pop()
1881
parent_map.pop(next)
1882
children = child_map.pop(next)
1883
for child in children:
1884
child_parents = parent_map[child]
1885
child_parents.remove(next)
1886
if len(child_parents) == 0:
1887
tails_to_remove.append(child)
1889
def _get_interesting_texts(self, parent_map):
1890
"""Return a dict of texts we are interested in.
1892
Note that the input is in key tuples, but the output is in plain
1895
:param parent_map: The output from _find_recursive_lcas
1896
:return: A dict of {'revision_id':lines} as returned by
1897
_PlanMergeBase.get_lines()
1899
all_revision_keys = set(parent_map)
1900
all_revision_keys.add(self.a_key)
1901
all_revision_keys.add(self.b_key)
1903
# Everything else is in 'keys' but get_lines is in 'revision_ids'
1904
all_texts = self.get_lines([k[-1] for k in all_revision_keys])
1907
def _build_weave(self):
1908
from bzrlib import weave
1909
self._weave = weave.Weave(weave_name='in_memory_weave',
1910
allow_reserved=True)
1911
parent_map = self._find_recursive_lcas()
1913
all_texts = self._get_interesting_texts(parent_map)
1915
# Note: Unfortunately, the order given by topo_sort will effect the
1916
# ordering resolution in the output. Specifically, if you add A then B,
1917
# then in the output text A lines will show up before B lines. And, of
1918
# course, topo_sort doesn't guarantee any real ordering.
1919
# So we use merge_sort, and add a fake node on the tip.
1920
# This ensures that left-hand parents will always be inserted into the
1921
# weave before right-hand parents.
1922
tip_key = self._key_prefix + (_mod_revision.CURRENT_REVISION,)
1923
parent_map[tip_key] = (self.a_key, self.b_key)
1925
for seq_num, key, depth, eom in reversed(tsort.merge_sort(parent_map,
1929
# for key in tsort.topo_sort(parent_map):
1930
parent_keys = parent_map[key]
1931
revision_id = key[-1]
1932
parent_ids = [k[-1] for k in parent_keys]
1933
self._weave.add_lines(revision_id, parent_ids,
1934
all_texts[revision_id])
1936
def plan_merge(self):
1937
"""Generate a 'plan' for merging the two revisions.
1939
This involves comparing their texts and determining the cause of
1940
differences. If text A has a line and text B does not, then either the
1941
line was added to text A, or it was deleted from B. Once the causes
1942
are combined, they are written out in the format described in
1943
VersionedFile.plan_merge
1945
if self._head_key is not None: # There was a single head
1946
if self._head_key == self.a_key:
1949
if self._head_key != self.b_key:
1950
raise AssertionError('There was an invalid head: %s != %s'
1951
% (self.b_key, self._head_key))
1953
head_rev = self._head_key[-1]
1954
lines = self.get_lines([head_rev])[head_rev]
1955
return ((plan, line) for line in lines)
1956
return self._weave.plan_merge(self.a_rev, self.b_rev)
1959
class _PlanLCAMerge(_PlanMergeBase):
1961
This merge algorithm differs from _PlanMerge in that:
1962
1. comparisons are done against LCAs only
1963
2. cases where a contested line is new versus one LCA but old versus
1964
another are marked as conflicts, by emitting the line as conflicted-a
1967
This is faster, and hopefully produces more useful output.
1970
def __init__(self, a_rev, b_rev, vf, key_prefix, graph):
1971
_PlanMergeBase.__init__(self, a_rev, b_rev, vf, key_prefix)
1972
lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
1975
if lca == NULL_REVISION:
1978
self.lcas.add(lca[-1])
1979
for lca in self.lcas:
1980
if _mod_revision.is_null(lca):
1983
lca_lines = self.get_lines([lca])[lca]
1984
matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_a,
1986
blocks = list(matcher.get_matching_blocks())
1987
self._cached_matching_blocks[(a_rev, lca)] = blocks
1988
matcher = patiencediff.PatienceSequenceMatcher(None, self.lines_b,
1990
blocks = list(matcher.get_matching_blocks())
1991
self._cached_matching_blocks[(b_rev, lca)] = blocks
1993
def _determine_status(self, revision_id, unique_line_numbers):
1994
"""Determines the status unique lines versus all lcas.
1996
Basically, determines why the line is unique to this revision.
1998
A line may be determined new, killed, or both.
2000
If a line is determined new, that means it was not present in at least
2001
one LCA, and is not present in the other merge revision.
2003
If a line is determined killed, that means the line was present in
2006
If a line is killed and new, this indicates that the two merge
2007
revisions contain differing conflict resolutions.
2008
:param revision_id: The id of the revision in which the lines are
2010
:param unique_line_numbers: The line numbers of unique lines.
2011
:return a tuple of (new_this, killed_other):
2015
unique_line_numbers = set(unique_line_numbers)
2016
for lca in self.lcas:
2017
blocks = self._get_matching_blocks(revision_id, lca)
2018
unique_vs_lca, _ignored = self._unique_lines(blocks)
2019
new.update(unique_line_numbers.intersection(unique_vs_lca))
2020
killed.update(unique_line_numbers.difference(unique_vs_lca))