~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
 
18
 
import errno
19
 
from itertools import chain
20
 
import os
21
 
import warnings
22
 
 
23
18
from bzrlib import (
 
19
    branch as _mod_branch,
 
20
    conflicts as _mod_conflicts,
24
21
    debug,
25
22
    errors,
26
23
    graph as _mod_graph,
 
24
    merge3,
27
25
    osutils,
28
26
    patiencediff,
 
27
    progress,
29
28
    registry,
30
29
    revision as _mod_revision,
 
30
    textfile,
 
31
    trace,
 
32
    transform,
31
33
    tree as _mod_tree,
32
34
    tsort,
33
 
    )
34
 
from bzrlib.branch import Branch
35
 
from bzrlib.conflicts import ConflictList, Conflict
36
 
from bzrlib.errors import (BzrCommandError,
37
 
                           BzrError,
38
 
                           NoCommonAncestor,
39
 
                           NoCommits,
40
 
                           NoSuchRevision,
41
 
                           NoSuchFile,
42
 
                           NotBranchError,
43
 
                           NotVersionedError,
44
 
                           UnrelatedBranches,
45
 
                           UnsupportedOperation,
46
 
                           WorkingTreeNotRevision,
47
 
                           BinaryFile,
48
 
                           )
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
61
 
from bzrlib import ui
62
 
 
 
35
    ui,
 
36
    versionedfile
 
37
    )
 
38
from bzrlib.symbol_versioning import (
 
39
    deprecated_in,
 
40
    deprecated_method,
 
41
    )
63
42
# TODO: Report back as changes are merged in
64
43
 
65
44
 
94
73
        self.show_base = False
95
74
        self.reprocess = False
96
75
        if pb is None:
97
 
            pb = DummyProgress()
 
76
            pb = progress.DummyProgress()
98
77
        self._pb = pb
99
78
        self.pp = None
100
79
        self.recurse = recurse
184
163
                base_revision_id, tree.branch.last_revision())):
185
164
                base_revision_id = None
186
165
            else:
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=
190
169
                                         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]
246
226
        if revno == -1:
247
227
            revision_id = branch.last_revision()
248
228
        else:
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)
252
232
 
 
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
265
246
 
 
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)
275
257
 
276
258
        trees = (self.this_basis_tree, self.other_tree)
277
259
        return [get_id(tree, file_id) for tree in trees]
278
260
 
 
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')")
283
267
        if check_clean:
284
268
            self.compare_basis()
285
269
            if self.this_basis != self.this_rev_id:
286
270
                raise errors.UncommittedChanges(self.this_tree)
287
271
 
 
272
    @deprecated_method(deprecated_in((2, 1, 0)))
288
273
    def compare_basis(self):
289
274
        try:
290
275
            basis_tree = self.revision_tree(self.this_tree.last_revision())
297
282
        self.interesting_files = file_list
298
283
 
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):
301
287
            return
302
288
        self._add_parent()
303
289
 
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)
376
362
 
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
384
370
        else:
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(
401
387
                                            *lcas)
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)
425
411
 
426
412
    def set_base(self, base_revision):
427
413
        """Set the base revision to use for the merge.
428
414
 
429
415
        :param base_revision: A 2-list containing a path and revision number.
430
416
        """
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]:
433
419
            self.find_base()
434
420
        else:
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()
515
502
        finally:
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.")
520
507
        else:
521
 
            note("%d conflicts encountered." % len(merge.cooked_conflicts))
 
508
            trace.note("%d conflicts encountered."
 
509
                       % len(merge.cooked_conflicts))
522
510
 
523
511
        return len(merge.cooked_conflicts)
524
512
 
553
541
 
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)
609
597
        if do_merge:
610
598
            self.do_merge()
611
599
 
614
602
        self.base_tree.lock_read()
615
603
        self.other_tree.lock_read()
616
604
        try:
617
 
            self.tt = TreeTransform(self.this_tree, self.pb)
 
605
            self.tt = transform.TreeTransform(self.this_tree, self.pb)
618
606
            try:
619
607
                self.pp.next_phase()
620
608
                self._compute_transform()
623
611
                self.write_modified(results)
624
612
                try:
625
613
                    self.this_tree.add_conflicts(self.cooked_conflicts)
626
 
                except UnsupportedOperation:
 
614
                except errors.UnsupportedOperation:
627
615
                    pass
628
616
            finally:
