~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Patrick Regan
  • Date: 2009-12-02 20:34:07 UTC
  • mto: (4852.3.3 2.1.0b4-doc-updates)
  • mto: This revision was merged to the branch mainline in revision 4856.
  • Revision ID: patrick.rubbs.regan@gmail.com-20091202203407-fjd0mshgn3j3foel
Removed trailing whitespace from files in doc directory

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
 
66
45
def transform_tree(from_tree, to_tree, interesting_ids=None):
67
 
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
68
 
                interesting_ids=interesting_ids, this_tree=from_tree)
 
46
    from_tree.lock_tree_write()
 
47
    try:
 
48
        merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
 
49
                    interesting_ids=interesting_ids, this_tree=from_tree)
 
50
    finally:
 
51
        from_tree.unlock()
69
52
 
70
53
 
71
54
class Merger(object):
90
73
        self.show_base = False
91
74
        self.reprocess = False
92
75
        if pb is None:
93
 
            pb = DummyProgress()
 
76
            pb = progress.DummyProgress()
94
77
        self._pb = pb
95
78
        self.pp = None
96
79
        self.recurse = recurse
102
85
        self._is_criss_cross = None
103
86
        self._lca_trees = None
104
87
 
 
88
    def cache_trees_with_revision_ids(self, trees):
 
89
        """Cache any tree in trees if it has a revision_id."""
 
90
        for maybe_tree in trees:
 
91
            if maybe_tree is None:
 
92
                continue
 
93
            try:
 
94
                rev_id = maybe_tree.get_revision_id()
 
95
            except AttributeError:
 
96
                continue
 
97
            self._cached_trees[rev_id] = maybe_tree
 
98
 
105
99
    @property
106
100
    def revision_graph(self):
107
101
        if self._revision_graph is None:
169
163
                base_revision_id, tree.branch.last_revision())):
170
164
                base_revision_id = None
171
165
            else:
172
 
                warning('Performing cherrypick')
 
166
                trace.warning('Performing cherrypick')
173
167
        merger = klass.from_revision_ids(pb, tree, other_revision_id,
174
168
                                         base_revision_id, revision_graph=
175
169
                                         revision_graph)
227
221
        if revno is None:
228
222
            tree = workingtree.WorkingTree.open_containing(location)[0]
229
223
            return tree.branch, tree
230
 
        branch = Branch.open_containing(location, possible_transports)[0]
 
224
        branch = _mod_branch.Branch.open_containing(
 
225
            location, possible_transports)[0]
231
226
        if revno == -1:
232
227
            revision_id = branch.last_revision()
233
228
        else:
234
229
            revision_id = branch.get_rev_id(revno)
235
 
        revision_id = ensure_null(revision_id)
 
230
        revision_id = _mod_revision.ensure_null(revision_id)
236
231
        return branch, self.revision_tree(revision_id, branch)
237
232
 
 
233
    @deprecated_method(deprecated_in((2, 1, 0)))
238
234
    def ensure_revision_trees(self):
239
235
        if self.this_revision_tree is None:
240
236
            self.this_basis_tree = self.revision_tree(self.this_basis)
248
244
            other_rev_id = self.other_basis
249
245
            self.other_tree = other_basis_tree
250
246
 
 
247
    @deprecated_method(deprecated_in((2, 1, 0)))
251
248
    def file_revisions(self, file_id):
252
249
        self.ensure_revision_trees()
253
250
        def get_id(tree, file_id):
256
253
        if self.this_rev_id is None:
257
254
            if self.this_basis_tree.get_file_sha1(file_id) != \
258
255
                self.this_tree.get_file_sha1(file_id):
259
 
                raise WorkingTreeNotRevision(self.this_tree)
 
256
                raise errors.WorkingTreeNotRevision(self.this_tree)
260
257
 
261
258
        trees = (self.this_basis_tree, self.other_tree)
262
259
        return [get_id(tree, file_id) for tree in trees]
263
260
 
 
261
    @deprecated_method(deprecated_in((2, 1, 0)))
264
262
    def check_basis(self, check_clean, require_commits=True):
265
263
        if self.this_basis is None and require_commits is True:
266
 
            raise BzrCommandError("This branch has no commits."
267
 
                                  " (perhaps you would prefer 'bzr pull')")
 
264
            raise errors.BzrCommandError(
 
265
                "This branch has no commits."
 
266
                " (perhaps you would prefer 'bzr pull')")
268
267
        if check_clean:
269
268
            self.compare_basis()
270
269
            if self.this_basis != self.this_rev_id:
271
270
                raise errors.UncommittedChanges(self.this_tree)
272
271
 
 
272
    @deprecated_method(deprecated_in((2, 1, 0)))
273
273
    def compare_basis(self):
274
274
        try:
275
275
            basis_tree = self.revision_tree(self.this_tree.last_revision())
282
282
        self.interesting_files = file_list
283
283
 
284
284
    def set_pending(self):
285
 
        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):
286
287
            return
287
288
        self._add_parent()
288
289
 
318
319
            self.other_rev_id = _mod_revision.ensure_null(
319
320
                self.other_branch.last_revision())
320
321
            if _mod_revision.is_null(self.other_rev_id):
321
 
                raise NoCommits(self.other_branch)
 
322
                raise errors.NoCommits(self.other_branch)
322
323
            self.other_basis = self.other_rev_id
323
324
        elif other_revision[1] is not None:
324
325
            self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
327
328
            self.other_rev_id = None
328
329
            self.other_basis = self.other_branch.last_revision()
329
330
            if self.other_basis is None:
330
 
                raise NoCommits(self.other_branch)
 
331
                raise errors.NoCommits(self.other_branch)
331
332
        if self.other_rev_id is not None:
332
333
            self._cached_trees[self.other_rev_id] = self.other_tree
333
334
        self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
360
361
            target.fetch(source, revision_id)
361
362
 
362
363
    def find_base(self):
363
 
        revisions = [ensure_null(self.this_basis),
364
 
                     ensure_null(self.other_basis)]
365
 
        if NULL_REVISION in revisions:
366
 
            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
367
368
            self.base_tree = self.revision_tree(self.base_rev_id)
368
369
            self._is_criss_cross = False
369
370
        else:
370
371
            lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
371
372
            self._is_criss_cross = False
372
373
            if len(lcas) == 0:
373
 
                self.base_rev_id = NULL_REVISION
 
374
                self.base_rev_id = _mod_revision.NULL_REVISION
374
375
            elif len(lcas) == 1:
375
376
                self.base_rev_id = list(lcas)[0]
376
377
            else: # len(lcas) > 1
385
386
                    self.base_rev_id = self.revision_graph.find_unique_lca(
386
387
                                            *lcas)
387
388
                self._is_criss_cross = True
388
 
            if self.base_rev_id == NULL_REVISION:
389
 
                raise UnrelatedBranches()
 
389
            if self.base_rev_id == _mod_revision.NULL_REVISION:
 
390
                raise errors.UnrelatedBranches()
390
391
            if self._is_criss_cross:
391
 
                warning('Warning: criss-cross merge encountered.  See bzr'
392
 
                        ' help criss-cross.')
393
 
                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)
394
395
                interesting_revision_ids = [self.base_rev_id]
395
396
                interesting_revision_ids.extend(lcas)
396
397
                interesting_trees = dict((t.get_revision_id(), t)
406
407
                self.base_tree = self.revision_tree(self.base_rev_id)
407
408
        self.base_is_ancestor = True
408
409
        self.base_is_other_ancestor = True
409
 
        mutter('Base revid: %r' % self.base_rev_id)
 
410
        trace.mutter('Base revid: %r' % self.base_rev_id)
410
411
 
411
412
    def set_base(self, base_revision):
412
413
        """Set the base revision to use for the merge.
413
414
 
414
415
        :param base_revision: A 2-list containing a path and revision number.
415
416
        """
416
 
        mutter("doing merge() with no base_revision specified")
 
417
        trace.mutter("doing merge() with no base_revision specified")
417
418
        if base_revision == [None, None]:
418
419
            self.find_base()
419
420
        else:
439
440
        if self.merge_type.supports_reprocess:
440
441
            kwargs['reprocess'] = self.reprocess
441
442
        elif self.reprocess:
442
 
            raise BzrError("Conflict reduction is not supported for merge"
443
 
                                  " type %s." % self.merge_type)
 
443
            raise errors.BzrError(
 
444
                "Conflict reduction is not supported for merge"
 
445
                " type %s." % self.merge_type)
444
446
        if self.merge_type.supports_show_base:
445
447
            kwargs['show_base'] = self.show_base
446
448
        elif self.show_base:
447
 
            raise BzrError("Showing base is not supported for this"
448
 
                           " merge type. %s" % self.merge_type)
 
449
            raise errors.BzrError("Showing base is not supported for this"
 
450
                                  " merge type. %s" % self.merge_type)
449
451
        if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
450
452
            and not self.base_is_other_ancestor):
