~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: INADA Naoki
  • Date: 2011-05-18 06:27:34 UTC
  • mfrom: (5887 +trunk)
  • mto: This revision was merged to the branch mainline in revision 5894.
  • Revision ID: songofacandy@gmail.com-20110518062734-1ilhll0rrqyyp8um
merge from lp:bzr and resolve conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
 
18
 
import errno
19
 
from itertools import chain
20
 
import os
21
17
import warnings
22
18
 
 
19
from bzrlib.lazy_import import lazy_import
 
20
lazy_import(globals(), """
23
21
from bzrlib import (
 
22
    branch as _mod_branch,
 
23
    cleanup,
 
24
    conflicts as _mod_conflicts,
24
25
    debug,
25
 
    errors,
 
26
    generate_ids,
26
27
    graph as _mod_graph,
 
28
    merge3,
27
29
    osutils,
28
30
    patiencediff,
29
 
    registry,
30
31
    revision as _mod_revision,
 
32
    textfile,
 
33
    trace,
 
34
    transform,
31
35
    tree as _mod_tree,
32
36
    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
 
 
 
37
    ui,
 
38
    versionedfile,
 
39
    workingtree,
 
40
    )
 
41
""")
 
42
from bzrlib import (
 
43
    decorators,
 
44
    errors,
 
45
    hooks,
 
46
    )
 
47
from bzrlib.symbol_versioning import (
 
48
    deprecated_in,
 
49
    deprecated_method,
 
50
    )
63
51
# TODO: Report back as changes are merged in
64
52
 
65
53
 
66
54
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)
 
55
    from_tree.lock_tree_write()
 
56
    operation = cleanup.OperationWithCleanups(merge_inner)
 
57
    operation.add_cleanup(from_tree.unlock)
 
58
    operation.run_simple(from_tree.branch, to_tree, from_tree,
 
59
        ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
 
60
 
 
61
 
 
62
class MergeHooks(hooks.Hooks):
 
63
 
 
64
    def __init__(self):
 
65
        hooks.Hooks.__init__(self, "bzrlib.merge", "Merger.hooks")
 
66
        self.add_hook('merge_file_content',
 
67
            "Called with a bzrlib.merge.Merger object to create a per file "
 
68
            "merge object when starting a merge. "
 
69
            "Should return either None or a subclass of "
 
70
            "``bzrlib.merge.AbstractPerFileMerger``. "
 
71
            "Such objects will then be called per file "
 
72
            "that needs to be merged (including when one "
 
73
            "side has deleted the file and the other has changed it). "
 
74
            "See the AbstractPerFileMerger API docs for details on how it is "
 
75
            "used by merge.",
 
76
            (2, 1))
 
77
 
 
78
 
 
79
class AbstractPerFileMerger(object):
 
80
    """PerFileMerger objects are used by plugins extending merge for bzrlib.
 
81
 
 
82
    See ``bzrlib.plugins.news_merge.news_merge`` for an example concrete class.
 
83
    
 
84
    :ivar merger: The Merge3Merger performing the merge.
 
85
    """
 
86
 
 
87
    def __init__(self, merger):
 
88
        """Create a PerFileMerger for use with merger."""
 
89
        self.merger = merger
 
90
 
 
91
    def merge_contents(self, merge_params):
 
92
        """Attempt to merge the contents of a single file.
 
93
        
 
94
        :param merge_params: A bzrlib.merge.MergeHookParams
 
95
        :return : A tuple of (status, chunks), where status is one of
 
96
            'not_applicable', 'success', 'conflicted', or 'delete'.  If status
 
97
            is 'success' or 'conflicted', then chunks should be an iterable of
 
98
            strings for the new file contents.
 
99
        """
 
100
        return ('not applicable', None)
 
101
 
 
102
 
 
103
class PerFileMerger(AbstractPerFileMerger):
 
104
    """Merge individual files when self.file_matches returns True.
 
105
 
 
106
    This class is intended to be subclassed.  The file_matches and
 
107
    merge_matching methods should be overridden with concrete implementations.
 
108
    """
 
109
 
 
110
    def file_matches(self, params):
 
111
        """Return True if merge_matching should be called on this file.
 
112
 
 
113
        Only called with merges of plain files with no clear winner.
 
114
 
 
115
        Subclasses must override this.
 
116
        """
 
117
        raise NotImplementedError(self.file_matches)
 
118
 
 
119
    def get_filename(self, params, tree):
 
120
        """Lookup the filename (i.e. basename, not path), given a Tree (e.g.
 
121
        self.merger.this_tree) and a MergeHookParams.
 
122
        """
 
123
        return osutils.basename(tree.id2path(params.file_id))
 
124
 
 
125
    def get_filepath(self, params, tree):
 
126
        """Calculate the path to the file in a tree.
 
127
 
 
128
        :param params: A MergeHookParams describing the file to merge
 
129
        :param tree: a Tree, e.g. self.merger.this_tree.
 
130
        """
 
131
        return tree.id2path(params.file_id)
 
132
 
 
133
    def merge_contents(self, params):
 
134
        """Merge the contents of a single file."""
 
135
        # Check whether this custom merge logic should be used.
 
136
        if (
 
137
            # OTHER is a straight winner, rely on default merge.
 
138
            params.winner == 'other' or
 
139
            # THIS and OTHER aren't both files.
 
140
            not params.is_file_merge() or
 
141
            # The filename doesn't match *.xml
 
142
            not self.file_matches(params)):
 
143
            return 'not_applicable', None
 
144
        return self.merge_matching(params)
 
145
 
 
146
    def merge_matching(self, params):
 
147
        """Merge the contents of a single file that has matched the criteria
 
148
        in PerFileMerger.merge_contents (is a conflict, is a file,
 
149
        self.file_matches is True).
 
150
 
 
151
        Subclasses must override this.
 
152
        """
 
153
        raise NotImplementedError(self.merge_matching)
 
154
 
 
155
 
 
156
class ConfigurableFileMerger(PerFileMerger):
 
157
    """Merge individual files when configured via a .conf file.
 
158
 
 
159
    This is a base class for concrete custom file merging logic. Concrete
 
160
    classes should implement ``merge_text``.
 
161
 
 
162
    See ``bzrlib.plugins.news_merge.news_merge`` for an example concrete class.
 
163
    
 
164
    :ivar affected_files: The configured file paths to merge.
 
165
 
 
166
    :cvar name_prefix: The prefix to use when looking up configuration
 
167
        details. <name_prefix>_merge_files describes the files targeted by the
 
168
        hook for example.
 
169
        
 
170
    :cvar default_files: The default file paths to merge when no configuration
 
171
        is present.
 
172
    """
 
173
 
 
174
    name_prefix = None
 
175
    default_files = None
 
176
 
 
177
    def __init__(self, merger):
 
178
        super(ConfigurableFileMerger, self).__init__(merger)
 
179
        self.affected_files = None
 
180
        self.default_files = self.__class__.default_files or []
 
181
        self.name_prefix = self.__class__.name_prefix
 
182
        if self.name_prefix is None:
 
183
            raise ValueError("name_prefix must be set.")
 
184
 
 
185
    def file_matches(self, params):
 
186
        """Check whether the file should call the merge hook.
 
187
 
 
188
        <name_prefix>_merge_files configuration variable is a list of files
 
189
        that should use the hook.
 
190
        """
 
191
        affected_files = self.affected_files
 
192
        if affected_files is None:
 
193
            config = self.merger.this_branch.get_config()
 
194
            # Until bzr provides a better policy for caching the config, we
 
195
            # just add the part we're interested in to the params to avoid
 
196
            # reading the config files repeatedly (bazaar.conf, location.conf,
 
197
            # branch.conf).
 
198
            config_key = self.name_prefix + '_merge_files'
 
199
            affected_files = config.get_user_option_as_list(config_key)
 
200
            if affected_files is None:
 
201
                # If nothing was specified in the config, use the default.
 
202
                affected_files = self.default_files
 
203
            self.affected_files = affected_files
 
204
        if affected_files:
 
205
            filepath = self.get_filepath(params, self.merger.this_tree)
 
206
            if filepath in affected_files:
 
207
                return True
 
208
        return False
 
209
 
 
210
    def merge_matching(self, params):
 
211
        return self.merge_text(params)
 
212
 
 
213
    def merge_text(self, params):
 
214
        """Merge the byte contents of a single file.
 
215
 
 
216
        This is called after checking that the merge should be performed in
 
217
        merge_contents, and it should behave as per
 
218
        ``bzrlib.merge.AbstractPerFileMerger.merge_contents``.
 
219
        """
 
220
        raise NotImplementedError(self.merge_text)
 
221
 
 
222
 
 
223
class MergeHookParams(object):
 
224
    """Object holding parameters passed to merge_file_content hooks.
 
225
 
 
226
    There are some fields hooks can access:
 
227
 
 
228
    :ivar file_id: the file ID of the file being merged
 
229
    :ivar trans_id: the transform ID for the merge of this file
 
230
    :ivar this_kind: kind of file_id in 'this' tree
 
231
    :ivar other_kind: kind of file_id in 'other' tree
 
232
    :ivar winner: one of 'this', 'other', 'conflict'
 
233
    """
 
234
 
 
235
    def __init__(self, merger, file_id, trans_id, this_kind, other_kind,
 
236
            winner):
 
237
        self._merger = merger
 
238
        self.file_id = file_id
 
239
        self.trans_id = trans_id
 
240
        self.this_kind = this_kind
 
241
        self.other_kind = other_kind
 
242
        self.winner = winner
 
243
 
 
244
    def is_file_merge(self):
 
245
        """True if this_kind and other_kind are both 'file'."""
 
246
        return self.this_kind == 'file' and self.other_kind == 'file'
 
247
 
 
248
    @decorators.cachedproperty
 
249
    def base_lines(self):
 
250
        """The lines of the 'base' version of the file."""
 
251
        return self._merger.get_lines(self._merger.base_tree, self.file_id)
 
252
 
 
253
    @decorators.cachedproperty
 
254
    def this_lines(self):
 
255
        """The lines of the 'this' version of the file."""
 
256
        return self._merger.get_lines(self._merger.this_tree, self.file_id)
 
257
 
 
258
    @decorators.cachedproperty
 
259
    def other_lines(self):
 
260
        """The lines of the 'other' version of the file."""
 
261
        return self._merger.get_lines(self._merger.other_tree, self.file_id)
69
262
 
70
263
 
71
264
class Merger(object):
 
265
 
 
266
    hooks = MergeHooks()
 
267
 
72
268
    def __init__(self, this_branch, other_tree=None, base_tree=None,
73
269
                 this_tree=None, pb=None, change_reporter=None,
74
270
                 recurse='down', revision_graph=None):
89
285
        self.interesting_files = None
90
286
        self.show_base = False
91
287
        self.reprocess = False
92
 
        if pb is None:
93
 
            pb = DummyProgress()
94
 
        self._pb = pb
 
288
        if pb is not None:
 
289
            warnings.warn("pb parameter to Merger() is deprecated and ignored")
95
290
        self.pp = None
96
291
        self.recurse = recurse
97
292
        self.change_reporter = change_reporter
102
297
        self._is_criss_cross = None
103
298
        self._lca_trees = None
104
299
 
 
300
    def cache_trees_with_revision_ids(self, trees):
 
301
        """Cache any tree in trees if it has a revision_id."""
 
302
        for maybe_tree in trees:
 
303
            if maybe_tree is None:
 
304
                continue
 
305
            try:
 
306
                rev_id = maybe_tree.get_revision_id()
 
307
            except AttributeError:
 
308
                continue
 
309
            self._cached_trees[rev_id] = maybe_tree
 
310
 
105
311
    @property
106
312
    def revision_graph(self):
107
313
        if self._revision_graph is None:
169
375
                base_revision_id, tree.branch.last_revision())):