629
617
                self.tt.finalize()
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)
640
628
        try:
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()
674
662
        try:
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))
677
665
        finally:
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:
685
 
            warning(conflict)
 
673
            trace.warning(conflict)
686
674
 
687
675
    def _entries3(self):
688
676
        """Gather data about files modified between three trees.
890
878
    def fix_root(self):
891
879
        try:
892
880
            self.tt.final_kind(self.tt.root)
893
 
        except NoSuchFile:
 
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),
903
891
            return
904
892
        try:
905
893
            self.tt.final_kind(other_root)
906
 
        except NoSuchFile:
 
894
        except errors.NoSuchFile:
907
895
            return
908
896
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
909
897
            # the other tree's root is a non-root in the current tree
1162
1150
                self.tt.delete_contents(trans_id)
1163
1151
            if file_id in self.other_tree:
1164
1152
                # OTHER changed the file
1165
 
                create_from_tree(self.tt, trans_id,
1166
 
                                 self.other_tree, file_id)
 
1153
                transform.create_from_tree(self.tt, trans_id,
 
1154
                                           self.other_tree, file_id)
1167
1155
                if not file_in_this:
1168
1156
                    self.tt.version_file(file_id, trans_id)
1169
1157
                return "modified"
1180
1168
                # have agreement that output should be a file.
1181
1169
                try:
1182
1170
                    self.text_merge(file_id, trans_id)
1183
 
                except BinaryFile:
 
1171
                except errors.BinaryFile:
1184
1172
                    return contents_conflict()
1185
1173
                if file_id not in self.this_tree:
1186
1174
                    self.tt.version_file(file_id, trans_id)
1187
1175
                try:
1188
1176
                    self.tt.tree_kind(trans_id)
1189
1177
                    self.tt.delete_contents(trans_id)
1190
 
                except NoSuchFile:
 
1178
                except errors.NoSuchFile:
1191
1179
                    pass
1192
1180
                return "modified"
1193
1181
            else:
1211
1199
            base_lines = []
1212
1200
        other_lines = self.get_lines(self.other_tree, file_id)
1213
1201
        this_lines = self.get_lines(self.this_tree, file_id)
1214
 
        m3 = Merge3(base_lines, this_lines, other_lines,
1215
 
                    is_cherrypick=self.cherrypick)
 
1202
        m3 = merge3.Merge3(base_lines, this_lines, other_lines,
 
1203
                           is_cherrypick=self.cherrypick)
1216
1204
        start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
1217
1205
        if self.show_base is True:
1218
1206
            base_marker = '|' * 7
1273
1261
        """Emit a single conflict file."""
1274
1262
        name = name + '.' + suffix
1275
1263
        trans_id = self.tt.create_path(name, parent_id)
1276
 
        create_from_tree(self.tt, trans_id, tree, file_id, lines)
 
1264
        transform.create_from_tree(self.tt, trans_id, tree, file_id, lines)
1277
1265
        return trans_id
1278
1266
 
1279
1267
    def merge_executable(self, file_id, file_status):
1303
1291
        try:
1304
1292
            if self.tt.final_kind(trans_id) != "file":
1305
1293
                return
1306
 
        except NoSuchFile:
 
1294
        except errors.NoSuchFile:
1307
1295
            return
1308
1296
        if winner == "this":
1309
1297
            executability = this_executable
1320
1308
 
1321
1309
    def cook_conflicts(self, fs_conflicts):
1322
1310
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1323
 
        from conflicts import Conflict
1324
1311
        name_conflicts = {}
1325
 
        self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
1326
 
        fp = FinalPaths(self.tt)
 
1312
        self.cooked_conflicts.extend(transform.cook_conflicts(
 
1313
                fs_conflicts, self.tt))
 
1314
        fp = transform.FinalPaths(self.tt)
1327
1315
        for conflict in self._raw_conflicts:
1328
1316
            conflict_type = conflict[0]
1329
1317
            if conflict_type in ('name conflict', 'parent conflict'):
1331
1319
                conflict_args = conflict[2:]
1332
1320
                if trans_id not in name_conflicts:
1333
1321
                    name_conflicts[trans_id] = {}
1334
 
                unique_add(name_conflicts[trans_id], conflict_type,
1335
 
                           conflict_args)
 
1322
                transform.unique_add(name_conflicts[trans_id], conflict_type,
 
1323
                                     conflict_args)
1336
1324
            if conflict_type == 'contents conflict':
1337
1325
                for trans_id in conflict[1]:
1338
1326
                    file_id = self.tt.final_file_id(trans_id)
1343
1331
                    if path.endswith(suffix):
1344
1332
                        path = path[:-len(suffix)]
1345
1333
                        break
1346
 
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
1334
                c = _mod_conflicts.Conflict.factory(conflict_type,
 
1335
                                                    path=path, file_id=file_id)
1347
1336
                self.cooked_conflicts.append(c)
1348
1337
            if conflict_type == 'text conflict':
1349
1338
                trans_id = conflict[1]
1350
1339
                path = fp.get_path(trans_id)
1351
1340
                file_id = self.tt.final_file_id(trans_id)
1352
 
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
1341
                c = _mod_conflicts.Conflict.factory(conflict_type,
 
1342
                                                    path=path, file_id=file_id)
1353
1343
                self.cooked_conflicts.append(c)
1354
1344
 
1355
1345
        for trans_id, conflicts in name_conflicts.iteritems():
1370
1360
            if this_parent is not None and this_name is not None:
1371
1361
                this_parent_path = \
1372
1362
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
1373
 
                this_path = pathjoin(this_parent_path, this_name)
 
1363
                this_path = osutils.pathjoin(this_parent_path, this_name)
1374
1364
            else:
1375
1365
                this_path = "<deleted>"
1376
1366
            file_id = self.tt.final_file_id(trans_id)
1377
 
            c = Conflict.factory('path conflict', path=this_path,
1378
 
                                 conflict_path=other_path, file_id=file_id)
 
1367
            c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
 
1368
                                                conflict_path=other_path,
 
1369
                                                file_id=file_id)
1379
1370
            self.cooked_conflicts.append(c)
1380
 
        self.cooked_conflicts.sort(key=Conflict.sort_key)
 
1371
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1381
1372
 
1382
1373
 
1383
1374
class WeaveMerger(Merge3Merger):
1404
1395
            name = self.tt.final_name(trans_id) + '.plan'
1405
1396
            contents = ('%10s|%s' % l for l in plan)
1406
1397
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1407
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1408
 
            '>>>>>>> MERGE-SOURCE\n')
 
1398
        textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
1399
                                                 '>>>>>>> MERGE-SOURCE\n')
1409
1400
        return textmerge.merge_lines(self.reprocess)
1410
1401
 
1411
1402
    def text_merge(self, file_id, trans_id):
1417
1408
        lines = list(lines)
1418
1409
        # Note we're checking whether the OUTPUT is binary in this case,
1419
1410
        # because we don't want to get into weave merge guts.
1420
 
        check_text_lines(lines)
 
1411
        textfile.check_text_lines(lines)
1421
1412
        self.tt.create_file(lines, trans_id)
1422
1413
        if conflicts:
1423
1414
            self._raw_conflicts.append(('text conflict', trans_id))
1447
1438
            name = self.tt.final_name(trans_id) + '.plan'
1448
1439
            contents = ('%10s|%s' % l for l in plan)
1449
1440
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1450
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1451
 
            '>>>>>>> MERGE-SOURCE\n')
 
1441
        textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
1442
                                                 '>>>>>>> MERGE-SOURCE\n')
1452
1443
        return textmerge.merge_lines(self.reprocess)
1453
1444
 
1454
1445
 
1456
1447
    """Three-way merger using external diff3 for text merging"""
1457
1448
 
1458
1449
    def dump_file(self, temp_dir, name, tree, file_id):
1459
 
        out_path = pathjoin(temp_dir, name)
 
1450
        out_path = osutils.pathjoin(temp_dir, name)
1460
1451
        out_file = open(out_path, "wb")
1461
1452
        try:
1462
1453
            in_file = tree.get_file(file_id)
1474
1465
        import bzrlib.patch
1475
1466
        temp_dir = osutils.mkdtemp(prefix="bzr-")
1476
1467
        try:
1477
 
            new_file = pathjoin(temp_dir, "new")
 
1468
            new_file = osutils.pathjoin(temp_dir, "new")
1478
1469
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
1479
1470
            base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
1480
1471
            other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
1481
1472
            status = bzrlib.patch.diff3(new_file, this, base, other)
1482
1473
            if status not in (0, 1):
1483
 
                raise BzrError("Unhandled diff3 exit code")
 
1474
                raise errors.BzrError("Unhandled diff3 exit code")
1484
1475
            f = open(new_file, 'rb')
1485
1476
            try:
1486
1477
                self.tt.create_file(f, trans_id)
1504
1495
                other_rev_id=None,
1505
1496
                interesting_files=None,
1506
1497
                this_tree=None,
1507
 
                pb=DummyProgress(),
 
1498
                pb=progress.DummyProgress(),
1508
1499
                change_reporter=None):
1509
1500
    """Primary interface for merging.