451
453
            raise errors.CannotReverseCherrypick()
500
502
        finally:
501
503
            self.this_tree.unlock()
502
504
        if len(merge.cooked_conflicts) == 0:
503
 
            if not self.ignore_zero and not is_quiet():
504
 
                note("All changes applied successfully.")
 
505
            if not self.ignore_zero and not trace.is_quiet():
 
506
                trace.note("All changes applied successfully.")
505
507
        else:
506
 
            note("%d conflicts encountered." % len(merge.cooked_conflicts))
 
508
            trace.note("%d conflicts encountered."
 
509
                       % len(merge.cooked_conflicts))
507
510
 
508
511
        return len(merge.cooked_conflicts)
509
512
 
538
541
 
539
542
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
540
543
                 interesting_ids=None, reprocess=False, show_base=False,
541
 
                 pb=DummyProgress(), pp=None, change_reporter=None,
 
544
                 pb=progress.DummyProgress(), pp=None, change_reporter=None,
542
545
                 interesting_files=None, do_merge=True,
543
546
                 cherrypick=False, lca_trees=None):
544
547
        """Initialize the merger object and perform the merge.
590
593
        self.change_reporter = change_reporter
591
594
        self.cherrypick = cherrypick
592
595
        if self.pp is None:
593
 
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
 
596
            self.pp = progress.ProgressPhase("Merge phase", 3, self.pb)
594
597
        if do_merge:
595
598
            self.do_merge()
596
599
 
598
601
        self.this_tree.lock_tree_write()
599
602
        self.base_tree.lock_read()
600
603
        self.other_tree.lock_read()
601
 
        self.tt = TreeTransform(self.this_tree, self.pb)
602
604
        try:
603
 
            self.pp.next_phase()
604
 
            self._compute_transform()
605
 
            self.pp.next_phase()
606
 
            results = self.tt.apply(no_conflicts=True)
607
 
            self.write_modified(results)
 
605
            self.tt = transform.TreeTransform(self.this_tree, self.pb)
608
606
            try:
609
 
                self.this_tree.add_conflicts(self.cooked_conflicts)
610
 
            except UnsupportedOperation:
611
 
                pass
 
607
                self.pp.next_phase()
 
608
                self._compute_transform()
 
609
                self.pp.next_phase()
 
610
                results = self.tt.apply(no_conflicts=True)
 
611
                self.write_modified(results)
 
612
                try:
 
613
                    self.this_tree.add_conflicts(self.cooked_conflicts)
 
614
                except errors.UnsupportedOperation:
 
615
                    pass
 
616
            finally:
 
617
                self.tt.finalize()
612
618
        finally:
613
 
            self.tt.finalize()
614
619
            self.other_tree.unlock()
615
620
            self.base_tree.unlock()
616
621
            self.this_tree.unlock()
619
624
    def make_preview_transform(self):
620
625
        self.base_tree.lock_read()
621
626
        self.other_tree.lock_read()
622
 
        self.tt = TransformPreview(self.this_tree)
 
627
        self.tt = transform.TransformPreview(self.this_tree)
623
628
        try:
624
629
            self.pp.next_phase()
625
630
            self._compute_transform()
655
660
        self.pp.next_phase()
656
661
        child_pb = ui.ui_factory.nested_progress_bar()
657
662
        try:
658
 
            fs_conflicts = resolve_conflicts(self.tt, child_pb,
659
 
                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))
660
665
        finally:
661
666
            child_pb.finished()
662
667
        if self.change_reporter is not None:
665
670
                self.tt.iter_changes(), self.change_reporter)
666
671
        self.cook_conflicts(fs_conflicts)
667
672
        for conflict in self.cooked_conflicts:
668
 
            warning(conflict)
 
673
            trace.warning(conflict)
669
674
 
670
675
    def _entries3(self):
671
676
        """Gather data about files modified between three trees.
