47
47
from progress import DummyProgress, ProgressPhase
48
48
from bzrlib.revision import (NULL_REVISION, ensure_null)
49
49
from bzrlib.textfile import check_text_lines
50
from bzrlib.trace import mutter, warning, note
51
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
50
from bzrlib.trace import mutter, warning, note, is_quiet
51
from bzrlib.transform import (TransformPreview, TreeTransform,
52
resolve_conflicts, cook_conflicts,
52
53
conflict_pass, FinalPaths, create_by_entry,
53
54
unique_add, ROOT_PARENT)
54
55
from bzrlib.versionedfile import PlanWeaveMerge
116
117
def _get_base_is_other_ancestor(self):
117
118
if self._base_is_other_ancestor is None:
118
self.base_is_other_ancestor = self.revision_graph.is_ancestor(
119
if self.other_basis is None:
121
self._base_is_other_ancestor = self.revision_graph.is_ancestor(
119
122
self.base_rev_id, self.other_basis)
120
123
return self._base_is_other_ancestor
161
164
return merger, verified
164
def from_revision_ids(pb, this, other, base=None, other_branch=None,
167
def from_revision_ids(pb, tree, other, base=None, other_branch=None,
165
168
base_branch=None, revision_graph=None):
166
169
"""Return a Merger for revision-ids.
170
173
:param base: The revision-id to use as BASE. If not specified, will
171
174
be auto-selected.
172
175
:param other_branch: A branch containing the other revision-id. If
173
not supplied, this.branch is used.
176
not supplied, tree.branch is used.
174
177
:param base_branch: A branch containing the base revision-id. If
175
not supplied, other_branch or this.branch will be used.
178
not supplied, other_branch or tree.branch will be used.
179
:param revision_graph: If you have a revision_graph precomputed, pass
180
it in, otherwise it will be created for you.
176
181
:param pb: A progress indicator
178
merger = Merger(this.branch, this_tree=this, pb=pb,
183
merger = Merger(tree.branch, this_tree=tree, pb=pb,
179
184
revision_graph=revision_graph)
180
185
if other_branch is None:
181
other_branch = this.branch
186
other_branch = tree.branch
182
187
merger.set_other_revision(other, other_branch)
184
189
merger.find_base()
376
381
base_branch.get_rev_id(base_revision[1]))
377
382
self._maybe_fetch(base_branch, self.this_branch, self.base_rev_id)
384
def make_merger(self):
380
385
kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
381
386
'other_tree': self.other_tree,
382
387
'interesting_ids': self.interesting_ids,
383
388
'interesting_files': self.interesting_files,
385
391
if self.merge_type.requires_base:
386
392
kwargs['base_tree'] = self.base_tree
387
393
if self.merge_type.supports_reprocess:
393
399
kwargs['show_base'] = self.show_base
394
400
elif self.show_base:
395
401
raise BzrError("Showing base is not supported for this"
396
" merge type. %s" % self.merge_type)
402
" merge type. %s" % self.merge_type)
397
403
if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
398
404
and not self.base_is_other_ancestor):
399
405
raise errors.CannotReverseCherrypick()
400
if self.merge_type.history_based:
406
if self.merge_type.supports_cherrypick:
401
407
kwargs['cherrypick'] = (not self.base_is_ancestor or
402
408
not self.base_is_other_ancestor)
409
return self.merge_type(pb=self._pb,
410
change_reporter=self.change_reporter,
403
414
self.this_tree.lock_tree_write()
404
415
if self.base_tree is not None:
405
416
self.base_tree.lock_read()
406
417
if self.other_tree is not None:
407
418
self.other_tree.lock_read()
409
merge = self.merge_type(pb=self._pb,
410
change_reporter=self.change_reporter,
420
merge = self.make_merger()
412
422
if self.recurse == 'down':
413
423
for path, file_id in self.this_tree.iter_references():
414
424
sub_tree = self.this_tree.get_nested_tree(file_id, path)
447
458
supports_reprocess = True
448
459
supports_show_base = True
449
460
history_based = False
461
supports_cherrypick = True
450
462
supports_reverse_cherrypick = True
451
463
winner_idx = {"this": 2, "other": 1, "conflict": 1}
453
465
def __init__(self, working_tree, this_tree, base_tree, other_tree,
454
466
interesting_ids=None, reprocess=False, show_base=False,
455
467
pb=DummyProgress(), pp=None, change_reporter=None,
456
interesting_files=None):
468
interesting_files=None, do_merge=True,
457
470
"""Initialize the merger object and perform the merge.
459
472
:param working_tree: The working tree to apply the merge to
482
495
self.interesting_ids = interesting_ids
483
496
self.interesting_files = interesting_files
484
497
self.this_tree = working_tree
485
self.this_tree.lock_tree_write()
486
498
self.base_tree = base_tree
487
self.base_tree.lock_read()
488
499
self.other_tree = other_tree
489
self.other_tree.lock_read()
490
500
self._raw_conflicts = []
491
501
self.cooked_conflicts = []
492
502
self.reprocess = reprocess
496
506
self.change_reporter = change_reporter
507
self.cherrypick = cherrypick
497
508
if self.pp is None:
498
509
self.pp = ProgressPhase("Merge phase", 3, self.pb)
500
self.tt = TreeTransform(working_tree, self.pb)
514
self.this_tree.lock_tree_write()
515
self.base_tree.lock_read()
516
self.other_tree.lock_read()
517
self.tt = TreeTransform(self.this_tree, self.pb)
502
519
self.pp.next_phase()
503
entries = self._entries3()
504
child_pb = ui.ui_factory.nested_progress_bar()
506
for num, (file_id, changed, parents3, names3,
507
executable3) in enumerate(entries):
508
child_pb.update('Preparing file merge', num, len(entries))
509
self._merge_names(file_id, parents3, names3)
511
file_status = self.merge_contents(file_id)
513
file_status = 'unmodified'
514
self._merge_executable(file_id,
515
executable3, file_status)
520
child_pb = ui.ui_factory.nested_progress_bar()
522
fs_conflicts = resolve_conflicts(self.tt, child_pb,
523
lambda t, c: conflict_pass(t, c, self.other_tree))
526
if change_reporter is not None:
527
from bzrlib import delta
528
delta.report_changes(self.tt._iter_changes(), change_reporter)
529
self.cook_conflicts(fs_conflicts)
530
for conflict in self.cooked_conflicts:
520
self._compute_transform()
532
521
self.pp.next_phase()
533
522
results = self.tt.apply(no_conflicts=True)
534
523
self.write_modified(results)
536
working_tree.add_conflicts(self.cooked_conflicts)
525
self.this_tree.add_conflicts(self.cooked_conflicts)
537
526
except UnsupportedOperation:
543
532
self.this_tree.unlock()
535
def make_preview_transform(self):
536
self.base_tree.lock_read()
537
self.other_tree.lock_read()
538
self.tt = TransformPreview(self.this_tree)
541
self._compute_transform()
544
self.other_tree.unlock()
545
self.base_tree.unlock()
549
def _compute_transform(self):
550
entries = self._entries3()
551
child_pb = ui.ui_factory.nested_progress_bar()
553
for num, (file_id, changed, parents3, names3,
554
executable3) in enumerate(entries):
555
child_pb.update('Preparing file merge', num, len(entries))
556
self._merge_names(file_id, parents3, names3)
558
file_status = self.merge_contents(file_id)
560
file_status = 'unmodified'
561
self._merge_executable(file_id,
562
executable3, file_status)
567
child_pb = ui.ui_factory.nested_progress_bar()
569
fs_conflicts = resolve_conflicts(self.tt, child_pb,
570
lambda t, c: conflict_pass(t, c, self.other_tree))
573
if self.change_reporter is not None:
574
from bzrlib import delta
575
delta.report_changes(
576
self.tt.iter_changes(), self.change_reporter)
577
self.cook_conflicts(fs_conflicts)
578
for conflict in self.cooked_conflicts:
546
581
def _entries3(self):
547
582
"""Gather data about files modified between three trees.
553
588
executable3 is a tuple of execute-bit values for base, other and this.
556
iterator = self.other_tree._iter_changes(self.base_tree,
591
iterator = self.other_tree.iter_changes(self.base_tree,
557
592
include_unchanged=True, specific_files=self.interesting_files,
558
593
extra_trees=[self.this_tree])
559
594
for (file_id, paths, changed, versioned, parents, names, kind,
839
874
other_lines = self.get_lines(self.other_tree, file_id)
840
875
this_lines = self.get_lines(self.this_tree, file_id)
841
m3 = Merge3(base_lines, this_lines, other_lines)
876
m3 = Merge3(base_lines, this_lines, other_lines,
877
is_cherrypick=self.cherrypick)
842
878
start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
843
879
if self.show_base is True:
844
880
base_marker = '|' * 7
1013
1049
supports_reverse_cherrypick = False
1014
1050
history_based = True
1016
def __init__(self, working_tree, this_tree, base_tree, other_tree,
1017
interesting_ids=None, pb=DummyProgress(), pp=None,
1018
reprocess=False, change_reporter=None,
1019
interesting_files=None, cherrypick=False):
1020
self.cherrypick = cherrypick
1021
super(WeaveMerger, self).__init__(working_tree, this_tree,
1022
base_tree, other_tree,
1023
interesting_ids=interesting_ids,
1024
pb=pb, pp=pp, reprocess=reprocess,
1025
change_reporter=change_reporter)
1027
1052
def _merged_lines(self, file_id):
1028
1053
"""Generate the merged lines.
1029
1054
There is no distinction between lines that are meant to contain <<<<<<<
1166
1191
merger.reprocess = reprocess
1167
1192
merger.other_rev_id = other_rev_id
1168
1193
merger.other_basis = other_rev_id
1194
get_revision_id = getattr(base_tree, 'get_revision_id', None)
1195
if get_revision_id is None:
1196
get_revision_id = base_tree.last_revision
1197
merger.set_base_revision(get_revision_id(), this_branch)
1169
1198
return merger.do_merge()
1171
1200
def get_merge_type_registry():
1261
1290
for b_index in range(last_j, j):
1262
1291
if b_index in new_b:
1263
1292
if b_index in killed_a:
1264
yield 'conflicted-b', self.lines_b[a_index]
1293
yield 'conflicted-b', self.lines_b[b_index]
1266
1295
yield 'new-b', self.lines_b[b_index]