1510
1501
 
1513
1504
                     branch.get_revision_tree(base_revision))'
1514
1505
        """
1515
1506
    if this_tree is None:
1516
 
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1517
 
            "parameter as of bzrlib version 0.8.")
 
1507
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
 
1508
                              "parameter as of bzrlib version 0.8.")
1518
1509
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1519
1510
                    pb=pb, change_reporter=change_reporter)
1520
1511
    merger.backup_files = backup_files
1738
1729
        super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1739
1730
        self.a_key = self._key_prefix + (self.a_rev,)
1740
1731
        self.b_key = self._key_prefix + (self.b_rev,)
1741
 
        self.graph = Graph(self.vf)
 
1732
        self.graph = _mod_graph.Graph(self.vf)
1742
1733
        heads = self.graph.heads((self.a_key, self.b_key))
1743
1734
        if len(heads) == 1:
1744
1735
            # one side dominates, so we can just return its values, yay for
1749
1740
                other = b_rev
1750
1741
            else:
1751
1742
                other = a_rev
1752
 
            mutter('found dominating revision for %s\n%s > %s', self.vf,
1753
 
                   self._head_key[-1], other)
 
1743
            trace.mutter('found dominating revision for %s\n%s > %s', self.vf,
 
1744
                         self._head_key[-1], other)
1754
1745
            self._weave = None
1755
1746
        else:
1756
1747
            self._head_key = None
1770
1761
        while True:
1771
1762
            next_lcas = self.graph.find_lca(*cur_ancestors)
1772
1763
            # Map a plain NULL_REVISION to a simple no-ancestors
1773
 
            if next_lcas == set([NULL_REVISION]):
 
1764
            if next_lcas == set([_mod_revision.NULL_REVISION]):
1774
1765
                next_lcas = ()
1775
1766
            # Order the lca's based on when they were merged into the tip
1776
1767
            # While the actual merge portion of weave merge uses a set() of
1788
1779
            elif len(next_lcas) > 2:
1789
1780
                # More than 2 lca's, fall back to grabbing all nodes between
1790
1781
                # this and the unique lca.
1791
 
                mutter('More than 2 LCAs, falling back to all nodes for:'
1792
 
                       ' %s, %s\n=> %s', self.a_key, self.b_key, cur_ancestors)
 
1782
                trace.mutter('More than 2 LCAs, falling back to all nodes for:'
 
1783
                             ' %s, %s\n=> %s',
 
1784
                             self.a_key, self.b_key, cur_ancestors)
1793
1785
                cur_lcas = next_lcas
1794
1786
                while len(cur_lcas) > 1:
1795
1787
                    cur_lcas = self.graph.find_lca(*cur_lcas)
1798
1790
                    unique_lca = None
1799
1791
                else:
1800
1792
                    unique_lca = list(cur_lcas)[0]
1801
 
                    if unique_lca == NULL_REVISION:
 
1793
                    if unique_lca == _mod_revision.NULL_REVISION:
1802
1794
                        # find_lca will return a plain 'NULL_REVISION' rather
1803
1795
                        # than a key tuple when there is no common ancestor, we
1804
1796
                        # prefer to just use None, because it doesn't confuse
1827
1819
            # We remove NULL_REVISION because it isn't a proper tuple key, and
1828
1820
            # thus confuses things like _get_interesting_texts, and our logic
1829
1821
            # to add the texts into the memory weave.
1830
 
            if NULL_REVISION in parent_map:
1831
 
                parent_map.pop(NULL_REVISION)
 
1822
            if _mod_revision.NULL_REVISION in parent_map:
 
1823
                parent_map.pop(_mod_revision.NULL_REVISION)
1832
1824
        else:
1833
1825
            interesting = set()
1834
1826
            for tip in tip_keys:
1986
1978
        lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
1987
1979
        self.lcas = set()
1988
1980
        for lca in lcas:
1989
 
            if lca == NULL_REVISION:
 
1981
            if lca == _mod_revision.NULL_REVISION:
1990
1982
                self.lcas.add(lca)
1991
1983
            else:
1992
1984
                self.lcas.add(lca[-1])