170
376
                base_revision_id = None
171
377
            else:
172
 
                warning('Performing cherrypick')
 
378
                trace.warning('Performing cherrypick')
173
379
        merger = klass.from_revision_ids(pb, tree, other_revision_id,
174
380
                                         base_revision_id, revision_graph=
175
381
                                         revision_graph)
222
428
        return self._cached_trees[revision_id]
223
429
 
224
430
    def _get_tree(self, treespec, possible_transports=None):
225
 
        from bzrlib import workingtree
226
431
        location, revno = treespec
227
432
        if revno is None:
228
433
            tree = workingtree.WorkingTree.open_containing(location)[0]
229
434
            return tree.branch, tree
230
 
        branch = Branch.open_containing(location, possible_transports)[0]
 
435
        branch = _mod_branch.Branch.open_containing(
 
436
            location, possible_transports)[0]
231
437
        if revno == -1:
232
438
            revision_id = branch.last_revision()
233
439
        else:
234
440
            revision_id = branch.get_rev_id(revno)
235
 
        revision_id = ensure_null(revision_id)
 
441
        revision_id = _mod_revision.ensure_null(revision_id)
236
442
        return branch, self.revision_tree(revision_id, branch)
237
443
 
 
444
    @deprecated_method(deprecated_in((2, 1, 0)))
238
445
    def ensure_revision_trees(self):
239
446
        if self.this_revision_tree is None:
240
447
            self.this_basis_tree = self.revision_tree(self.this_basis)
244
451
        if self.other_rev_id is None:
245
452
            other_basis_tree = self.revision_tree(self.other_basis)
246
453
            if other_basis_tree.has_changes(self.other_tree):
247
 
                raise WorkingTreeNotRevision(self.this_tree)
 
454
                raise errors.WorkingTreeNotRevision(self.this_tree)
248
455
            other_rev_id = self.other_basis
249
456
            self.other_tree = other_basis_tree
250
457
 
 
458
    @deprecated_method(deprecated_in((2, 1, 0)))
251
459
    def file_revisions(self, file_id):
252
460
        self.ensure_revision_trees()
253
 
        def get_id(tree, file_id):
254
 
            revision_id = tree.inventory[file_id].revision
255
 
            return revision_id
256
461
        if self.this_rev_id is None:
257
462
            if self.this_basis_tree.get_file_sha1(file_id) != \
258
463
                self.this_tree.get_file_sha1(file_id):
259
 
                raise WorkingTreeNotRevision(self.this_tree)
 
464
                raise errors.WorkingTreeNotRevision(self.this_tree)
260
465
 
261
466
        trees = (self.this_basis_tree, self.other_tree)
262
 
        return [get_id(tree, file_id) for tree in trees]
 
467
        return [tree.get_file_revision(file_id) for tree in trees]
263
468
 
 
469
    @deprecated_method(deprecated_in((2, 1, 0)))
264
470
    def check_basis(self, check_clean, require_commits=True):
265
471
        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')")
 
472
            raise errors.BzrCommandError(
 
473
                "This branch has no commits."
 
474
                " (perhaps you would prefer 'bzr pull')")
268
475
        if check_clean:
269
476
            self.compare_basis()
270
477
            if self.this_basis != self.this_rev_id:
271
478
                raise errors.UncommittedChanges(self.this_tree)
272
479
 
 
480
    @deprecated_method(deprecated_in((2, 1, 0)))
273
481
    def compare_basis(self):
274
482
        try:
275
483
            basis_tree = self.revision_tree(self.this_tree.last_revision())
282
490
        self.interesting_files = file_list
283
491
 
284
492
    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:
 
493
        if (not self.base_is_ancestor or not self.base_is_other_ancestor
 
494
            or self.other_rev_id is None):
286
495
            return
287
496
        self._add_parent()
288
497
 
289
498
    def _add_parent(self):
290
499
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
291
500
        new_parent_trees = []
 
501
        operation = cleanup.OperationWithCleanups(
 
502
            self.this_tree.set_parent_trees)
292
503
        for revision_id in new_parents:
293
504
            try:
294
505
                tree = self.revision_tree(revision_id)
296
507
                tree = None
297
508
            else:
298
509
                tree.lock_read()
 
510
                operation.add_cleanup(tree.unlock)
299
511
            new_parent_trees.append((revision_id, tree))
300
 
        try:
301
 
            self.this_tree.set_parent_trees(new_parent_trees,
302
 
                                            allow_leftmost_as_ghost=True)
303
 
        finally:
304
 
            for _revision_id, tree in new_parent_trees:
305
 
                if tree is not None:
306
 
                    tree.unlock()
 
512
        operation.run_simple(new_parent_trees, allow_leftmost_as_ghost=True)
307
513
 
308
514
    def set_other(self, other_revision, possible_transports=None):
309
515
        """Set the revision and tree to merge from.
318
524
            self.other_rev_id = _mod_revision.ensure_null(
319
525
                self.other_branch.last_revision())
320
526
            if _mod_revision.is_null(self.other_rev_id):
321
 
                raise NoCommits(self.other_branch)
 
527
                raise errors.NoCommits(self.other_branch)
322
528
            self.other_basis = self.other_rev_id
323
529
        elif other_revision[1] is not None:
324
530
            self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
327
533
            self.other_rev_id = None
328
534
            self.other_basis = self.other_branch.last_revision()
329
535
            if self.other_basis is None:
330
 
                raise NoCommits(self.other_branch)
 
536
                raise errors.NoCommits(self.other_branch)
331
537
        if self.other_rev_id is not None:
332
538
            self._cached_trees[self.other_rev_id] = self.other_tree
333
539
        self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
360
566
            target.fetch(source, revision_id)
361
567
 
362
568
    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
 
569
        revisions = [_mod_revision.ensure_null(self.this_basis),
 
570
                     _mod_revision.ensure_null(self.other_basis)]
 
571
        if _mod_revision.NULL_REVISION in revisions:
 
572
            self.base_rev_id = _mod_revision.NULL_REVISION
367
573
            self.base_tree = self.revision_tree(self.base_rev_id)
368
574
            self._is_criss_cross = False
369
575
        else:
370
576
            lcas = self.revision_graph.find_lca(revisions[0], revisions[1])
371
577
            self._is_criss_cross = False
372
578
            if len(lcas) == 0:
373
 
                self.base_rev_id = NULL_REVISION
 
579
                self.base_rev_id = _mod_revision.NULL_REVISION
374
580
            elif len(lcas) == 1:
375
581
                self.base_rev_id = list(lcas)[0]
376
582
            else: # len(lcas) > 1
 
583
                self._is_criss_cross = True
377
584
                if len(lcas) > 2:
378
585
                    # find_unique_lca can only handle 2 nodes, so we have to
379
586
                    # start back at the beginning. It is a shame to traverse
384
591
                else:
385
592
                    self.base_rev_id = self.revision_graph.find_unique_lca(
386
593
                                            *lcas)
387
 
                self._is_criss_cross = True
388
 
            if self.base_rev_id == NULL_REVISION:
389
 
                raise UnrelatedBranches()
 
594
                sorted_lca_keys = self.revision_graph.find_merge_order(                
 
595
                    revisions[0], lcas)
 
596
                if self.base_rev_id == _mod_revision.NULL_REVISION:
 
597
                    self.base_rev_id = sorted_lca_keys[0]
 
598
                
 
599
            if self.base_rev_id == _mod_revision.NULL_REVISION:
 
600
                raise errors.UnrelatedBranches()
390
601
            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)
394
 
                interesting_revision_ids = [self.base_rev_id]
395
 
                interesting_revision_ids.extend(lcas)
 
602
                trace.warning('Warning: criss-cross merge encountered.  See bzr'
 
603
                              ' help criss-cross.')
 
604
                trace.mutter('Criss-cross lcas: %r' % lcas)
 
605
                if self.base_rev_id in lcas:
 
606
                    trace.mutter('Unable to find unique lca. '
 
607
                                 'Fallback %r as best option.' % self.base_rev_id)
 
608
                interesting_revision_ids = set(lcas)
 
609
                interesting_revision_ids.add(self.base_rev_id)
396
610
                interesting_trees = dict((t.get_revision_id(), t)
397
611
                    for t in self.this_branch.repository.revision_trees(
398
612
                        interesting_revision_ids))
399
613
                self._cached_trees.update(interesting_trees)
400
 
                self.base_tree = interesting_trees.pop(self.base_rev_id)
401
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
402
 
                    revisions[0], lcas)
 
614
                if self.base_rev_id in lcas:
 
615
                    self.base_tree = interesting_trees[self.base_rev_id]
 
616
                else:
 
617
                    self.base_tree = interesting_trees.pop(self.base_rev_id)
403
618
                self._lca_trees = [interesting_trees[key]
404
619
                                   for key in sorted_lca_keys]
405
620
            else:
406
621
                self.base_tree = self.revision_tree(self.base_rev_id)
407
622
        self.base_is_ancestor = True
408
623
        self.base_is_other_ancestor = True
409
 
        mutter('Base revid: %r' % self.base_rev_id)
 
624
        trace.mutter('Base revid: %r' % self.base_rev_id)
410
625
 
411
626
    def set_base(self, base_revision):
412
627
        """Set the base revision to use for the merge.
413
628
 
414
629
        :param base_revision: A 2-list containing a path and revision number.
415
630
        """
416
 
        mutter("doing merge() with no base_revision specified")
 
631
        trace.mutter("doing merge() with no base_revision specified")
417
632
        if base_revision == [None, None]:
418
633
            self.find_base()
419
634
        else:
432
647
                  'other_tree': self.other_tree,
433
648
                  'interesting_ids': self.interesting_ids,
434
649
                  'interesting_files': self.interesting_files,
435
 
                  'pp': self.pp,
 
650
                  'this_branch': self.this_branch,
436
651
                  'do_merge': False}
437
652
        if self.merge_type.requires_base:
438
653
            kwargs['base_tree'] = self.base_tree
439
654
        if self.merge_type.supports_reprocess:
440
655
            kwargs['reprocess'] = self.reprocess
441
656
        elif self.reprocess:
442
 
            raise BzrError("Conflict reduction is not supported for merge"
443
 
                                  " type %s." % self.merge_type)
 
657
            raise errors.BzrError(
 
658
                "Conflict reduction is not supported for merge"
 
659
                " type %s." % self.merge_type)
444
660
        if self.merge_type.supports_show_base:
445
661
            kwargs['show_base'] = self.show_base
446
662
        elif self.show_base:
447
 
            raise BzrError("Showing base is not supported for this"
448
 
                           " merge type. %s" % self.merge_type)
 
663
            raise errors.BzrError("Showing base is not supported for this"
 
664
                                  " merge type. %s" % self.merge_type)
449
665
        if (not getattr(self.merge_type, 'supports_reverse_cherrypick', True)
450
666
            and not self.base_is_other_ancestor):
451
667
            raise errors.CannotReverseCherrypick()
455
671
        if self._is_criss_cross and getattr(self.merge_type,
456
672
                                            'supports_lca_trees', False):
457
673
            kwargs['lca_trees'] = self._lca_trees
458
 
        return self.merge_type(pb=self._pb,
 
674
        return self.merge_type(pb=None,
459
675
                               change_reporter=self.change_reporter,
460
676
                               **kwargs)
461
677
 
462
 
    def _do_merge_to(self, merge):
 
678
    def _do_merge_to(self):
 
679
        merge = self.make_merger()
463
680
        if self.other_branch is not None:
464
681
            self.other_branch.update_references(self.this_branch)
465
682
        merge.do_merge()
479
696
                    sub_tree.branch.repository.revision_tree(base_revision)
480
697
                sub_merge.base_rev_id = base_revision
481
698
                sub_merge.do_merge()
 
699
        return merge
482
700
 
483
701
    def do_merge(self):
 
702
        operation = cleanup.OperationWithCleanups(self._do_merge_to)
484
703
        self.this_tree.lock_tree_write()
485
 
        try:
486
 
            if self.base_tree is not None:
487
 
                self.base_tree.lock_read()
488
 
            try:
489
 
                if self.other_tree is not None:
490
 
                    self.other_tree.lock_read()
491
 
                try:
492
 
                    merge = self.make_merger()
493
 
                    self._do_merge_to(merge)
494
 
                finally:
495
 
                    if self.other_tree is not None:
496
 
                        self.other_tree.unlock()
497
 
            finally:
498
 
                if self.base_tree is not None:
499
 
                    self.base_tree.unlock()
500
 
        finally:
501
 
            self.this_tree.unlock()
 
704
        operation.add_cleanup(self.this_tree.unlock)
 
705
        if self.base_tree is not None:
 
706
            self.base_tree.lock_read()
 
707
            operation.add_cleanup(self.base_tree.unlock)
 
708
        if self.other_tree is not None:
 
709
            self.other_tree.lock_read()
 
710
            operation.add_cleanup(self.other_tree.unlock)
 
711
        merge = operation.run_simple()
502
712
        if len(merge.cooked_conflicts) == 0:
503
 
            if not self.ignore_zero and not is_quiet():
504
 
                note("All changes applied successfully.")
 
713
            if not self.ignore_zero and not trace.is_quiet():
 
714
                trace.note("All changes applied successfully.")
505
715
        else:
506
 
            note("%d conflicts encountered." % len(merge.cooked_conflicts))
 
716
            trace.note("%d conflicts encountered."
 
717
                       % len(merge.cooked_conflicts))
507
718
 
508
719
        return len(merge.cooked_conflicts)
509
720
 
538
749
 
539
750
    def __init__(self, working_tree, this_tree, base_tree, other_tree,
540
751
                 interesting_ids=None, reprocess=False, show_base=False,
541
 
                 pb=DummyProgress(), pp=None, change_reporter=None,
 
752
                 pb=None, pp=None, change_reporter=None,
542
753
                 interesting_files=None, do_merge=True,
543
 
                 cherrypick=False, lca_trees=None):
 
754
                 cherrypick=False, lca_trees=None, this_branch=None):
544
755
        """Initialize the merger object and perform the merge.
545
756
 
546
757
        :param working_tree: The working tree to apply the merge to
547
758
        :param this_tree: The local tree in the merge operation
548
759
        :param base_tree: The common tree in the merge operation
549
760
        :param other_tree: The other tree to merge changes from
 
761
        :param this_branch: The branch associated with this_tree.  Defaults to
 
762
            this_tree.branch if not supplied.
550
763
        :param interesting_ids: The file_ids of files that should be
551
764
            participate in the merge.  May not be combined with
552
765
            interesting_files.
553
766
        :param: reprocess If True, perform conflict-reduction processing.
554
767
        :param show_base: If True, show the base revision in text conflicts.
555
768
            (incompatible with reprocess)
556
 
        :param pb: A Progress bar
 
769
        :param pb: ignored
557
770
        :param pp: A ProgressPhase object
558
771
        :param change_reporter: An object that should report changes made
559
772
        :param interesting_files: The tree-relative paths of files that should
570
783
        if interesting_files is not None and interesting_ids is not None:
571
784
            raise ValueError(
572
785
                'specify either interesting_ids or interesting_files')
 
786
        if this_branch is None:
 
787
            this_branch = this_tree.branch
573
788
        self.interesting_ids = interesting_ids
574
789
        self.interesting_files = interesting_files
575
790
        self.this_tree = working_tree
576
791
        self.base_tree = base_tree
577
792
        self.other_tree = other_tree
 
793
        self.this_branch = this_branch
578
794
        self._raw_conflicts = []
579
795
        self.cooked_conflicts = []
580
796
        self.reprocess = reprocess
585
801
        # making sure we haven't missed any corner cases.
586
802
        # if lca_trees is None:
587
803
        #     self._lca_trees = [self.base_tree]
588
 
        self.pb = pb
589
 
        self.pp = pp
590
804
        self.change_reporter = change_reporter
591
805
        self.cherrypick = cherrypick
592
 
        if self.pp is None:
593
 
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
594
806
        if do_merge:
595
807
            self.do_merge()
 
808
        if pp is not None:
 
809
            warnings.warn("pp argument to Merge3Merger is deprecated")
 
810
        if pb is not None:
 
811
            warnings.warn("pb argument to Merge3Merger is deprecated")
596
812
 
597
813
    def do_merge(self):
 
814
        operation = cleanup.OperationWithCleanups(self._do_merge)
598
815
        self.this_tree.lock_tree_write()
 
816
        operation.add_cleanup(self.this_tree.unlock)
599
817
        self.base_tree.lock_read()
 
818
        operation.add_cleanup(self.base_tree.unlock)
600
819
        self.other_tree.lock_read()
601
 
        self.tt = TreeTransform(self.this_tree, self.pb)
 
820
        operation.add_cleanup(self.other_tree.unlock)
 
821
        operation.run()
 
822
 
 
823
    def _do_merge(self, operation):
 
824
        self.tt = transform.TreeTransform(self.this_tree, None)
 
825
        operation.add_cleanup(self.tt.finalize)
 
826
        self._compute_transform()
 
827
        results = self.tt.apply(no_conflicts=True)
 
828
        self.write_modified(results)
602
829
        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)
608
 
            try:
609
 
                self.this_tree.add_conflicts(self.cooked_conflicts)
610
 
            except UnsupportedOperation:
611
 
                pass
612
 
        finally:
613
 
            self.tt.finalize()
614
 
            self.other_tree.unlock()
615
 
            self.base_tree.unlock()
616
 
            self.this_tree.unlock()
617
 
            self.pb.clear()
 
830
            self.this_tree.add_conflicts(self.cooked_conflicts)
 
831
        except errors.UnsupportedOperation:
 
832
            pass
618
833
 
619
834
    def make_preview_transform(self):
 
835
        operation = cleanup.OperationWithCleanups(self._make_preview_transform)
620
836
        self.base_tree.lock_read()
 
837
        operation.add_cleanup(self.base_tree.unlock)
621
838
        self.other_tree.lock_read()
622
 
        self.tt = TransformPreview(self.this_tree)
623
 
        try:
624
 
            self.pp.next_phase()
625
 
            self._compute_transform()
626
 
            self.pp.next_phase()
627
 
        finally:
628
 
            self.other_tree.unlock()
629
 
            self.base_tree.unlock()
630
 
            self.pb.clear()
 
839
        operation.add_cleanup(self.other_tree.unlock)
 
840
        return operation.run_simple()
 
841
 
 
842
    def _make_preview_transform(self):
 
843
        self.tt = transform.TransformPreview(self.this_tree)
 
844
        self._compute_transform()
631
845
        return self.tt
632
846
 
633
847
    def _compute_transform(self):
639
853
            resolver = self._lca_multi_way
640
854
        child_pb = ui.ui_factory.nested_progress_bar()
641
855
        try:
 
856
            factories = Merger.hooks['merge_file_content']
 
857
            hooks = [factory(self) for factory in factories] + [self]
 
858
            self.active_hooks = [hook for hook in hooks if hook is not None]
642
859
            for num, (file_id, changed, parents3, names3,
643
860
                      executable3) in enumerate(entries):
644
861
                child_pb.update('Preparing file merge', num, len(entries))
645
862
                self._merge_names(file_id, parents3, names3, resolver=resolver)
646
863
                if changed:
647
 
                    file_status = self.merge_contents(file_id)
 
864
                    file_status = self._do_merge_contents(file_id)
648
865
                else:
649
866
                    file_status = 'unmodified'
650
867
                self._merge_executable(file_id,
652
869
        finally:
653
870
            child_pb.finished()
654
871
        self.fix_root()
655
 
        self.pp.next_phase()
 
872
        self._finish_computing_transform()
 
873
 
 
874
    def _finish_computing_transform(self):
 
875
        """Finalize the transform and report the changes.
 
876
 
 
877
        This is the second half of _compute_transform.
 
878
        """
656
879
        child_pb = ui.ui_factory.nested_progress_bar()
657
880
        try:
658
 
            fs_conflicts = resolve_conflicts(self.tt, child_pb,
659
 
                lambda t, c: conflict_pass(t, c, self.other_tree))
 
881
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
 
882
                lambda t, c: transform.conflict_pass(t, c, self.other_tree))
660
883
        finally:
661
884
            child_pb.finished()
662
885
        if self.change_reporter is not None:
665
888
                self.tt.iter_changes(), self.change_reporter)
666
889
        self.cook_conflicts(fs_conflicts)
667
890
        for conflict in self.cooked_conflicts:
668
 
            warning(conflict)
 
891
            trace.warning(conflict)
669
892
 
670
893
    def _entries3(self):
671
894
        """Gather data about files modified between three trees.
678
901
        """
679
902
        result = []
680
903
        iterator = self.other_tree.iter_changes(self.base_tree,
681
 
                include_unchanged=True, specific_files=self.interesting_files,
 
904
                specific_files=self.interesting_files,
682
905
                extra_trees=[self.this_tree])
683
906
        this_entries = dict((e.file_id, e) for p, e in
684
907
                            self.this_tree.iter_entries_by_dir(
856
1079
                        continue
857
1080
                else:
858
1081
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
859
 
                # XXX: We need to handle kind == 'symlink'
860
1082
 
861
1083
            # If we have gotten this far, that means something has changed
862
1084
            result.append((file_id, content_changed,
869
1091
                          ))
870
1092
        return result
871
1093
 
872
 
 
873
1094
    def fix_root(self):
874
 
        try:
875
 
            self.tt.final_kind(self.tt.root)
876
 
        except NoSuchFile:
 
1095
        if self.tt.final_kind(self.tt.root) is None:
877
1096
            self.tt.cancel_deletion(self.tt.root)
878
1097
        if self.tt.final_file_id(self.tt.root) is None:
879
1098
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
884
1103
        other_root = self.tt.trans_id_file_id(other_root_file_id)
885
1104
        if other_root == self.tt.root:
886
1105
            return
887
 
        try:
888
 
            self.tt.final_kind(other_root)
889
 
        except NoSuchFile:
890
 
            return
891
1106
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
892
 
            # the other tree's root is a non-root in the current tree
 
1107
            # the other tree's root is a non-root in the current tree (as when
 
1108
            # a previously unrelated branch is merged into another)
893
1109
            return
894
 
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
895
 
        self.tt.cancel_creation(other_root)
896
 
        self.tt.cancel_versioning(other_root)
897
 
 
898
 
    def reparent_children(self, ie, target):
899
 
        for thing, child in ie.children.iteritems():
 
1110
        if self.tt.final_kind(other_root) is not None:
 
1111
            other_root_is_present = True
 
1112
        else:
 
1113
            # other_root doesn't have a physical representation. We still need
 
1114
            # to move any references to the actual root of the tree.
 
1115
            other_root_is_present = False
 
1116
        # 'other_tree.inventory.root' is not present in this tree. We are
 
1117
        # calling adjust_path for children which *want* to be present with a
 
1118
        # correct place to go.
 
1119
        for _, child in self.other_tree.inventory.root.children.iteritems():
900
1120
            trans_id = self.tt.trans_id_file_id(child.file_id)
901
 
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
 
1121
            if not other_root_is_present:
 
1122
                if self.tt.final_kind(trans_id) is not None:
 
1123
                    # The item exist in the final tree and has a defined place
 
1124
                    # to go already.
 
1125
                    continue
 
1126
            # Move the item into the root
 
1127
            try:
 
1128
                final_name = self.tt.final_name(trans_id)
 
1129
            except errors.NoFinalPath:
 
1130
                # This file is not present anymore, ignore it.
 
1131
                continue
 
1132
            self.tt.adjust_path(final_name, self.tt.root, trans_id)
 
1133
        if other_root_is_present:
 
1134
            self.tt.cancel_creation(other_root)
 
1135
            self.tt.cancel_versioning(other_root)
902
1136
 
903
1137
    def write_modified(self, results):
904
1138
        modified_hashes = {}
936
1170
    @staticmethod
937
1171
    def executable(tree, file_id):
938
1172
        """Determine the executability of a file-id (used as a key method)."""
939
 
        if file_id not in tree:
 
1173
        if not tree.has_id(file_id):
940
1174
            return None
941
1175
        if tree.kind(file_id) != "file":
942
1176
            return False
945
1179
    @staticmethod
946
1180
    def kind(tree, file_id):
947
1181
        """Determine the kind of a file-id (used as a key method)."""
948
 
        if file_id not in tree:
 
1182
        if not tree.has_id(file_id):
949
1183
            return None
950
1184
        return tree.kind(file_id)
951
1185
 
952
1186
    @staticmethod
953
1187
    def _three_way(base, other, this):
954
 
        #if base == other, either they all agree, or only THIS has changed.
955
1188
        if base == other:
 
1189
            # if 'base == other', either they all agree, or only 'this' has
 
1190
            # changed.
956
1191
            return 'this'
957
1192
        elif this not in (base, other):
 
1193
            # 'this' is neither 'base' nor 'other', so both sides changed
958
1194
            return 'conflict'
959
 
        # "Ambiguous clean merge" -- both sides have made the same change.
960
1195
        elif this == other:
 
1196
            # "Ambiguous clean merge" -- both sides have made the same change.
961
1197
            return "this"
962
 
        # this == base: only other has changed.
963
1198
        else:
 
1199
            # this == base: only other has changed.
964
1200
            return "other"
965
1201
 
966
1202
    @staticmethod
1010
1246
                # only has an lca value
1011
1247
                return 'other'
1012
1248
 
1013
 
        # At this point, the lcas disagree, and the tips disagree
 
1249
        # At this point, the lcas disagree, and the tip disagree
1014
1250
        return 'conflict'
1015
1251
 
1016
1252
    @staticmethod
 
1253
    @deprecated_method(deprecated_in((2, 2, 0)))
1017
1254
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1018
1255
        """Do a three-way test on a scalar.
1019
1256
        Return "this", "other" or "conflict", depending whether a value wins.
1034
1271
 
1035
1272
    def merge_names(self, file_id):
1036
1273
        def get_entry(tree):
1037
 
            if file_id in tree.inventory:
 
1274
            if tree.has_id(file_id):
1038
1275
                return tree.inventory[file_id]
1039
1276
            else:
1040
1277
                return None
1069
1306
                parent_id_winner = "other"
1070
1307
        if name_winner == "this" and parent_id_winner == "this":
1071
1308
            return
1072
 
        if name_winner == "conflict":
1073
 
            trans_id = self.tt.trans_id_file_id(file_id)
1074
 
            self._raw_conflicts.append(('name conflict', trans_id,
1075
 
                                        this_name, other_name))
1076
 
        if parent_id_winner == "conflict":
1077
 
            trans_id = self.tt.trans_id_file_id(file_id)
1078
 
            self._raw_conflicts.append(('parent conflict', trans_id,
1079
 
                                        this_parent, other_parent))
 
1309
        if name_winner == 'conflict' or parent_id_winner == 'conflict':
 
1310
            # Creating helpers (.OTHER or .THIS) here cause problems down the
 
1311
            # road if a ContentConflict needs to be created so we should not do
 
1312
            # that
 
1313
            trans_id = self.tt.trans_id_file_id(file_id)
 
1314
            self._raw_conflicts.append(('path conflict', trans_id, file_id,
 
1315
                                        this_parent, this_name,
 
1316
                                        other_parent, other_name))
1080
1317
        if other_name is None:
1081
1318
            # it doesn't matter whether the result was 'other' or
1082
1319
            # 'conflict'-- if there's no 'other', we leave it alone.
1083
1320
            return
1084
 
        # if we get here, name_winner and parent_winner are set to safe values.
1085
 
        trans_id = self.tt.trans_id_file_id(file_id)
1086
1321
        parent_id = parents[self.winner_idx[parent_id_winner]]
1087
1322
        if parent_id is not None:
1088
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1323
            # if we get here, name_winner and parent_winner are set to safe
 
1324
            # values.
1089
1325
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
1090
 
                                parent_trans_id, trans_id)
 
1326
                                self.tt.trans_id_file_id(parent_id),
 
1327
                                self.tt.trans_id_file_id(file_id))
1091
1328
 
1092
 
    def merge_contents(self, file_id):
 
1329
    def _do_merge_contents(self, file_id):
1093
1330
        """Performs a merge on file_id contents."""
1094
1331
        def contents_pair(tree):
1095
1332
            if file_id not in tree:
1103
1340
                contents = None
1104
1341
            return kind, contents
1105
1342
 
1106
 
        def contents_conflict():
1107
 
            trans_id = self.tt.trans_id_file_id(file_id)
1108
 
            name = self.tt.final_name(trans_id)
1109
 
            parent_id = self.tt.final_parent(trans_id)
1110
 
            if file_id in self.this_tree.inventory:
1111
 
                self.tt.unversion_file(trans_id)
1112
 
                if file_id in self.this_tree:
1113
 
                    self.tt.delete_contents(trans_id)
1114
 
            file_group = self._dump_conflicts(name, parent_id, file_id,
1115
 
                                              set_version=True)
1116
 
            self._raw_conflicts.append(('contents conflict', file_group))
1117
 
 
1118
1343
        # See SPOT run.  run, SPOT, run.
1119
1344
        # So we're not QUITE repeating ourselves; we do tricky things with
1120
1345
        # file kind...
1136
1361
        if winner == 'this':
1137
1362
            # No interesting changes introduced by OTHER
1138
1363
            return "unmodified"
 
1364
        # We have a hypothetical conflict, but if we have files, then we
 
1365
        # can try to merge the content
1139
1366
        trans_id = self.tt.trans_id_file_id(file_id)
1140
 
        if winner == 'other':
 
1367
        params = MergeHookParams(self, file_id, trans_id, this_pair[0],
 
1368
            other_pair[0], winner)
 
1369
        hooks = self.active_hooks
 
1370
        hook_status = 'not_applicable'
 
1371
        for hook in hooks:
 
1372
            hook_status, lines = hook.merge_contents(params)
 
1373
            if hook_status != 'not_applicable':
 
1374
                # Don't try any more hooks, this one applies.
 
1375
                break
 
1376
        result = "modified"
 
1377
        if hook_status == 'not_applicable':
 
1378
            # This is a contents conflict, because none of the available
 
1379
            # functions could merge it.
 
1380
            result = None
 
1381
            name = self.tt.final_name(trans_id)
 
1382
            parent_id = self.tt.final_parent(trans_id)
 
1383
            if self.this_tree.has_id(file_id):
 
1384
                self.tt.unversion_file(trans_id)
 
1385
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
1386
                                              set_version=True)
 
1387
            self._raw_conflicts.append(('contents conflict', file_group))
 
1388
        elif hook_status == 'success':
 
1389
            self.tt.create_file(lines, trans_id)
 
1390
        elif hook_status == 'conflicted':
 
1391
            # XXX: perhaps the hook should be able to provide
 
1392
            # the BASE/THIS/OTHER files?
 
1393
            self.tt.create_file(lines, trans_id)
 
1394
            self._raw_conflicts.append(('text conflict', trans_id))
 
1395
            name = self.tt.final_name(trans_id)
 
1396
            parent_id = self.tt.final_parent(trans_id)
 
1397
            self._dump_conflicts(name, parent_id, file_id)
 
1398
        elif hook_status == 'delete':
 
1399
            self.tt.unversion_file(trans_id)
 
1400
            result = "deleted"
 
1401
        elif hook_status == 'done':
 
1402
            # The hook function did whatever it needs to do directly, no
 
1403
            # further action needed here.
 
1404
            pass
 
1405
        else:
 
1406
            raise AssertionError('unknown hook_status: %r' % (hook_status,))
 
1407
        if not self.this_tree.has_id(file_id) and result == "modified":
 
1408
            self.tt.version_file(file_id, trans_id)
 
1409
        # The merge has been performed, so the old contents should not be
 
1410
        # retained.
 
1411
        self.tt.delete_contents(trans_id)
 
1412
        return result
 
1413
 
 
1414
    def _default_other_winner_merge(self, merge_hook_params):
 
1415
        """Replace this contents with other."""
 
1416
        file_id = merge_hook_params.file_id
 
1417
        trans_id = merge_hook_params.trans_id
 
1418
        file_in_this = self.this_tree.has_id(file_id)
 
1419
        if self.other_tree.has_id(file_id):
 
1420
            # OTHER changed the file
 
1421
            wt = self.this_tree
 
1422
            if wt.supports_content_filtering():
 
1423
                # We get the path from the working tree if it exists.
 
1424
                # That fails though when OTHER is adding a file, so
 
1425
                # we fall back to the other tree to find the path if
 
1426
                # it doesn't exist locally.
 
1427
                try:
 
1428
                    filter_tree_path = wt.id2path(file_id)
 
1429
                except errors.NoSuchId:
 
1430
                    filter_tree_path = self.other_tree.id2path(file_id)
 
1431
            else:
 
1432
                # Skip the id2path lookup for older formats
 
1433
                filter_tree_path = None
 
1434
            transform.create_from_tree(self.tt, trans_id,
 
1435
                             self.other_tree, file_id,
 
1436
                             filter_tree_path=filter_tree_path)
 
1437
            return 'done', None
 
1438
        elif file_in_this:
 
1439
            # OTHER deleted the file
 
1440
            return 'delete', None
 
1441
        else:
 
1442
            raise AssertionError(
 
1443
                'winner is OTHER, but file_id %r not in THIS or OTHER tree'
 
1444
                % (file_id,))
 
1445
 
 
1446
    def merge_contents(self, merge_hook_params):
 
1447
        """Fallback merge logic after user installed hooks."""
 
1448
        # This function is used in merge hooks as the fallback instance.
 
1449
        # Perhaps making this function and the functions it calls be a 
 
1450
        # a separate class would be better.
 
1451
        if merge_hook_params.winner == 'other':
1141
1452
            # OTHER is a straight winner, so replace this contents with other
1142
 
            file_in_this = file_id in self.this_tree
1143
 
            if file_in_this:
1144
 
                # Remove any existing contents
1145
 
                self.tt.delete_contents(trans_id)
1146
 
            if file_id in self.other_tree:
1147
 
                # OTHER changed the file
1148
 
                create_from_tree(self.tt, trans_id,
1149
 
                                 self.other_tree, file_id)
1150
 
                if not file_in_this:
1151
 
                    self.tt.version_file(file_id, trans_id)
1152
 
                return "modified"
1153
 
            elif file_in_this:
1154
 
                # OTHER deleted the file
1155
 
                self.tt.unversion_file(trans_id)
1156
 
                return "deleted"
 
1453
            return self._default_other_winner_merge(merge_hook_params)
 
1454
        elif merge_hook_params.is_file_merge():
 
1455
            # THIS and OTHER are both files, so text merge.  Either
 
1456
            # BASE is a file, or both converted to files, so at least we
 
1457
            # have agreement that output should be a file.
 
1458
            try:
 
1459
                self.text_merge(merge_hook_params.file_id,
 
1460
                    merge_hook_params.trans_id)
 
1461
            except errors.BinaryFile:
 
1462
                return 'not_applicable', None
 
1463
            return 'done', None
1157
1464
        else:
1158
 
            # We have a hypothetical conflict, but if we have files, then we
1159
 
            # can try to merge the content
1160
 
            if this_pair[0] == 'file' and other_pair[0] == 'file':
1161
 
                # THIS and OTHER are both files, so text merge.  Either
1162
 
                # BASE is a file, or both converted to files, so at least we
1163
 
                # have agreement that output should be a file.
1164
 
                try:
1165
 
                    self.text_merge(file_id, trans_id)
1166
 
                except BinaryFile:
1167
 
                    return contents_conflict()
1168
 
                if file_id not in self.this_tree:
1169
 
                    self.tt.version_file(file_id, trans_id)
1170
 
                try:
1171
 
                    self.tt.tree_kind(trans_id)
1172
 
                    self.tt.delete_contents(trans_id)
1173
 
                except NoSuchFile:
1174
 
                    pass
1175
 
                return "modified"
1176
 
            else:
1177
 
                return contents_conflict()
 
1465
            return 'not_applicable', None
1178
1466
 
1179
1467
    def get_lines(self, tree, file_id):
1180
1468
        """Return the lines in a file, or an empty list."""
1181
 
        if file_id in tree:
1182
 
            return tree.get_file(file_id).readlines()
 
1469
        if tree.has_id(file_id):
 
1470
            return tree.get_file_lines(file_id)
1183
1471
        else:
1184
1472
            return []
1185
1473
 
1187
1475
        """Perform a three-way text merge on a file_id"""
1188
1476
        # it's possible that we got here with base as a different type.
1189
1477
        # if so, we just want two-way text conflicts.
1190
 
        if file_id in self.base_tree and \
 
1478
        if self.base_tree.has_id(file_id) and \
1191
1479
            self.base_tree.kind(file_id) == "file":
1192
1480
            base_lines = self.get_lines(self.base_tree, file_id)
1193
1481
        else:
1194
1482
            base_lines = []
1195
1483
        other_lines = self.get_lines(self.other_tree, file_id)
1196
1484
        this_lines = self.get_lines(self.this_tree, file_id)
1197
 
        m3 = Merge3(base_lines, this_lines, other_lines,
1198
 
                    is_cherrypick=self.cherrypick)
 
1485
        m3 = merge3.Merge3(base_lines, this_lines, other_lines,
 
1486
                           is_cherrypick=self.cherrypick)
1199
1487
        start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
1200
1488
        if self.show_base is True:
1201
1489
            base_marker = '|' * 7
1239
1527
                ('THIS', self.this_tree, this_lines)]
1240
1528
        if not no_base:
1241
1529
            data.append(('BASE', self.base_tree, base_lines))
 
1530
 
 
1531
        # We need to use the actual path in the working tree of the file here,
 
1532
        # ignoring the conflict suffixes
 
1533
        wt = self.this_tree
 
1534
        if wt.supports_content_filtering():
 
1535
            try:
 
1536
                filter_tree_path = wt.id2path(file_id)
 
1537
            except errors.NoSuchId:
 
1538
                # file has been deleted
 
1539
                filter_tree_path = None
 
1540
        else:
 
1541
            # Skip the id2path lookup for older formats
 
1542
            filter_tree_path = None
 
1543
 
1242
1544
        versioned = False
1243
1545
        file_group = []
1244
1546
        for suffix, tree, lines in data:
1245
 
            if file_id in tree:
 
1547
            if tree.has_id(file_id):
1246
1548
                trans_id = self._conflict_file(name, parent_id, tree, file_id,
1247
 
                                               suffix, lines)
 
1549
                                               suffix, lines, filter_tree_path)
1248
1550
                file_group.append(trans_id)
1249
1551
                if set_version and not versioned:
1250
1552
                    self.tt.version_file(file_id, trans_id)
1252
1554
        return file_group
1253
1555
 
1254
1556
    def _conflict_file(self, name, parent_id, tree, file_id, suffix,
1255
 
                       lines=None):
 
1557
                       lines=None, filter_tree_path=None):
1256
1558
        """Emit a single conflict file."""
1257
1559
        name = name + '.' + suffix
1258
1560
        trans_id = self.tt.create_path(name, parent_id)
1259
 
        create_from_tree(self.tt, trans_id, tree, file_id, lines)
 
1561
        transform.create_from_tree(self.tt, trans_id, tree, file_id, lines,
 
1562
            filter_tree_path)
1260
1563
        return trans_id
1261
1564
 
1262
1565
    def merge_executable(self, file_id, file_status):
1283
1586
        if winner == 'this' and file_status != "modified":
1284
1587
            return
1285
1588
        trans_id = self.tt.trans_id_file_id(file_id)
1286
 
        try:
1287
 
            if self.tt.final_kind(trans_id) != "file":
1288
 
                return
1289
 
        except NoSuchFile:
 
1589
        if self.tt.final_kind(trans_id) != "file":
1290
1590
            return
1291
1591
        if winner == "this":
1292
1592
            executability = this_executable
1293
1593
        else:
1294
 
            if file_id in self.other_tree:
 
1594
            if self.other_tree.has_id(file_id):
1295
1595
                executability = other_executable
1296
 
            elif file_id in self.this_tree:
 
1596
            elif self.this_tree.has_id(file_id):
1297
1597
                executability = this_executable
1298
 
            elif file_id in self.base_tree:
 
1598
            elif self.base_tree_has_id(file_id):
1299
1599
                executability = base_executable
1300
1600
        if executability is not None:
1301
1601
            trans_id = self.tt.trans_id_file_id(file_id)
1303
1603
 
1304
1604
    def cook_conflicts(self, fs_conflicts):
1305
1605
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1306
 
        from conflicts import Conflict
1307
 
        name_conflicts = {}
1308
 
        self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
1309
 
        fp = FinalPaths(self.tt)
 
1606
        self.cooked_conflicts.extend(transform.cook_conflicts(
 
1607
                fs_conflicts, self.tt))
 
1608
        fp = transform.FinalPaths(self.tt)
1310
1609
        for conflict in self._raw_conflicts:
1311
1610
            conflict_type = conflict[0]
1312
 
            if conflict_type in ('name conflict', 'parent conflict'):
1313
 
                trans_id = conflict[1]
1314
 
                conflict_args = conflict[2:]
1315
 
                if trans_id not in name_conflicts:
1316
 
                    name_conflicts[trans_id] = {}
1317
 
                unique_add(name_conflicts[trans_id], conflict_type,
1318
 
                           conflict_args)
1319
 
            if conflict_type == 'contents conflict':
 
1611
            if conflict_type == 'path conflict':
 
1612
                (trans_id, file_id,
 
1613
                this_parent, this_name,
 
1614
                other_parent, other_name) = conflict[1:]
 
1615
                if this_parent is None or this_name is None:
 
1616
                    this_path = '<deleted>'
 
1617
                else:
 
1618
                    parent_path =  fp.get_path(
 
1619
                        self.tt.trans_id_file_id(this_parent))
 
1620
                    this_path = osutils.pathjoin(parent_path, this_name)
 
1621
                if other_parent is None or other_name is None:
 
1622
                    other_path = '<deleted>'
 
1623
                else:
 
1624
                    parent_path =  fp.get_path(
 
1625
                        self.tt.trans_id_file_id(other_parent))
 
1626
                    other_path = osutils.pathjoin(parent_path, other_name)
 
1627
                c = _mod_conflicts.Conflict.factory(
 
1628
                    'path conflict', path=this_path,
 
1629
                    conflict_path=other_path,
 
1630
                    file_id=file_id)
 
1631
            elif conflict_type == 'contents conflict':
1320
1632
                for trans_id in conflict[1]:
1321
1633
                    file_id = self.tt.final_file_id(trans_id)
1322
1634
                    if file_id is not None:
1326
1638
                    if path.endswith(suffix):
1327
1639
                        path = path[:-len(suffix)]
1328
1640
                        break
1329
 
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1330
 
                self.cooked_conflicts.append(c)
1331
 
            if conflict_type == 'text conflict':
 
1641
                c = _mod_conflicts.Conflict.factory(conflict_type,
 
1642
                                                    path=path, file_id=file_id)
 
1643
            elif conflict_type == 'text conflict':
1332
1644
                trans_id = conflict[1]
1333
1645
                path = fp.get_path(trans_id)
1334
1646
                file_id = self.tt.final_file_id(trans_id)
1335
 
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
1336
 
                self.cooked_conflicts.append(c)
1337
 
 
1338
 
        for trans_id, conflicts in name_conflicts.iteritems():
1339
 
            try:
1340
 
                this_parent, other_parent = conflicts['parent conflict']
1341
 
                if this_parent == other_parent:
1342
 
                    raise AssertionError()
1343
 
            except KeyError:
1344
 
                this_parent = other_parent = \
1345
 
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
1346
 
            try:
1347
 
                this_name, other_name = conflicts['name conflict']
1348
 
                if this_name == other_name:
1349
 
                    raise AssertionError()
1350
 
            except KeyError:
1351
 
                this_name = other_name = self.tt.final_name(trans_id)
1352
 
            other_path = fp.get_path(trans_id)
1353
 
            if this_parent is not None and this_name is not None:
1354
 
                this_parent_path = \
1355
 
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
1356
 
                this_path = pathjoin(this_parent_path, this_name)
 
1647
                c = _mod_conflicts.Conflict.factory(conflict_type,
 
1648
                                                    path=path, file_id=file_id)
1357
1649
            else:
1358
 
                this_path = "<deleted>"
1359
 
            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)
 
1650
                raise AssertionError('bad conflict type: %r' % (conflict,))
1362
1651
            self.cooked_conflicts.append(c)
1363
 
        self.cooked_conflicts.sort(key=Conflict.sort_key)
 
1652
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1364
1653
 
1365
1654
 
1366
1655
class WeaveMerger(Merge3Merger):
1370
1659
    supports_reverse_cherrypick = False
1371
1660
    history_based = True
1372
1661
 
1373
 
    def _merged_lines(self, file_id):
1374
 
        """Generate the merged lines.
1375
 
        There is no distinction between lines that are meant to contain <<<<<<<
1376
 
        and conflicts.
1377
 
        """
1378
 
        if self.cherrypick:
1379
 
            base = self.base_tree
1380
 
        else:
1381
 
            base = None
1382
 
        plan = self.this_tree.plan_file_merge(file_id, self.other_tree,
 
1662
    def _generate_merge_plan(self, file_id, base):
 
1663
        return self.this_tree.plan_file_merge(file_id, self.other_tree,
1383
1664
                                              base=base)
 
1665
 
 
1666
    def _merged_lines(self, file_id):
 
1667
        """Generate the merged lines.
 
1668
        There is no distinction between lines that are meant to contain <<<<<<<
 
1669
        and conflicts.
 
1670
        """
 
1671
        if self.cherrypick:
 
1672
            base = self.base_tree
 
1673
        else:
 
1674
            base = None
 
1675
        plan = self._generate_merge_plan(file_id, base)
1384
1676
        if 'merge' in debug.debug_flags:
1385
1677
            plan = list(plan)
1386
1678
            trans_id = self.tt.trans_id_file_id(file_id)
1387
1679
            name = self.tt.final_name(trans_id) + '.plan'
1388
 
            contents = ('%10s|%s' % l for l in plan)
 
1680
            contents = ('%11s|%s' % l for l in plan)
1389
1681
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1390
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1391
 
            '>>>>>>> MERGE-SOURCE\n')
1392
 
        return textmerge.merge_lines(self.reprocess)
 
1682
        textmerge = versionedfile.PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
1683
                                                 '>>>>>>> MERGE-SOURCE\n')
 
1684
        lines, conflicts = textmerge.merge_lines(self.reprocess)
 
1685
        if conflicts:
 
1686
            base_lines = textmerge.base_from_plan()
 
1687
        else:
 
1688
            base_lines = None
 
1689
        return lines, base_lines
1393
1690
 
1394
1691
    def text_merge(self, file_id, trans_id):
1395
1692
        """Perform a (weave) text merge for a given file and file-id.
1396
1693
        If conflicts are encountered, .THIS and .OTHER files will be emitted,
1397
1694
        and a conflict will be noted.
1398
1695
        """
1399
 
        lines, conflicts = self._merged_lines(file_id)
 
1696
        lines, base_lines = self._merged_lines(file_id)
1400
1697
        lines = list(lines)
1401
1698
        # Note we're checking whether the OUTPUT is binary in this case,
1402
1699
        # because we don't want to get into weave merge guts.
1403
 
        check_text_lines(lines)
 
1700
        textfile.check_text_lines(lines)
1404
1701
        self.tt.create_file(lines, trans_id)
1405
 
        if conflicts:
 
1702
        if base_lines is not None:
 
1703
            # Conflict
1406
1704
            self._raw_conflicts.append(('text conflict', trans_id))
1407
1705
            name = self.tt.final_name(trans_id)
1408
1706
            parent_id = self.tt.final_parent(trans_id)
1409
1707
            file_group = self._dump_conflicts(name, parent_id, file_id,
1410
 
                                              no_base=True)
 
1708
                                              no_base=False,
 
1709
                                              base_lines=base_lines)
1411
1710
            file_group.append(trans_id)
1412
1711
 
1413
1712
 
1414
1713
class LCAMerger(WeaveMerger):
1415
1714
 
1416
 
    def _merged_lines(self, file_id):
1417
 
        """Generate the merged lines.
1418
 
        There is no distinction between lines that are meant to contain <<<<<<<
1419
 
        and conflicts.
1420
 
        """
1421
 
        if self.cherrypick:
1422
 
            base = self.base_tree
1423
 
        else:
1424
 
            base = None
1425
 
        plan = self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
 
1715
    def _generate_merge_plan(self, file_id, base):
 
1716
        return self.this_tree.plan_file_lca_merge(file_id, self.other_tree,
1426
1717
                                                  base=base)
1427
 
        if 'merge' in debug.debug_flags:
1428
 
            plan = list(plan)
1429
 
            trans_id = self.tt.trans_id_file_id(file_id)
1430
 
            name = self.tt.final_name(trans_id) + '.plan'
1431
 
            contents = ('%10s|%s' % l for l in plan)
1432
 
            self.tt.new_file(name, self.tt.final_parent(trans_id), contents)
1433
 
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
1434
 
            '>>>>>>> MERGE-SOURCE\n')
1435
 
        return textmerge.merge_lines(self.reprocess)
1436
 
 
1437
1718
 
1438
1719
class Diff3Merger(Merge3Merger):
1439
1720
    """Three-way merger using external diff3 for text merging"""
1440
1721
 
1441
1722
    def dump_file(self, temp_dir, name, tree, file_id):
1442
 
        out_path = pathjoin(temp_dir, name)
 
1723
        out_path = osutils.pathjoin(temp_dir, name)
1443
1724
        out_file = open(out_path, "wb")
1444
1725
        try:
1445
1726
            in_file = tree.get_file(file_id)
1457
1738
        import bzrlib.patch
1458
1739
        temp_dir = osutils.mkdtemp(prefix="bzr-")
1459
1740
        try:
1460
 
            new_file = pathjoin(temp_dir, "new")
 
1741
            new_file = osutils.pathjoin(temp_dir, "new")
1461
1742
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
1462
1743
            base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
1463
1744
            other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
1464
1745
            status = bzrlib.patch.diff3(new_file, this, base, other)
1465
1746
            if status not in (0, 1):
1466
 
                raise BzrError("Unhandled diff3 exit code")
 
1747
                raise errors.BzrError("Unhandled diff3 exit code")
1467
1748
            f = open(new_file, 'rb')
1468
1749
            try:
1469
1750
                self.tt.create_file(f, trans_id)
1478
1759
            osutils.rmtree(temp_dir)
1479
1760
 
1480
1761
 
 
1762
class PathNotInTree(errors.BzrError):
 
1763
 
 
1764
    _fmt = """Merge-into failed because %(tree)s does not contain %(path)s."""
 
1765
 
 
1766
    def __init__(self, path, tree):
 
1767
        errors.BzrError.__init__(self, path=path, tree=tree)
 
1768
 
 
1769
 
 
1770
class MergeIntoMerger(Merger):
 
1771
    """Merger that understands other_tree will be merged into a subdir.
 
1772
 
 
1773
    This also changes the Merger api so that it uses real Branch, revision_id,
 
1774
    and RevisonTree objects, rather than using revision specs.
 
1775
    """
 
1776
 
 
1777
    def __init__(self, this_tree, other_branch, other_tree, target_subdir,
 
1778
            source_subpath, other_rev_id=None):
 
1779
        """Create a new MergeIntoMerger object.
 
1780
 
 
1781
        source_subpath in other_tree will be effectively copied to
 
1782
        target_subdir in this_tree.
 
1783
 
 
1784
        :param this_tree: The tree that we will be merging into.
 
1785
        :param other_branch: The Branch we will be merging from.
 
1786
        :param other_tree: The RevisionTree object we want to merge.
 
1787
        :param target_subdir: The relative path where we want to merge
 
1788
            other_tree into this_tree
 
1789
        :param source_subpath: The relative path specifying the subtree of
 
1790
            other_tree to merge into this_tree.
 
1791
        """
 
1792
        # It is assumed that we are merging a tree that is not in our current
 
1793
        # ancestry, which means we are using the "EmptyTree" as our basis.
 
1794
        null_ancestor_tree = this_tree.branch.repository.revision_tree(
 
1795
                                _mod_revision.NULL_REVISION)
 
1796
        super(MergeIntoMerger, self).__init__(
 
1797
            this_branch=this_tree.branch,
 
1798
            this_tree=this_tree,
 
1799
            other_tree=other_tree,
 
1800
            base_tree=null_ancestor_tree,
 
1801
            )
 
1802
        self._target_subdir = target_subdir
 
1803
        self._source_subpath = source_subpath
 
1804
        self.other_branch = other_branch
 
1805
        if other_rev_id is None:
 
1806
            other_rev_id = other_tree.get_revision_id()
 
1807
        self.other_rev_id = self.other_basis = other_rev_id
 
1808
        self.base_is_ancestor = True
 
1809
        self.backup_files = True
 
1810
        self.merge_type = Merge3Merger
 
1811
        self.show_base = False
 
1812
        self.reprocess = False
 
1813
        self.interesting_ids = None
 
1814
        self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
 
1815
              target_subdir=self._target_subdir,
 
1816
              source_subpath=self._source_subpath)
 
1817
        if self._source_subpath != '':
 
1818
            # If this isn't a partial merge make sure the revisions will be
 
1819
            # present.
 
1820
            self._maybe_fetch(self.other_branch, self.this_branch,
 
1821
                self.other_basis)
 
1822
 
 
1823
    def set_pending(self):
 
1824
        if self._source_subpath != '':
 
1825
            return
 
1826
        Merger.set_pending(self)
 
1827
 
 
1828
 
 
1829
class _MergeTypeParameterizer(object):
 
1830
    """Wrap a merge-type class to provide extra parameters.
 
1831
    
 
1832
    This is hack used by MergeIntoMerger to pass some extra parameters to its
 
1833
    merge_type.  Merger.do_merge() sets up its own set of parameters to pass to
 
1834
    the 'merge_type' member.  It is difficult override do_merge without
 
1835
    re-writing the whole thing, so instead we create a wrapper which will pass
 
1836
    the extra parameters.
 
1837
    """
 
1838
 
 
1839
    def __init__(self, merge_type, **kwargs):
 
1840
        self._extra_kwargs = kwargs
 
1841
        self._merge_type = merge_type
 
1842
 
 
1843
    def __call__(self, *args, **kwargs):
 
1844
        kwargs.update(self._extra_kwargs)
 
1845
        return self._merge_type(*args, **kwargs)
 
1846
 
 
1847
    def __getattr__(self, name):
 
1848
        return getattr(self._merge_type, name)
 
1849
 
 
1850
 
 
1851
class MergeIntoMergeType(Merge3Merger):
 
1852
    """Merger that incorporates a tree (or part of a tree) into another."""
 
1853
 
 
1854
    def __init__(self, *args, **kwargs):
 
1855
        """Initialize the merger object.
 
1856
 
 
1857
        :param args: See Merge3Merger.__init__'s args.
 
1858
        :param kwargs: See Merge3Merger.__init__'s keyword args, except for
 
1859
            source_subpath and target_subdir.
 
1860
        :keyword source_subpath: The relative path specifying the subtree of
 
1861
            other_tree to merge into this_tree.
 
1862
        :keyword target_subdir: The relative path where we want to merge
 
1863
            other_tree into this_tree
 
1864
        """
 
1865
        # All of the interesting work happens during Merge3Merger.__init__(),
 
1866
        # so we have have to hack in to get our extra parameters set.
 
1867
        self._source_subpath = kwargs.pop('source_subpath')
 
1868
        self._target_subdir = kwargs.pop('target_subdir')
 
1869
        super(MergeIntoMergeType, self).__init__(*args, **kwargs)
 
1870
 
 
1871
    def _compute_transform(self):
 
1872
        child_pb = ui.ui_factory.nested_progress_bar()
 
1873
        try:
 
1874
            entries = self._entries_to_incorporate()
 
1875
            entries = list(entries)
 
1876
            for num, (entry, parent_id) in enumerate(entries):
 
1877
                child_pb.update('Preparing file merge', num, len(entries))
 
1878
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1879
                trans_id = transform.new_by_entry(self.tt, entry,
 
1880
                    parent_trans_id, self.other_tree)
 
1881
        finally:
 
1882
            child_pb.finished()
 
1883
        self._finish_computing_transform()
 
1884
 
 
1885
    def _entries_to_incorporate(self):
 
1886
        """Yields pairs of (inventory_entry, new_parent)."""
 
1887
        other_inv = self.other_tree.inventory
 
1888
        subdir_id = other_inv.path2id(self._source_subpath)
 
1889
        if subdir_id is None:
 
1890
            # XXX: The error would be clearer if it gave the URL of the source
 
1891
            # branch, but we don't have a reference to that here.
 
1892
            raise PathNotInTree(self._source_subpath, "Source tree")
 
1893
        subdir = other_inv[subdir_id]
 
1894
        parent_in_target = osutils.dirname(self._target_subdir)
 
1895
        target_id = self.this_tree.inventory.path2id(parent_in_target)
 
1896
        if target_id is None:
 
1897
            raise PathNotInTree(self._target_subdir, "Target tree")
 
1898
        name_in_target = osutils.basename(self._target_subdir)
 
1899
        merge_into_root = subdir.copy()
 
1900
        merge_into_root.name = name_in_target
 
1901
        if merge_into_root.file_id in self.this_tree.inventory:
 
1902
            # Give the root a new file-id.
 
1903
            # This can happen fairly easily if the directory we are
 
1904
            # incorporating is the root, and both trees have 'TREE_ROOT' as
 
1905
            # their root_id.  Users will expect this to Just Work, so we
 
1906
            # change the file-id here.
 
1907
            # Non-root file-ids could potentially conflict too.  That's really
 
1908
            # an edge case, so we don't do anything special for those.  We let
 
1909
            # them cause conflicts.
 
1910
            merge_into_root.file_id = generate_ids.gen_file_id(name_in_target)
 
1911
        yield (merge_into_root, target_id)
 
1912
        if subdir.kind != 'directory':
 
1913
            # No children, so we are done.
 
1914
            return
 
1915
        for ignored_path, entry in other_inv.iter_entries_by_dir(subdir_id):
 
1916
            parent_id = entry.parent_id
 
1917
            if parent_id == subdir.file_id:
 
1918
                # The root's parent ID has changed, so make sure children of
 
1919
                # the root refer to the new ID.
 
1920
                parent_id = merge_into_root.file_id
 
1921
            yield (entry, parent_id)
 
1922
 
 
1923
 
1481
1924
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1482
1925
                backup_files=False,
1483
1926
                merge_type=Merge3Merger,
1487
1930
                other_rev_id=None,
1488
1931
                interesting_files=None,
1489
1932
                this_tree=None,
1490
 
                pb=DummyProgress(),
 
1933
                pb=None,
1491
1934
                change_reporter=None):
1492
1935
    """Primary interface for merging.
1493
1936
 
1494
 
        typical use is probably
1495
 
        'merge_inner(branch, branch.get_revision_tree(other_revision),
1496
 
                     branch.get_revision_tree(base_revision))'
1497
 
        """
 
1937
    Typical use is probably::
 
1938
 
 
1939
        merge_inner(branch, branch.get_revision_tree(other_revision),
 
1940
                    branch.get_revision_tree(base_revision))
 
1941
    """
1498
1942
    if this_tree is None:
1499
 
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
1500
 
            "parameter as of bzrlib version 0.8.")
 
1943
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
 
1944
                              "parameter as of bzrlib version 0.8.")
1501
1945
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1502
1946
                    pb=pb, change_reporter=change_reporter)
1503
1947
    merger.backup_files = backup_files
1516
1960
    get_revision_id = getattr(base_tree, 'get_revision_id', None)
1517
1961
    if get_revision_id is None:
1518
1962
        get_revision_id = base_tree.last_revision
 
1963
    merger.cache_trees_with_revision_ids([other_tree, base_tree, this_tree])
1519
1964
    merger.set_base_revision(get_revision_id(), this_branch)
1520
1965
    return merger.do_merge()
1521
1966
 
1720
2165
        super(_PlanMerge, self).__init__(a_rev, b_rev, vf, key_prefix)
1721
2166
        self.a_key = self._key_prefix + (self.a_rev,)
1722
2167
        self.b_key = self._key_prefix + (self.b_rev,)
1723
 
        self.graph = Graph(self.vf)
 
2168
        self.graph = _mod_graph.Graph(self.vf)
1724
2169
        heads = self.graph.heads((self.a_key, self.b_key))
1725
2170
        if len(heads) == 1:
1726
2171
            # one side dominates, so we can just return its values, yay for
1731
2176
                other = b_rev
1732
2177
            else:
1733
2178
                other = a_rev
1734
 
            mutter('found dominating revision for %s\n%s > %s', self.vf,
1735
 
                   self._head_key[-1], other)
 
2179
            trace.mutter('found dominating revision for %s\n%s > %s', self.vf,
 
2180
                         self._head_key[-1], other)
1736
2181
            self._weave = None
1737
2182
        else:
1738
2183
            self._head_key = None
1752
2197
        while True:
1753
2198
            next_lcas = self.graph.find_lca(*cur_ancestors)
1754
2199
            # Map a plain NULL_REVISION to a simple no-ancestors
1755
 
            if next_lcas == set([NULL_REVISION]):
 
2200
            if next_lcas == set([_mod_revision.NULL_REVISION]):
1756
2201
                next_lcas = ()
1757
2202
            # Order the lca's based on when they were merged into the tip
1758
2203
            # While the actual merge portion of weave merge uses a set() of
1770
2215
            elif len(next_lcas) > 2:
1771
2216
                # More than 2 lca's, fall back to grabbing all nodes between
1772
2217
                # 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)
 
2218
                trace.mutter('More than 2 LCAs, falling back to all nodes for:'
 
2219
                             ' %s, %s\n=> %s',
 
2220
                             self.a_key, self.b_key, cur_ancestors)
1775
2221
                cur_lcas = next_lcas
1776
2222
                while len(cur_lcas) > 1:
1777
2223
                    cur_lcas = self.graph.find_lca(*cur_lcas)
1780
2226
                    unique_lca = None
1781
2227
                else:
1782
2228
                    unique_lca = list(cur_lcas)[0]
1783
 
                    if unique_lca == NULL_REVISION:
 
2229
                    if unique_lca == _mod_revision.NULL_REVISION:
1784
2230
                        # find_lca will return a plain 'NULL_REVISION' rather
1785
2231
                        # than a key tuple when there is no common ancestor, we
1786
2232
                        # prefer to just use None, because it doesn't confuse
1809
2255
            # We remove NULL_REVISION because it isn't a proper tuple key, and
1810
2256
            # thus confuses things like _get_interesting_texts, and our logic
1811
2257
            # to add the texts into the memory weave.
1812
 
            if NULL_REVISION in parent_map:
1813
 
                parent_map.pop(NULL_REVISION)
 
2258
            if _mod_revision.NULL_REVISION in parent_map:
 
2259
                parent_map.pop(_mod_revision.NULL_REVISION)
1814
2260
        else:
1815
2261
            interesting = set()
1816
2262
            for tip in tip_keys:
1968
2414
        lcas = graph.find_lca(key_prefix + (a_rev,), key_prefix + (b_rev,))
1969
2415
        self.lcas = set()
1970
2416
        for lca in lcas:
1971
 
            if lca == NULL_REVISION:
 
2417
            if lca == _mod_revision.NULL_REVISION:
1972
2418
                self.lcas.add(lca)
1973
2419
            else:
1974
2420
                self.lcas.add(lca[-1])