873
878
    def fix_root(self):
874
879
        try:
875
880
            self.tt.final_kind(self.tt.root)
876
 
        except NoSuchFile:
 
881
        except errors.NoSuchFile:
877
882
            self.tt.cancel_deletion(self.tt.root)
878
883
        if self.tt.final_file_id(self.tt.root) is None:
879
884
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
886
891
            return
887
892
        try:
888
893
            self.tt.final_kind(other_root)
889
 
        except NoSuchFile:
 
894
        except errors.NoSuchFile:
890
895
            return
891
896
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
892
897
            # the other tree's root is a non-root in the current tree
1145
1150
                self.tt.delete_contents(trans_id)
1146
1151
            if file_id in self.other_tree:
1147
1152
                # OTHER changed the file
1148
 
                create_from_tree(self.tt, trans_id,
1149
 
                                 self.other_tree, file_id)
 
1153
                transform.create_from_tree(self.tt, trans_id,
 
1154
                                           self.other_tree, file_id)
1150
1155
                if not file_in_this:
1151
1156
                    self.tt.version_file(file_id, trans_id)
1152
1157
                return "modified"
1163
1168
                # have agreement that output should be a file.
1164
1169
                try:
1165
1170
                    self.text_merge(file_id, trans_id)
1166
 
                except BinaryFile:
 
1171
                except errors.BinaryFile:
1167
1172
                    return contents_conflict()
1168
1173
                if file_id not in self.this_tree:
1169
1174
                    self.tt.version_file(file_id, trans_id)
1170
1175
                try:
1171
1176
                    self.tt.tree_kind(trans_id)
1172
1177
                    self.tt.delete_contents(trans_id)
1173
 
                except NoSuchFile:
 
1178
                except errors.NoSuchFile:
1174
1179
                    pass
1175
1180
                return "modified"
1176
1181
            else:
1194
1199
            base_lines = []
1195
1200
        other_lines = self.get_lines(self.other_tree, file_id)
1196
1201
        this_lines = self.get_lines(self.this_tree, file_id)
1197
 
        m3 = Merge3(base_lines, this_lines, other_lines,
1198
 
                    is_cherrypick=self.cherrypick)
 
1202
        m3 = merge3.Merge3(base_lines, this_lines, other_lines,
 
1203
                           is_cherrypick=self.cherrypick)
1199
1204
        start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
1200
1205
        if self.show_base is True:
1201
1206
            base_marker = '|' * 7
1256
1261
        """Emit a single conflict file."""
1257
1262
        name = name + '.' + suffix
1258
1263
        trans_id = self.tt.create_path(name, parent_id)
1259
 
        create_from_tree(self.tt, trans_id, tree, file_id, lines)
 
1264
        transform.create_from_tree(self.tt, trans_id, tree, file_id, lines)
1260
1265
        return trans_id
1261
1266
 
1262
1267
    def merge_executable(self, file_id, file_status):
1286
1291
        try:
1287
1292
            if self.tt.final_kind(trans_id) != "file":
1288
1293
                return
1289
 
        except NoSuchFile:
 
1294
        except errors.NoSuchFile:
1290
1295
            return
1291
1296
        if winner == "this":
1292
1297
            executability = this_executable
1303
1308
 
1304
1309
    def cook_conflicts(self, fs_conflicts):
1305
1310
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1306
 
        from conflicts import Conflict
1307
1311
        name_conflicts = {}
1308
 
        self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
1309
 
        fp = FinalPaths(self.tt)
 
1312
        self.cooked_conflicts.extend(transform.cook_conflicts(
 
1313
                fs_conflicts, self.tt))
 
1314
        fp = transform.FinalPaths(self.tt)
1310
1315
        for conflict in self._raw_conflicts:
1311
1316
            conflict_type = conflict[0]
1312
1317
            if conflict_type in ('name conflict', 'parent conflict'):
1314
1319
                conflict_args = conflict[2:]
1315
1320
                if trans_id not in name_conflicts:
1316
1321
                    name_conflicts[trans_id] = {}
1317
 
                unique_add(name_conflicts[trans_id], conflict_type,
1318
 
                           conflict_args)
 
1322
                transform.unique_add(name_conflicts[trans_id], conflict_type,
 
1323
                                     conflict_args)
1319
1324
            if conflict_type == 'contents conflict':
1320
1325
                for trans_id in conflict[1]:
1321
1326
                    file_id = self.tt.final_file_id(trans_id)
1326
1331
                    if path.endswith(suffix):
1327
1332
                        path = path[:-len(suffix)]
1328
1333
                        break
1329
 
                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)
1330
1336
                self.cooked_conflicts.append(c)
1331
1337
            if conflict_type == 'text conflict':
1332
1338
                trans_id = conflict[1]
1333
1339
                path = fp.get_path(trans_id)
1334
1340
                file_id = self.tt.final_file_id(trans_id)
1335
 
                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)
1336
1343
                self.cooked_conflicts.append(c)
1337
1344
 
1338
1345
        for trans_id, conflicts in name_conflicts.iteritems():
1353
1360
            if this_parent is not None and this_name is not None:
1354
1361
                this_parent_path = \
1355
1362
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
1356
 
                this_path = pathjoin(this_parent_path, this_name)
 
1363
                this_path = osutils.pathjoin(this_parent_path, this_name)
1357
1364
            else:
1358
1365
                this_path = "<deleted>"
1359
1366
            file_id = self.tt.final_file_id(trans_id)
1360
 
            c = Conflict.factory('path conflict', path=this_path,
1361
 
                                 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)
1362
1370
            self.cooked_conflicts.append(c)
1363
 
        self.cooked_conflicts.sort(key=Conflict.sort_key)
 
1371
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1364
1372
 
1365
1373
 
1366
1374
class WeaveMerger(Merge3Merger):
1387
1395
            name = self.tt.final_name(trans_id) + '.plan'
1388
1396
            contents = ('%10s|%s' % l for l in plan)
1389
1397
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1390
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1391
 
            '>>>>>>> MERGE-SOURCE\n')
 
1398
        textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
1399
                                                 '>>>>>>> MERGE-SOURCE\n')
1392
1400
        return textmerge.merge_lines(self.reprocess)
1393
1401
 
1394
1402
    def text_merge(self, file_id, trans_id):
1400
1408
        lines = list(lines)
1401
1409
        # Note we're checking whether the OUTPUT is binary in this case,
1402
1410
        # because we don't want to get into weave merge guts.
1403
 
        check_text_lines(lines)
 
1411
        textfile.check_text_lines(lines)
1404
1412
        self.tt.create_file(lines, trans_id)
1405
1413
        if conflicts:
1406
1414
            self._raw_conflicts.append(('text conflict', trans_id))
1430
1438
            name = self.tt.final_name(trans_id) + '.plan'
1431
1439
            contents = ('%10s|%s' % l for l in plan)
1432
1440
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1433
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1434
 
            '>>>>>>> MERGE-SOURCE\n')
 
1441
        textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
1442
                                                 '>>>>>>> MERGE-SOURCE\n')
1435
1443
        return textmerge.merge_lines(self.reprocess)
1436
1444
 
1437
1445
 
1439
1447
    """Three-way merger using external diff3 for text merging"""
1440
1448
 
1441
1449
    def dump_file(self, temp_dir, name, tree, file_id):
1442
 
        out_path = pathjoin(temp_dir, name)
 
1450
        out_path = osutils.pathjoin(temp_dir, name)
1443
1451
        out_file = open(out_path, "wb")
1444
1452
        try:
1445
1453
            in_file = tree.get_file(file_id)
1457
1465
        import bzrlib.patch
1458
1466
        temp_dir = osutils.mkdtemp(prefix="bzr-")
1459
1467
        try:
1460
 
            new_file = pathjoin(temp_dir, "new")
 
1468
            new_file = osutils.pathjoin(temp_dir, "new")
1461
1469
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
1462
1470
            base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
1463
1471
            other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
1464
1472
            status = bzrlib.patch.diff3(new_file, this, base, other)
1465
1473
            if status not in (0, 1):
1466
 
                raise BzrError("Unhandled diff3 exit code")
 
1474
                raise errors.BzrError("Unhandled diff3 exit code")
1467
1475
            f = open(new_file, 'rb')
1468
1476
            try:
1469
1477
                self.tt.create_file(f, trans_id)
1487
1495
                other_rev_id=None,
1488
1496
                interesting_files=None,
1489
1497
                this_tree=None,
1490
 
                pb=DummyProgress(),
 
1498
                pb=progress.DummyProgress(),
1491
1499
                change_reporter=None):
1492
1500
    """Primary interface for merging.
1493
1501
 
1496
1504
                     branch.get_revision_tree(base_revision))'
1497
1505
        """
1498
1506
    if this_tree is None:
1499
 
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1500
 
            "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.")
1501
1509
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1502
1510
                    pb=pb, change_reporter=change_reporter)
1503
1511
    merger.backup_files = backup_files
1516
1524
    get_revision_id = getattr(base_tree, 'get_revision_id', None)
1517
1525
    if get_revision_id is None:
1518
1526
        get_revision_id = base_tree.last_revision
 
1527
    merger.cache_trees_with_revision_ids([other_tree, base_tree, this_tree])
1519
1528
    merger.set_base_revision(get_revision_id(), this_branch)
1520
1529
    return merger.do_merge()
1521
1530
 
1720
1729
        super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1721
1730
        self.a_key = self._key_prefix + (self.a_rev,)
1722
1731
        self.b_key = self._key_prefix + (self.b_rev,)
1723
 
        self.graph = Graph(self.vf)
 
1732
        self.graph = _mod_graph.Graph(self.vf)
1724
1733
        heads = self.graph.heads((self.a_key, self.b_key))
1725
1734
        if len(heads) == 1:
1726
1735
            # one side dominates, so we can just return its values, yay for
1731
1740
                other = b_rev
1732
1741
            else:
1733
1742
                other = a_rev
1734
 
            mutter('found dominating revision for %s\n%s > %s', self.vf,
1735
 
                   self._head_key[-1], other)
 
1743
            trace.mutter('found dominating revision for %s\n%s > %s', self.vf,
 
1744
                         self._head_key[-1], other)
1736
1745
            self._weave = None
1737
1746
        else:
1738
1747
            self._head_key = None
1752
1761
        while True:
1753
1762
            next_lcas = self.graph.find_lca(*cur_ancestors)
1754
1763
            # Map a plain NULL_REVISION to a simple no-ancestors
1755
 
            if next_lcas == set([NULL_REVISION]):
 
1764
            if next_lcas == set([_mod_revision.NULL_REVISION]):
1756
1765
                next_lcas = ()
1757
1766
            # Order the lca's based on when they were merged into the tip
1758
1767
            # While the actual merge portion of weave merge uses a set() of
1770
1779
            elif len(next_lcas) > 2:
1771
1780
                # More than 2 lca's, fall back to grabbing all nodes between
1772
1781
                # this and the unique lca.
1773
 
                mutter('More than 2 LCAs, falling back to all nodes for:'
1774
 
                       ' %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)
1775
1785
                cur_lcas = next_lcas
1776
1786
                while len(cur_lcas) > 1:
1777
1787
                    cur_lcas = self.graph.find_lca(*cur_lcas)
1780
1790
                    unique_lca = None
1781
1791
                else:
1782
1792
                    unique_lca = list(cur_lcas)[0]
1783
 
                    if unique_lca == NULL_REVISION:
 
1793
                    if unique_lca == _mod_revision.NULL_REVISION:
1784
1794
                        # find_lca will return a plain 'NULL_REVISION' rather
1785
1795
                        # than a key tuple when there is no common ancestor, we
1786
1796
                        # prefer to just use None, because it doesn't confuse
1809
1819
            # We remove NULL_REVISION because it isn't a proper tuple key, and
1810
1820
            # thus confuses things like _get_interesting_texts, and our logic
1811
1821
            # to add the texts into the memory weave.
1812
 
            if NULL_REVISION in parent_map:
1813
 
                parent_map.pop(NULL_REVISION)
 
1822
            if _mod_revision.NULL_REVISION in parent_map:
 
1823
                parent_map.pop(_mod_revision.NULL_REVISION)
1814
1824
        else:
1815
1825
            interesting = set()
1816
1826
            for tip in tip_keys:
1968
1978
        lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
1969
1979
        self.lcas = set()
1970
1980
        for lca in lcas:
1971
 
            if lca == NULL_REVISION:
 
1981
            if lca == _mod_revision.NULL_REVISION:
1972
1982
                self.lcas.add(lca)
1973
1983
            else:
1974
1984
                self.lcas.add(lca[-1])