15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
from itertools import chain
23
18
from bzrlib import (
19
branch as _mod_branch,
20
conflicts as _mod_conflicts,
26
23
graph as _mod_graph,
30
29
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
38
from bzrlib.symbol_versioning import (
63
42
# TODO: Report back as changes are merged in
184
163
base_revision_id, tree.branch.last_revision())):
185
164
base_revision_id = None
187
warning('Performing cherrypick')
166
trace.warning('Performing cherrypick')
188
167
merger = klass.from_revision_ids(pb, tree, other_revision_id,
189
168
base_revision_id, revision_graph=
242
221
if revno is None:
243
222
tree = workingtree.WorkingTree.open_containing(location)[0]
244
223
return tree.branch, tree
245
branch = Branch.open_containing(location, possible_transports)[0]
224
branch = _mod_branch.Branch.open_containing(
225
location, possible_transports)[0]
247
227
revision_id = branch.last_revision()
249
229
revision_id = branch.get_rev_id(revno)
250
revision_id = ensure_null(revision_id)
230
revision_id = _mod_revision.ensure_null(revision_id)
251
231
return branch, self.revision_tree(revision_id, branch)
233
@deprecated_method(deprecated_in((2, 1, 0)))
253
234
def ensure_revision_trees(self):
254
235
if self.this_revision_tree is None:
255
236
self.this_basis_tree = self.revision_tree(self.this_basis)
263
244
other_rev_id = self.other_basis
264
245
self.other_tree = other_basis_tree
247
@deprecated_method(deprecated_in((2, 1, 0)))
266
248
def file_revisions(self, file_id):
267
249
self.ensure_revision_trees()
268
250
def get_id(tree, file_id):
271
253
if self.this_rev_id is None:
272
254
if self.this_basis_tree.get_file_sha1(file_id) != \
273
255
self.this_tree.get_file_sha1(file_id):
274
raise WorkingTreeNotRevision(self.this_tree)
256
raise errors.WorkingTreeNotRevision(self.this_tree)
276
258
trees = (self.this_basis_tree, self.other_tree)
277
259
return [get_id(tree, file_id) for tree in trees]
261
@deprecated_method(deprecated_in((2, 1, 0)))
279
262
def check_basis(self, check_clean, require_commits=True):
280
263
if self.this_basis is None and require_commits is True:
281
raise BzrCommandError("This branch has no commits."
282
" (perhaps you would prefer 'bzr pull')")
264
raise errors.BzrCommandError(
265
"This branch has no commits."
266
" (perhaps you would prefer 'bzr pull')")
284
268
self.compare_basis()
285
269
if self.this_basis != self.this_rev_id:
286
270
raise errors.UncommittedChanges(self.this_tree)
272
@deprecated_method(deprecated_in((2, 1, 0)))
288
273
def compare_basis(self):
290
275
basis_tree = self.revision_tree(self.this_tree.last_revision())
297
282
self.interesting_files = file_list
299
284
def set_pending(self):
300
if not self.base_is_ancestor or not self.base_is_other_ancestor or self.other_rev_id is None:
285
if (not self.base_is_ancestor or not self.base_is_other_ancestor
286
or self.other_rev_id is None):
302
288
self._add_parent()
333
319
self.other_rev_id = _mod_revision.ensure_null(
334
320
self.other_branch.last_revision())
335
321
if _mod_revision.is_null(self.other_rev_id):
336
raise NoCommits(self.other_branch)
322
raise errors.NoCommits(self.other_branch)
337
323
self.other_basis = self.other_rev_id
338
324
elif other_revision[1] is not None:
339
325
self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
342
328
self.other_rev_id = None
343
329
self.other_basis = self.other_branch.last_revision()
344
330
if self.other_basis is None:
345
raise NoCommits(self.other_branch)
331
raise errors.NoCommits(self.other_branch)
346
332
if self.other_rev_id is not None:
347
333
self._cached_trees[self.other_rev_id] = self.other_tree
348
334
self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
375
361
target.fetch(source, revision_id)
377
363
def find_base(self):
378
revisions = [ensure_null(self.this_basis),
379
ensure_null(self.other_basis)]
380
if NULL_REVISION in revisions:
381
self.base_rev_id = NULL_REVISION
364
revisions = [_mod_revision.ensure_null(self.this_basis),
365
_mod_revision.ensure_null(self.other_basis)]
366
if _mod_revision.NULL_REVISION in revisions:
367
self.base_rev_id = _mod_revision.NULL_REVISION
382
368
self.base_tree = self.revision_tree(self.base_rev_id)
383
369
self._is_criss_cross = False
385
371
lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
386
372
self._is_criss_cross = False
387
373
if len(lcas) == 0:
388
self.base_rev_id = NULL_REVISION
374
self.base_rev_id = _mod_revision.NULL_REVISION
389
375
elif len(lcas) == 1:
390
376
self.base_rev_id = list(lcas)[0]
391
377
else: # len(lcas) > 1
400
386
self.base_rev_id = self.revision_graph.find_unique_lca(
402
388
self._is_criss_cross = True
403
if self.base_rev_id == NULL_REVISION:
404
raise UnrelatedBranches()
389
if self.base_rev_id == _mod_revision.NULL_REVISION:
390
raise errors.UnrelatedBranches()
405
391
if self._is_criss_cross:
406
warning('Warning: criss-cross merge encountered. See bzr'
407
' help criss-cross.')
408
mutter('Criss-cross lcas: %r' % lcas)
392
trace.warning('Warning: criss-cross merge encountered. See bzr'
393
' help criss-cross.')
394
trace.mutter('Criss-cross lcas: %r' % lcas)
409
395
interesting_revision_ids = [self.base_rev_id]
410
396
interesting_revision_ids.extend(lcas)
411
397
interesting_trees = dict((t.get_revision_id(), t)
421
407
self.base_tree = self.revision_tree(self.base_rev_id)
422
408
self.base_is_ancestor = True
423
409
self.base_is_other_ancestor = True
424
mutter('Base revid: %r' % self.base_rev_id)
410
trace.mutter('Base revid: %r' % self.base_rev_id)
426
412
def set_base(self, base_revision):
427
413
"""Set the base revision to use for the merge.
429
415
:param base_revision: A 2-list containing a path and revision number.
431
mutter("doing merge() with no base_revision specified")
417
trace.mutter("doing merge() with no base_revision specified")
432
418
if base_revision == [None, None]:
454
440
if self.merge_type.supports_reprocess:
455
441
kwargs['reprocess'] = self.reprocess
456
442
elif self.reprocess:
457
raise BzrError("Conflict reduction is not supported for merge"
458
" type %s." % self.merge_type)
443
raise errors.BzrError(
444
"Conflict reduction is not supported for merge"
445
" type %s." % self.merge_type)
459
446
if self.merge_type.supports_show_base:
460
447
kwargs['show_base'] = self.show_base
461
448
elif self.show_base:
462
raise BzrError("Showing base is not supported for this"
463
" merge type. %s" % self.merge_type)
449
raise errors.BzrError("Showing base is not supported for this"
450
" merge type. %s" % self.merge_type)
464
451
if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
465
452
and not self.base_is_other_ancestor):
466
453
raise errors.CannotReverseCherrypick()
516
503
self.this_tree.unlock()
517
504
if len(merge.cooked_conflicts) == 0:
518
if not self.ignore_zero and not is_quiet():
519
note("All changes applied successfully.")
505
if not self.ignore_zero and not trace.is_quiet():
506
trace.note("All changes applied successfully.")
521
note("%d conflicts encountered." % len(merge.cooked_conflicts))
508
trace.note("%d conflicts encountered."
509
% len(merge.cooked_conflicts))
523
511
return len(merge.cooked_conflicts)
554
542
def __init__(self, working_tree, this_tree, base_tree, other_tree,
555
543
interesting_ids=None, reprocess=False, show_base=False,
556
pb=DummyProgress(), pp=None, change_reporter=None,
544
pb=progress.DummyProgress(), pp=None, change_reporter=None,
557
545
interesting_files=None, do_merge=True,
558
546
cherrypick=False, lca_trees=None):
559
547
"""Initialize the merger object and perform the merge.
605
593
self.change_reporter = change_reporter
606
594
self.cherrypick = cherrypick
607
595
if self.pp is None:
608
self.pp = ProgressPhase("Merge phase", 3, self.pb)
596
self.pp = progress.ProgressPhase("Merge phase", 3, self.pb)
614
602
self.base_tree.lock_read()
615
603
self.other_tree.lock_read()
617
self.tt = TreeTransform(self.this_tree, self.pb)
605
self.tt = transform.TreeTransform(self.this_tree, self.pb)
619
607
self.pp.next_phase()
620
608
self._compute_transform()
636
624
def make_preview_transform(self):
637
625
self.base_tree.lock_read()
638
626
self.other_tree.lock_read()
639
self.tt = TransformPreview(self.this_tree)
627
self.tt = transform.TransformPreview(self.this_tree)
641
629
self.pp.next_phase()
642
630
self._compute_transform()
672
660
self.pp.next_phase()
673
661
child_pb = ui.ui_factory.nested_progress_bar()
675
fs_conflicts = resolve_conflicts(self.tt, child_pb,
676
lambda t, c: conflict_pass(t, c, self.other_tree))
663
fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
664
lambda t, c: transform.conflict_pass(t, c, self.other_tree))
678
666
child_pb.finished()
679
667
if self.change_reporter is not None:
682
670
self.tt.iter_changes(), self.change_reporter)
683
671
self.cook_conflicts(fs_conflicts)
684
672
for conflict in self.cooked_conflicts:
673
trace.warning(conflict)
687
675
def _entries3(self):
688
676
"""Gather data about files modified between three trees.
890
878
def fix_root(self):
892
880
self.tt.final_kind(self.tt.root)
881
except errors.NoSuchFile:
894
882
self.tt.cancel_deletion(self.tt.root)
895
883
if self.tt.final_file_id(self.tt.root) is None:
896
884
self.tt.version_file(self.tt.tree_file_id(self.tt.root),
1176
1164
# Skip the id2path lookup for older formats
1177
1165
filter_tree_path = None
1178
create_from_tree(self.tt, trans_id,
1166
transform.create_from_tree(self.tt, trans_id,
1179
1167
self.other_tree, file_id,
1180
1168
filter_tree_path=filter_tree_path)
1181
1169
if not file_in_this:
1194
1182
# have agreement that output should be a file.
1196
1184
self.text_merge(file_id, trans_id)
1185
except errors.BinaryFile:
1198
1186
return contents_conflict()
1199
1187
if file_id not in self.this_tree:
1200
1188
self.tt.version_file(file_id, trans_id)
1202
1190
self.tt.tree_kind(trans_id)
1203
1191
self.tt.delete_contents(trans_id)
1192
except errors.NoSuchFile:
1206
1194
return "modified"
1225
1213
base_lines = []
1226
1214
other_lines = self.get_lines(self.other_tree, file_id)
1227
1215
this_lines = self.get_lines(self.this_tree, file_id)
1228
m3 = Merge3(base_lines, this_lines, other_lines,
1229
is_cherrypick=self.cherrypick)
1216
m3 = merge3.Merge3(base_lines, this_lines, other_lines,
1217
is_cherrypick=self.cherrypick)
1230
1218
start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
1231
1219
if self.show_base is True:
1232
1220
base_marker = '|' * 7
1301
1289
"""Emit a single conflict file."""
1302
1290
name = name + '.' + suffix
1303
1291
trans_id = self.tt.create_path(name, parent_id)
1304
create_from_tree(self.tt, trans_id, tree, file_id, lines,
1292
transform.create_from_tree(self.tt, trans_id, tree, file_id, lines,
1305
1293
filter_tree_path)
1306
1294
return trans_id
1350
1338
def cook_conflicts(self, fs_conflicts):
1351
1339
"""Convert all conflicts into a form that doesn't depend on trans_id"""
1352
from conflicts import Conflict
1353
1340
name_conflicts = {}
1354
self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
1355
fp = FinalPaths(self.tt)
1341
self.cooked_conflicts.extend(transform.cook_conflicts(
1342
fs_conflicts, self.tt))
1343
fp = transform.FinalPaths(self.tt)
1356
1344
for conflict in self._raw_conflicts:
1357
1345
conflict_type = conflict[0]
1358
1346
if conflict_type in ('name conflict', 'parent conflict'):
1360
1348
conflict_args = conflict[2:]
1361
1349
if trans_id not in name_conflicts:
1362
1350
name_conflicts[trans_id] = {}
1363
unique_add(name_conflicts[trans_id], conflict_type,
1351
transform.unique_add(name_conflicts[trans_id], conflict_type,
1365
1353
if conflict_type == 'contents conflict':
1366
1354
for trans_id in conflict[1]:
1367
1355
file_id = self.tt.final_file_id(trans_id)
1372
1360
if path.endswith(suffix):
1373
1361
path = path[:-len(suffix)]
1375
c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1363
c = _mod_conflicts.Conflict.factory(conflict_type,
1364
path=path, file_id=file_id)
1376
1365
self.cooked_conflicts.append(c)
1377
1366
if conflict_type == 'text conflict':
1378
1367
trans_id = conflict[1]
1379
1368
path = fp.get_path(trans_id)
1380
1369
file_id = self.tt.final_file_id(trans_id)
1381
c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1370
c = _mod_conflicts.Conflict.factory(conflict_type,
1371
path=path, file_id=file_id)
1382
1372
self.cooked_conflicts.append(c)
1384
1374
for trans_id, conflicts in name_conflicts.iteritems():
1399
1389
if this_parent is not None and this_name is not None:
1400
1390
this_parent_path = \
1401
1391
fp.get_path(self.tt.trans_id_file_id(this_parent))
1402
this_path = pathjoin(this_parent_path, this_name)
1392
this_path = osutils.pathjoin(this_parent_path, this_name)
1404
1394
this_path = "<deleted>"
1405
1395
file_id = self.tt.final_file_id(trans_id)
1406
c = Conflict.factory('path conflict', path=this_path,
1407
conflict_path=other_path, file_id=file_id)
1396
c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
1397
conflict_path=other_path,
1408
1399
self.cooked_conflicts.append(c)
1409
self.cooked_conflicts.sort(key=Conflict.sort_key)
1400
self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1412
1403
class WeaveMerger(Merge3Merger):
1436
1427
name = self.tt.final_name(trans_id) + '.plan'
1437
1428
contents = ('%11s|%s' % l for l in plan)
1438
1429
self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1439
textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1440
'>>>>>>> MERGE-SOURCE\n')
1430
textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1431
'>>>>>>> MERGE-SOURCE\n')
1441
1432
lines, conflicts = textmerge.merge_lines(self.reprocess)
1443
1434
base_lines = textmerge.base_from_plan()
1454
1445
lines = list(lines)
1455
1446
# Note we're checking whether the OUTPUT is binary in this case,
1456
1447
# because we don't want to get into weave merge guts.
1457
check_text_lines(lines)
1448
textfile.check_text_lines(lines)
1458
1449
self.tt.create_file(lines, trans_id)
1459
1450
if base_lines is not None:
1472
1463
def _generate_merge_plan(self, file_id, base):
1473
1464
return self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
1477
1467
class Diff3Merger(Merge3Merger):
1478
1468
"""Three-way merger using external diff3 for text merging"""
1480
1470
def dump_file(self, temp_dir, name, tree, file_id):
1481
out_path = pathjoin(temp_dir, name)
1471
out_path = osutils.pathjoin(temp_dir, name)
1482
1472
out_file = open(out_path, "wb")
1484
1474
in_file = tree.get_file(file_id)
1496
1486
import bzrlib.patch
1497
1487
temp_dir = osutils.mkdtemp(prefix="bzr-")
1499
new_file = pathjoin(temp_dir, "new")
1489
new_file = osutils.pathjoin(temp_dir, "new")
1500
1490
this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
1501
1491
base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
1502
1492
other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
1503
1493
status = bzrlib.patch.diff3(new_file, this, base, other)
1504
1494
if status not in (0, 1):
1505
raise BzrError("Unhandled diff3 exit code")
1495
raise errors.BzrError("Unhandled diff3 exit code")
1506
1496
f = open(new_file, 'rb')
1508
1498
self.tt.create_file(f, trans_id)
1535
1525
branch.get_revision_tree(base_revision))'
1537
1527
if this_tree is None:
1538
raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1539
"parameter as of bzrlib version 0.8.")
1528
raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
1529
"parameter as of bzrlib version 0.8.")
1540
1530
merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1541
1531
pb=pb, change_reporter=change_reporter)
1542
1532
merger.backup_files = backup_files
1760
1750
super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1761
1751
self.a_key = self._key_prefix + (self.a_rev,)
1762
1752
self.b_key = self._key_prefix + (self.b_rev,)
1763
self.graph = Graph(self.vf)
1753
self.graph = _mod_graph.Graph(self.vf)
1764
1754
heads = self.graph.heads((self.a_key, self.b_key))
1765
1755
if len(heads) == 1:
1766
1756
# one side dominates, so we can just return its values, yay for
1774
mutter('found dominating revision for %s\n%s > %s', self.vf,
1775
self._head_key[-1], other)
1764
trace.mutter('found dominating revision for %s\n%s > %s', self.vf,
1765
self._head_key[-1], other)
1776
1766
self._weave = None
1778
1768
self._head_key = None
1793
1783
next_lcas = self.graph.find_lca(*cur_ancestors)
1794
1784
# Map a plain NULL_REVISION to a simple no-ancestors
1795
if next_lcas == set([NULL_REVISION]):
1785
if next_lcas == set([_mod_revision.NULL_REVISION]):
1797
1787
# Order the lca's based on when they were merged into the tip
1798
1788
# While the actual merge portion of weave merge uses a set() of
1810
1800
elif len(next_lcas) > 2:
1811
1801
# More than 2 lca's, fall back to grabbing all nodes between
1812
1802
# this and the unique lca.
1813
mutter('More than 2 LCAs, falling back to all nodes for:'
1814
' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
1803
trace.mutter('More than 2 LCAs, falling back to all nodes for:'
1805
self.a_key, self.b_key, cur_ancestors)
1815
1806
cur_lcas = next_lcas
1816
1807
while len(cur_lcas) > 1:
1817
1808
cur_lcas = self.graph.find_lca(*cur_lcas)
1820
1811
unique_lca = None
1822
1813
unique_lca = list(cur_lcas)[0]
1823
if unique_lca == NULL_REVISION:
1814
if unique_lca == _mod_revision.NULL_REVISION:
1824
1815
# find_lca will return a plain 'NULL_REVISION' rather
1825
1816
# than a key tuple when there is no common ancestor, we
1826
1817
# prefer to just use None, because it doesn't confuse
1849
1840
# We remove NULL_REVISION because it isn't a proper tuple key, and
1850
1841
# thus confuses things like _get_interesting_texts, and our logic
1851
1842
# to add the texts into the memory weave.
1852
if NULL_REVISION in parent_map:
1853
parent_map.pop(NULL_REVISION)
1843
if _mod_revision.NULL_REVISION in parent_map:
1844
parent_map.pop(_mod_revision.NULL_REVISION)
1855
1846
interesting = set()
1856
1847
for tip in tip_keys: