1
# Copyright (C) 2005, 2006 Canonical Ltd
 
 
3
# This program is free software; you can redistribute it and/or modify
 
 
4
# it under the terms of the GNU General Public License as published by
 
 
5
# the Free Software Foundation; either version 2 of the License, or
 
 
6
# (at your option) any later version.
 
 
8
# This program is distributed in the hope that it will be useful,
 
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
11
# GNU General Public License for more details.
 
 
13
# You should have received a copy of the GNU General Public License
 
 
14
# along with this program; if not, write to the Free Software
 
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
27
    revision as _mod_revision,
 
 
29
from bzrlib.branch import Branch
 
 
30
from bzrlib.conflicts import ConflictList, Conflict
 
 
31
from bzrlib.errors import (BzrCommandError,
 
 
41
                           WorkingTreeNotRevision,
 
 
44
from bzrlib.merge3 import Merge3
 
 
45
from bzrlib.osutils import rename, pathjoin
 
 
46
from progress import DummyProgress, ProgressPhase
 
 
47
from bzrlib.revision import (is_ancestor, NULL_REVISION, ensure_null)
 
 
48
from bzrlib.textfile import check_text_lines
 
 
49
from bzrlib.trace import mutter, warning, note
 
 
50
from bzrlib.transform import (TreeTransform, resolve_conflicts, cook_conflicts,
 
 
51
                              conflict_pass, FinalPaths, create_by_entry,
 
 
52
                              unique_add, ROOT_PARENT)
 
 
53
from bzrlib.versionedfile import PlanWeaveMerge
 
 
56
# TODO: Report back as changes are merged in
 
 
59
def transform_tree(from_tree, to_tree, interesting_ids=None):
 
 
60
    merge_inner(from_tree.branch, to_tree, from_tree, ignore_zero=True,
 
 
61
                interesting_ids=interesting_ids, this_tree=from_tree)
 
 
65
    def __init__(self, this_branch, other_tree=None, base_tree=None,
 
 
66
                 this_tree=None, pb=DummyProgress(), change_reporter=None,
 
 
69
        assert this_tree is not None, "this_tree is required"
 
 
70
        self.this_branch = this_branch
 
 
71
        self.this_basis = _mod_revision.ensure_null(
 
 
72
            this_branch.last_revision())
 
 
73
        self.this_rev_id = None
 
 
74
        self.this_tree = this_tree
 
 
75
        self.this_revision_tree = None
 
 
76
        self.this_basis_tree = None
 
 
77
        self.other_tree = other_tree
 
 
78
        self.other_branch = None
 
 
79
        self.base_tree = base_tree
 
 
80
        self.ignore_zero = False
 
 
81
        self.backup_files = False
 
 
82
        self.interesting_ids = None
 
 
83
        self.interesting_files = None
 
 
84
        self.show_base = False
 
 
85
        self.reprocess = False
 
 
88
        self.recurse = recurse
 
 
89
        self.change_reporter = change_reporter
 
 
90
        self._cached_trees = {}
 
 
93
    def from_uncommitted(tree, other_tree, pb):
 
 
94
        """Return a Merger for uncommitted changes in other_tree.
 
 
96
        :param tree: The tree to merge into
 
 
97
        :param other_tree: The tree to get uncommitted changes from
 
 
98
        :param pb: A progress indicator
 
 
100
        merger = Merger(tree.branch, other_tree, other_tree.basis_tree(), tree,
 
 
102
        merger.base_rev_id = merger.base_tree.get_revision_id()
 
 
103
        merger.other_rev_id = None
 
 
107
    def from_mergeable(klass, tree, mergeable, pb):
 
 
108
        """Return a Merger for a bundle or merge directive.
 
 
110
        :param tree: The tree to merge changes into
 
 
111
        :param mergeable: A merge directive or bundle
 
 
112
        :param pb: A progress indicator
 
 
114
        mergeable.install_revisions(tree.branch.repository)
 
 
115
        base_revision_id, other_revision_id, verified =\
 
 
116
            mergeable.get_merge_request(tree.branch.repository)
 
 
117
        if (base_revision_id != _mod_revision.NULL_REVISION and
 
 
118
            tree.branch.repository.get_graph().is_ancestor(
 
 
119
            base_revision_id, tree.branch.last_revision())):
 
 
120
            base_revision_id = None
 
 
121
        merger = klass.from_revision_ids(pb, tree, other_revision_id,
 
 
123
        return merger, verified
 
 
126
    def from_revision_ids(pb, this, other, base=None, other_branch=None,
 
 
128
        """Return a Merger for revision-ids.
 
 
130
        :param tree: The tree to merge changes into
 
 
131
        :param other: The revision-id to use as OTHER
 
 
132
        :param base: The revision-id to use as BASE.  If not specified, will
 
 
134
        :param other_branch: A branch containing the other revision-id.  If
 
 
135
            not supplied, this.branch is used.
 
 
136
        :param base_branch: A branch containing the base revision-id.  If
 
 
137
            not supplied, other_branch or this.branch will be used.
 
 
138
        :param pb: A progress indicator
 
 
140
        merger = Merger(this.branch, this_tree=this, pb=pb)
 
 
141
        if other_branch is None:
 
 
142
            other_branch = this.branch
 
 
143
        merger.set_other_revision(other, other_branch)
 
 
147
            if base_branch is None:
 
 
148
                base_branch = other_branch
 
 
149
            merger.set_base_revision(base, base_branch)
 
 
152
    def revision_tree(self, revision_id, branch=None):
 
 
153
        if revision_id not in self._cached_trees:
 
 
155
                branch = self.this_branch
 
 
157
                tree = self.this_tree.revision_tree(revision_id)
 
 
158
            except errors.NoSuchRevisionInTree:
 
 
159
                tree = branch.repository.revision_tree(revision_id)
 
 
160
            self._cached_trees[revision_id] = tree
 
 
161
        return self._cached_trees[revision_id]
 
 
163
    def _get_tree(self, treespec, possible_transports=None):
 
 
164
        from bzrlib import workingtree
 
 
165
        location, revno = treespec
 
 
167
            tree = workingtree.WorkingTree.open_containing(location)[0]
 
 
168
            return tree.branch, tree
 
 
169
        branch = Branch.open_containing(location, possible_transports)[0]
 
 
171
            revision_id = branch.last_revision()
 
 
173
            revision_id = branch.get_rev_id(revno)
 
 
174
        revision_id = ensure_null(revision_id)
 
 
175
        return branch, self.revision_tree(revision_id, branch)
 
 
177
    def ensure_revision_trees(self):
 
 
178
        if self.this_revision_tree is None:
 
 
179
            self.this_basis_tree = self.revision_tree(self.this_basis)
 
 
180
            if self.this_basis == self.this_rev_id:
 
 
181
                self.this_revision_tree = self.this_basis_tree
 
 
183
        if self.other_rev_id is None:
 
 
184
            other_basis_tree = self.revision_tree(self.other_basis)
 
 
185
            changes = other_basis_tree.changes_from(self.other_tree)
 
 
186
            if changes.has_changed():
 
 
187
                raise WorkingTreeNotRevision(self.this_tree)
 
 
188
            other_rev_id = self.other_basis
 
 
189
            self.other_tree = other_basis_tree
 
 
191
    def file_revisions(self, file_id):
 
 
192
        self.ensure_revision_trees()
 
 
193
        def get_id(tree, file_id):
 
 
194
            revision_id = tree.inventory[file_id].revision
 
 
195
            assert revision_id is not None
 
 
197
        if self.this_rev_id is None:
 
 
198
            if self.this_basis_tree.get_file_sha1(file_id) != \
 
 
199
                self.this_tree.get_file_sha1(file_id):
 
 
200
                raise WorkingTreeNotRevision(self.this_tree)
 
 
202
        trees = (self.this_basis_tree, self.other_tree)
 
 
203
        return [get_id(tree, file_id) for tree in trees]
 
 
205
    def check_basis(self, check_clean, require_commits=True):
 
 
206
        if self.this_basis is None and require_commits is True:
 
 
207
            raise BzrCommandError("This branch has no commits."
 
 
208
                                  " (perhaps you would prefer 'bzr pull')")
 
 
211
            if self.this_basis != self.this_rev_id:
 
 
212
                raise errors.UncommittedChanges(self.this_tree)
 
 
214
    def compare_basis(self):
 
 
216
            basis_tree = self.revision_tree(self.this_tree.last_revision())
 
 
217
        except errors.RevisionNotPresent:
 
 
218
            basis_tree = self.this_tree.basis_tree()
 
 
219
        changes = self.this_tree.changes_from(basis_tree)
 
 
220
        if not changes.has_changed():
 
 
221
            self.this_rev_id = self.this_basis
 
 
223
    def set_interesting_files(self, file_list):
 
 
224
        self.interesting_files = file_list
 
 
226
    def set_pending(self):
 
 
227
        if not self.base_is_ancestor or not self.base_is_other_ancestor or self.other_rev_id is None:
 
 
231
    def _add_parent(self):
 
 
232
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
 
 
233
        new_parent_trees = []
 
 
234
        for revision_id in new_parents:
 
 
236
                tree = self.revision_tree(revision_id)
 
 
237
            except errors.RevisionNotPresent:
 
 
241
            new_parent_trees.append((revision_id, tree))
 
 
243
            self.this_tree.set_parent_trees(new_parent_trees,
 
 
244
                                            allow_leftmost_as_ghost=True)
 
 
246
            for _revision_id, tree in new_parent_trees:
 
 
250
    def set_other(self, other_revision, possible_transports=None):
 
 
251
        """Set the revision and tree to merge from.
 
 
253
        This sets the other_tree, other_rev_id, other_basis attributes.
 
 
255
        :param other_revision: The [path, revision] list to merge from.
 
 
257
        self.other_branch, self.other_tree = self._get_tree(other_revision,
 
 
259
        if other_revision[1] == -1:
 
 
260
            self.other_rev_id = _mod_revision.ensure_null(
 
 
261
                self.other_branch.last_revision())
 
 
262
            if _mod_revision.is_null(self.other_rev_id):
 
 
263
                raise NoCommits(self.other_branch)
 
 
264
            self.other_basis = self.other_rev_id
 
 
265
        elif other_revision[1] is not None:
 
 
266
            self.other_rev_id = self.other_branch.get_rev_id(other_revision[1])
 
 
267
            self.other_basis = self.other_rev_id
 
 
269
            self.other_rev_id = None
 
 
270
            self.other_basis = self.other_branch.last_revision()
 
 
271
            if self.other_basis is None:
 
 
272
                raise NoCommits(self.other_branch)
 
 
273
        if self.other_rev_id is not None:
 
 
274
            self._cached_trees[self.other_rev_id] = self.other_tree
 
 
275
        self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
 
 
277
    def set_other_revision(self, revision_id, other_branch):
 
 
278
        """Set 'other' based on a branch and revision id
 
 
280
        :param revision_id: The revision to use for a tree
 
 
281
        :param other_branch: The branch containing this tree
 
 
283
        self.other_rev_id = revision_id
 
 
284
        self.other_branch = other_branch
 
 
285
        self._maybe_fetch(other_branch, self.this_branch, self.other_rev_id)
 
 
286
        self.other_tree = self.revision_tree(revision_id)
 
 
287
        self.other_basis = revision_id
 
 
289
    def set_base_revision(self, revision_id, branch):
 
 
290
        """Set 'base' based on a branch and revision id
 
 
292
        :param revision_id: The revision to use for a tree
 
 
293
        :param branch: The branch containing this tree
 
 
295
        self.base_rev_id = revision_id
 
 
296
        self.base_branch = branch
 
 
297
        self._maybe_fetch(branch, self.this_branch, revision_id)
 
 
298
        self.base_tree = self.revision_tree(revision_id)
 
 
299
        self.base_is_ancestor = is_ancestor(self.this_basis,
 
 
302
        self.base_is_other_ancestor = is_ancestor(self.other_basis,
 
 
306
    def _maybe_fetch(self, source, target, revision_id):
 
 
307
        if not source.repository.has_same_location(target.repository):
 
 
308
            target.fetch(source, revision_id)
 
 
311
        this_repo = self.this_branch.repository
 
 
312
        graph = this_repo.get_graph()
 
 
313
        revisions = [ensure_null(self.this_basis),
 
 
314
                     ensure_null(self.other_basis)]
 
 
315
        if NULL_REVISION in revisions:
 
 
316
            self.base_rev_id = NULL_REVISION
 
 
318
            self.base_rev_id = graph.find_unique_lca(*revisions)
 
 
319
            if self.base_rev_id == NULL_REVISION:
 
 
320
                raise UnrelatedBranches()
 
 
321
        self.base_tree = self.revision_tree(self.base_rev_id)
 
 
322
        self.base_is_ancestor = True
 
 
323
        self.base_is_other_ancestor = True
 
 
325
    def set_base(self, base_revision):
 
 
326
        """Set the base revision to use for the merge.
 
 
328
        :param base_revision: A 2-list containing a path and revision number.
 
 
330
        mutter("doing merge() with no base_revision specified")
 
 
331
        if base_revision == [None, None]:
 
 
334
            base_branch, self.base_tree = self._get_tree(base_revision)
 
 
335
            if base_revision[1] == -1:
 
 
336
                self.base_rev_id = base_branch.last_revision()
 
 
337
            elif base_revision[1] is None:
 
 
338
                self.base_rev_id = _mod_revision.NULL_REVISION
 
 
340
                self.base_rev_id = _mod_revision.ensure_null(
 
 
341
                    base_branch.get_rev_id(base_revision[1]))
 
 
342
            self._maybe_fetch(base_branch, self.this_branch, self.base_rev_id)
 
 
343
            self.base_is_ancestor = is_ancestor(self.this_basis, 
 
 
346
            self.base_is_other_ancestor = is_ancestor(self.other_basis,
 
 
351
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
 
 
352
                  'other_tree': self.other_tree,
 
 
353
                  'interesting_ids': self.interesting_ids,
 
 
354
                  'interesting_files': self.interesting_files,
 
 
356
        if self.merge_type.requires_base:
 
 
357
            kwargs['base_tree'] = self.base_tree
 
 
358
        if self.merge_type.supports_reprocess:
 
 
359
            kwargs['reprocess'] = self.reprocess
 
 
361
            raise BzrError("Conflict reduction is not supported for merge"
 
 
362
                                  " type %s." % self.merge_type)
 
 
363
        if self.merge_type.supports_show_base:
 
 
364
            kwargs['show_base'] = self.show_base
 
 
366
            raise BzrError("Showing base is not supported for this"
 
 
367
                                  " merge type. %s" % self.merge_type)
 
 
368
        self.this_tree.lock_tree_write()
 
 
369
        if self.base_tree is not None:
 
 
370
            self.base_tree.lock_read()
 
 
371
        if self.other_tree is not None:
 
 
372
            self.other_tree.lock_read()
 
 
374
            merge = self.merge_type(pb=self._pb,
 
 
375
                                    change_reporter=self.change_reporter,
 
 
377
            if self.recurse == 'down':
 
 
378
                for path, file_id in self.this_tree.iter_references():
 
 
379
                    sub_tree = self.this_tree.get_nested_tree(file_id, path)
 
 
380
                    other_revision = self.other_tree.get_reference_revision(
 
 
382
                    if  other_revision == sub_tree.last_revision():
 
 
384
                    sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
 
 
385
                    sub_merge.merge_type = self.merge_type
 
 
386
                    relpath = self.this_tree.relpath(path)
 
 
387
                    other_branch = self.other_branch.reference_parent(file_id, relpath)
 
 
388
                    sub_merge.set_other_revision(other_revision, other_branch)
 
 
389
                    base_revision = self.base_tree.get_reference_revision(file_id)
 
 
390
                    sub_merge.base_tree = \
 
 
391
                        sub_tree.branch.repository.revision_tree(base_revision)
 
 
395
            if self.other_tree is not None:
 
 
396
                self.other_tree.unlock()
 
 
397
            if self.base_tree is not None:
 
 
398
                self.base_tree.unlock()
 
 
399
            self.this_tree.unlock()
 
 
400
        if len(merge.cooked_conflicts) == 0:
 
 
401
            if not self.ignore_zero:
 
 
402
                note("All changes applied successfully.")
 
 
404
            note("%d conflicts encountered." % len(merge.cooked_conflicts))
 
 
406
        return len(merge.cooked_conflicts)
 
 
409
class Merge3Merger(object):
 
 
410
    """Three-way merger that uses the merge3 text merger"""
 
 
412
    supports_reprocess = True
 
 
413
    supports_show_base = True
 
 
414
    history_based = False
 
 
415
    winner_idx = {"this": 2, "other": 1, "conflict": 1}
 
 
417
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
 
 
418
                 interesting_ids=None, reprocess=False, show_base=False,
 
 
419
                 pb=DummyProgress(), pp=None, change_reporter=None,
 
 
420
                 interesting_files=None):
 
 
421
        """Initialize the merger object and perform the merge.
 
 
423
        :param working_tree: The working tree to apply the merge to
 
 
424
        :param this_tree: The local tree in the merge operation
 
 
425
        :param base_tree: The common tree in the merge operation
 
 
426
        :param other_tree: The other other tree to merge changes from
 
 
427
        :param interesting_ids: The file_ids of files that should be
 
 
428
            participate in the merge.  May not be combined with
 
 
430
        :param: reprocess If True, perform conflict-reduction processing.
 
 
431
        :param show_base: If True, show the base revision in text conflicts.
 
 
432
            (incompatible with reprocess)
 
 
433
        :param pb: A Progress bar
 
 
434
        :param pp: A ProgressPhase object
 
 
435
        :param change_reporter: An object that should report changes made
 
 
436
        :param interesting_files: The tree-relative paths of files that should
 
 
437
            participate in the merge.  If these paths refer to directories,
 
 
438
            the contents of those directories will also be included.  May not
 
 
439
            be combined with interesting_ids.  If neither interesting_files nor
 
 
440
            interesting_ids is specified, all files may participate in the
 
 
443
        object.__init__(self)
 
 
444
        if interesting_files is not None:
 
 
445
            assert interesting_ids is None
 
 
446
        self.interesting_ids = interesting_ids
 
 
447
        self.interesting_files = interesting_files
 
 
448
        self.this_tree = working_tree
 
 
449
        self.this_tree.lock_tree_write()
 
 
450
        self.base_tree = base_tree
 
 
451
        self.base_tree.lock_read()
 
 
452
        self.other_tree = other_tree
 
 
453
        self.other_tree.lock_read()
 
 
454
        self._raw_conflicts = []
 
 
455
        self.cooked_conflicts = []
 
 
456
        self.reprocess = reprocess
 
 
457
        self.show_base = show_base
 
 
460
        self.change_reporter = change_reporter
 
 
462
            self.pp = ProgressPhase("Merge phase", 3, self.pb)
 
 
464
        self.tt = TreeTransform(working_tree, self.pb)
 
 
467
            entries = self._entries3()
 
 
468
            child_pb = ui.ui_factory.nested_progress_bar()
 
 
470
                for num, (file_id, changed, parents3, names3,
 
 
471
                          executable3) in enumerate(entries):
 
 
472
                    child_pb.update('Preparing file merge', num, len(entries))
 
 
473
                    self._merge_names(file_id, parents3, names3)
 
 
475
                        file_status = self.merge_contents(file_id)
 
 
477
                        file_status = 'unmodified'
 
 
478
                    self._merge_executable(file_id,
 
 
479
                        executable3, file_status)
 
 
484
            child_pb = ui.ui_factory.nested_progress_bar()
 
 
486
                fs_conflicts = resolve_conflicts(self.tt, child_pb,
 
 
487
                    lambda t, c: conflict_pass(t, c, self.other_tree))
 
 
490
            if change_reporter is not None:
 
 
491
                from bzrlib import delta
 
 
492
                delta.report_changes(self.tt._iter_changes(), change_reporter)
 
 
493
            self.cook_conflicts(fs_conflicts)
 
 
494
            for conflict in self.cooked_conflicts:
 
 
497
            results = self.tt.apply(no_conflicts=True)
 
 
498
            self.write_modified(results)
 
 
500
                working_tree.add_conflicts(self.cooked_conflicts)
 
 
501
            except UnsupportedOperation:
 
 
505
            self.other_tree.unlock()
 
 
506
            self.base_tree.unlock()
 
 
507
            self.this_tree.unlock()
 
 
511
        """Gather data about files modified between three trees.
 
 
513
        Return a list of tuples of file_id, changed, parents3, names3,
 
 
514
        executable3.  changed is a boolean indicating whether the file contents
 
 
515
        or kind were changed.  parents3 is a tuple of parent ids for base,
 
 
516
        other and this.  names3 is a tuple of names for base, other and this.
 
 
517
        executable3 is a tuple of execute-bit values for base, other and this.
 
 
520
        iterator = self.other_tree._iter_changes(self.base_tree,
 
 
521
                include_unchanged=True, specific_files=self.interesting_files,
 
 
522
                extra_trees=[self.this_tree])
 
 
523
        for (file_id, paths, changed, versioned, parents, names, kind,
 
 
524
             executable) in iterator:
 
 
525
            if (self.interesting_ids is not None and
 
 
526
                file_id not in self.interesting_ids):
 
 
528
            if file_id in self.this_tree.inventory:
 
 
529
                entry = self.this_tree.inventory[file_id]
 
 
530
                this_name = entry.name
 
 
531
                this_parent = entry.parent_id
 
 
532
                this_executable = entry.executable
 
 
536
                this_executable = None
 
 
537
            parents3 = parents + (this_parent,)
 
 
538
            names3 = names + (this_name,)
 
 
539
            executable3 = executable + (this_executable,)
 
 
540
            result.append((file_id, changed, parents3, names3, executable3))
 
 
545
            self.tt.final_kind(self.tt.root)
 
 
547
            self.tt.cancel_deletion(self.tt.root)
 
 
548
        if self.tt.final_file_id(self.tt.root) is None:
 
 
549
            self.tt.version_file(self.tt.tree_file_id(self.tt.root), 
 
 
551
        if self.other_tree.inventory.root is None:
 
 
553
        other_root_file_id = self.other_tree.get_root_id()
 
 
554
        other_root = self.tt.trans_id_file_id(other_root_file_id)
 
 
555
        if other_root == self.tt.root:
 
 
558
            self.tt.final_kind(other_root)
 
 
561
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
 
 
562
        self.tt.cancel_creation(other_root)
 
 
563
        self.tt.cancel_versioning(other_root)
 
 
565
    def reparent_children(self, ie, target):
 
 
566
        for thing, child in ie.children.iteritems():
 
 
567
            trans_id = self.tt.trans_id_file_id(child.file_id)
 
 
568
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
 
 
570
    def write_modified(self, results):
 
 
572
        for path in results.modified_paths:
 
 
573
            file_id = self.this_tree.path2id(self.this_tree.relpath(path))
 
 
576
            hash = self.this_tree.get_file_sha1(file_id)
 
 
579
            modified_hashes[file_id] = hash
 
 
580
        self.this_tree.set_merge_modified(modified_hashes)
 
 
583
    def parent(entry, file_id):
 
 
584
        """Determine the parent for a file_id (used as a key method)"""
 
 
587
        return entry.parent_id
 
 
590
    def name(entry, file_id):
 
 
591
        """Determine the name for a file_id (used as a key method)"""
 
 
597
    def contents_sha1(tree, file_id):
 
 
598
        """Determine the sha1 of the file contents (used as a key method)."""
 
 
599
        if file_id not in tree:
 
 
601
        return tree.get_file_sha1(file_id)
 
 
604
    def executable(tree, file_id):
 
 
605
        """Determine the executability of a file-id (used as a key method)."""
 
 
606
        if file_id not in tree:
 
 
608
        if tree.kind(file_id) != "file":
 
 
610
        return tree.is_executable(file_id)
 
 
613
    def kind(tree, file_id):
 
 
614
        """Determine the kind of a file-id (used as a key method)."""
 
 
615
        if file_id not in tree:
 
 
617
        return tree.kind(file_id)
 
 
620
    def _three_way(base, other, this):
 
 
621
        #if base == other, either they all agree, or only THIS has changed.
 
 
624
        elif this not in (base, other):
 
 
626
        # "Ambiguous clean merge" -- both sides have made the same change.
 
 
629
        # this == base: only other has changed.
 
 
634
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
 
 
635
        """Do a three-way test on a scalar.
 
 
636
        Return "this", "other" or "conflict", depending whether a value wins.
 
 
638
        key_base = key(base_tree, file_id)
 
 
639
        key_other = key(other_tree, file_id)
 
 
640
        #if base == other, either they all agree, or only THIS has changed.
 
 
641
        if key_base == key_other:
 
 
643
        key_this = key(this_tree, file_id)
 
 
644
        if key_this not in (key_base, key_other):
 
 
646
        # "Ambiguous clean merge"
 
 
647
        elif key_this == key_other:
 
 
650
            assert key_this == key_base
 
 
653
    def merge_names(self, file_id):
 
 
655
            if file_id in tree.inventory:
 
 
656
                return tree.inventory[file_id]
 
 
659
        this_entry = get_entry(self.this_tree)
 
 
660
        other_entry = get_entry(self.other_tree)
 
 
661
        base_entry = get_entry(self.base_tree)
 
 
662
        entries = (base_entry, other_entry, this_entry)
 
 
665
        for entry in entries:
 
 
670
                names.append(entry.name)
 
 
671
                parents.append(entry.parent_id)
 
 
672
        return self._merge_names(file_id, parents, names)
 
 
674
    def _merge_names(self, file_id, parents, names):
 
 
675
        """Perform a merge on file_id names and parents"""
 
 
676
        base_name, other_name, this_name = names
 
 
677
        base_parent, other_parent, this_parent = parents
 
 
679
        name_winner = self._three_way(*names)
 
 
681
        parent_id_winner = self._three_way(*parents)
 
 
682
        if this_name is None:
 
 
683
            if name_winner == "this":
 
 
684
                name_winner = "other"
 
 
685
            if parent_id_winner == "this":
 
 
686
                parent_id_winner = "other"
 
 
687
        if name_winner == "this" and parent_id_winner == "this":
 
 
689
        if name_winner == "conflict":
 
 
690
            trans_id = self.tt.trans_id_file_id(file_id)
 
 
691
            self._raw_conflicts.append(('name conflict', trans_id, 
 
 
692
                                        this_name, other_name))
 
 
693
        if parent_id_winner == "conflict":
 
 
694
            trans_id = self.tt.trans_id_file_id(file_id)
 
 
695
            self._raw_conflicts.append(('parent conflict', trans_id, 
 
 
696
                                        this_parent, other_parent))
 
 
697
        if other_name is None:
 
 
698
            # it doesn't matter whether the result was 'other' or 
 
 
699
            # 'conflict'-- if there's no 'other', we leave it alone.
 
 
701
        # if we get here, name_winner and parent_winner are set to safe values.
 
 
702
        trans_id = self.tt.trans_id_file_id(file_id)
 
 
703
        parent_id = parents[self.winner_idx[parent_id_winner]]
 
 
704
        if parent_id is not None:
 
 
705
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
 
706
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
 
 
707
                                parent_trans_id, trans_id)
 
 
709
    def merge_contents(self, file_id):
 
 
710
        """Performa a merge on file_id contents."""
 
 
711
        def contents_pair(tree):
 
 
712
            if file_id not in tree:
 
 
714
            kind = tree.kind(file_id)
 
 
716
                contents = tree.get_file_sha1(file_id)
 
 
717
            elif kind == "symlink":
 
 
718
                contents = tree.get_symlink_target(file_id)
 
 
721
            return kind, contents
 
 
723
        def contents_conflict():
 
 
724
            trans_id = self.tt.trans_id_file_id(file_id)
 
 
725
            name = self.tt.final_name(trans_id)
 
 
726
            parent_id = self.tt.final_parent(trans_id)
 
 
727
            if file_id in self.this_tree.inventory:
 
 
728
                self.tt.unversion_file(trans_id)
 
 
729
                if file_id in self.this_tree:
 
 
730
                    self.tt.delete_contents(trans_id)
 
 
731
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
 
733
            self._raw_conflicts.append(('contents conflict', file_group))
 
 
735
        # See SPOT run.  run, SPOT, run.
 
 
736
        # So we're not QUITE repeating ourselves; we do tricky things with
 
 
738
        base_pair = contents_pair(self.base_tree)
 
 
739
        other_pair = contents_pair(self.other_tree)
 
 
740
        if base_pair == other_pair:
 
 
741
            # OTHER introduced no changes
 
 
743
        this_pair = contents_pair(self.this_tree)
 
 
744
        if this_pair == other_pair:
 
 
745
            # THIS and OTHER introduced the same changes
 
 
748
            trans_id = self.tt.trans_id_file_id(file_id)
 
 
749
            if this_pair == base_pair:
 
 
750
                # only OTHER introduced changes
 
 
751
                if file_id in self.this_tree:
 
 
752
                    # Remove any existing contents
 
 
753
                    self.tt.delete_contents(trans_id)
 
 
754
                if file_id in self.other_tree:
 
 
755
                    # OTHER changed the file
 
 
756
                    create_by_entry(self.tt, 
 
 
757
                                    self.other_tree.inventory[file_id], 
 
 
758
                                    self.other_tree, trans_id)
 
 
759
                    if file_id not in self.this_tree.inventory:
 
 
760
                        self.tt.version_file(file_id, trans_id)
 
 
762
                elif file_id in self.this_tree.inventory:
 
 
763
                    # OTHER deleted the file
 
 
764
                    self.tt.unversion_file(trans_id)
 
 
766
            #BOTH THIS and OTHER introduced changes; scalar conflict
 
 
767
            elif this_pair[0] == "file" and other_pair[0] == "file":
 
 
768
                # THIS and OTHER are both files, so text merge.  Either
 
 
769
                # BASE is a file, or both converted to files, so at least we
 
 
770
                # have agreement that output should be a file.
 
 
772
                    self.text_merge(file_id, trans_id)
 
 
774
                    return contents_conflict()
 
 
775
                if file_id not in self.this_tree.inventory:
 
 
776
                    self.tt.version_file(file_id, trans_id)
 
 
778
                    self.tt.tree_kind(trans_id)
 
 
779
                    self.tt.delete_contents(trans_id)
 
 
784
                # Scalar conflict, can't text merge.  Dump conflicts
 
 
785
                return contents_conflict()
 
 
787
    def get_lines(self, tree, file_id):
 
 
788
        """Return the lines in a file, or an empty list."""
 
 
790
            return tree.get_file(file_id).readlines()
 
 
794
    def text_merge(self, file_id, trans_id):
 
 
795
        """Perform a three-way text merge on a file_id"""
 
 
796
        # it's possible that we got here with base as a different type.
 
 
797
        # if so, we just want two-way text conflicts.
 
 
798
        if file_id in self.base_tree and \
 
 
799
            self.base_tree.kind(file_id) == "file":
 
 
800
            base_lines = self.get_lines(self.base_tree, file_id)
 
 
803
        other_lines = self.get_lines(self.other_tree, file_id)
 
 
804
        this_lines = self.get_lines(self.this_tree, file_id)
 
 
805
        m3 = Merge3(base_lines, this_lines, other_lines)
 
 
806
        start_marker = "!START OF MERGE CONFLICT!" + "I HOPE THIS IS UNIQUE"
 
 
807
        if self.show_base is True:
 
 
808
            base_marker = '|' * 7
 
 
812
        def iter_merge3(retval):
 
 
813
            retval["text_conflicts"] = False
 
 
814
            for line in m3.merge_lines(name_a = "TREE", 
 
 
815
                                       name_b = "MERGE-SOURCE", 
 
 
816
                                       name_base = "BASE-REVISION",
 
 
817
                                       start_marker=start_marker, 
 
 
818
                                       base_marker=base_marker,
 
 
819
                                       reprocess=self.reprocess):
 
 
820
                if line.startswith(start_marker):
 
 
821
                    retval["text_conflicts"] = True
 
 
822
                    yield line.replace(start_marker, '<' * 7)
 
 
826
        merge3_iterator = iter_merge3(retval)
 
 
827
        self.tt.create_file(merge3_iterator, trans_id)
 
 
828
        if retval["text_conflicts"] is True:
 
 
829
            self._raw_conflicts.append(('text conflict', trans_id))
 
 
830
            name = self.tt.final_name(trans_id)
 
 
831
            parent_id = self.tt.final_parent(trans_id)
 
 
832
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
 
833
                                              this_lines, base_lines,
 
 
835
            file_group.append(trans_id)
 
 
837
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None, 
 
 
838
                        base_lines=None, other_lines=None, set_version=False,
 
 
840
        """Emit conflict files.
 
 
841
        If this_lines, base_lines, or other_lines are omitted, they will be
 
 
842
        determined automatically.  If set_version is true, the .OTHER, .THIS
 
 
843
        or .BASE (in that order) will be created as versioned files.
 
 
845
        data = [('OTHER', self.other_tree, other_lines), 
 
 
846
                ('THIS', self.this_tree, this_lines)]
 
 
848
            data.append(('BASE', self.base_tree, base_lines))
 
 
851
        for suffix, tree, lines in data:
 
 
853
                trans_id = self._conflict_file(name, parent_id, tree, file_id,
 
 
855
                file_group.append(trans_id)
 
 
856
                if set_version and not versioned:
 
 
857
                    self.tt.version_file(file_id, trans_id)
 
 
861
    def _conflict_file(self, name, parent_id, tree, file_id, suffix, 
 
 
863
        """Emit a single conflict file."""
 
 
864
        name = name + '.' + suffix
 
 
865
        trans_id = self.tt.create_path(name, parent_id)
 
 
866
        entry = tree.inventory[file_id]
 
 
867
        create_by_entry(self.tt, entry, tree, trans_id, lines)
 
 
870
    def merge_executable(self, file_id, file_status):
 
 
871
        """Perform a merge on the execute bit."""
 
 
872
        executable = [self.executable(t, file_id) for t in (self.base_tree,
 
 
873
                      self.other_tree, self.this_tree)]
 
 
874
        self._merge_executable(file_id, executable, file_status)
 
 
876
    def _merge_executable(self, file_id, executable, file_status):
 
 
877
        """Perform a merge on the execute bit."""
 
 
878
        base_executable, other_executable, this_executable = executable
 
 
879
        if file_status == "deleted":
 
 
881
        trans_id = self.tt.trans_id_file_id(file_id)
 
 
883
            if self.tt.final_kind(trans_id) != "file":
 
 
887
        winner = self._three_way(*executable)
 
 
888
        if winner == "conflict":
 
 
889
        # There must be a None in here, if we have a conflict, but we
 
 
890
        # need executability since file status was not deleted.
 
 
891
            if self.executable(self.other_tree, file_id) is None:
 
 
896
            if file_status == "modified":
 
 
897
                executability = this_executable
 
 
898
                if executability is not None:
 
 
899
                    trans_id = self.tt.trans_id_file_id(file_id)
 
 
900
                    self.tt.set_executability(executability, trans_id)
 
 
902
            assert winner == "other"
 
 
903
            if file_id in self.other_tree:
 
 
904
                executability = other_executable
 
 
905
            elif file_id in self.this_tree:
 
 
906
                executability = this_executable
 
 
907
            elif file_id in self.base_tree:
 
 
908
                executability = base_executable
 
 
909
            if executability is not None:
 
 
910
                trans_id = self.tt.trans_id_file_id(file_id)
 
 
911
                self.tt.set_executability(executability, trans_id)
 
 
913
    def cook_conflicts(self, fs_conflicts):
 
 
914
        """Convert all conflicts into a form that doesn't depend on trans_id"""
 
 
915
        from conflicts import Conflict
 
 
917
        self.cooked_conflicts.extend(cook_conflicts(fs_conflicts, self.tt))
 
 
918
        fp = FinalPaths(self.tt)
 
 
919
        for conflict in self._raw_conflicts:
 
 
920
            conflict_type = conflict[0]
 
 
921
            if conflict_type in ('name conflict', 'parent conflict'):
 
 
922
                trans_id = conflict[1]
 
 
923
                conflict_args = conflict[2:]
 
 
924
                if trans_id not in name_conflicts:
 
 
925
                    name_conflicts[trans_id] = {}
 
 
926
                unique_add(name_conflicts[trans_id], conflict_type, 
 
 
928
            if conflict_type == 'contents conflict':
 
 
929
                for trans_id in conflict[1]:
 
 
930
                    file_id = self.tt.final_file_id(trans_id)
 
 
931
                    if file_id is not None:
 
 
933
                path = fp.get_path(trans_id)
 
 
934
                for suffix in ('.BASE', '.THIS', '.OTHER'):
 
 
935
                    if path.endswith(suffix):
 
 
936
                        path = path[:-len(suffix)]
 
 
938
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
 
939
                self.cooked_conflicts.append(c)
 
 
940
            if conflict_type == 'text conflict':
 
 
941
                trans_id = conflict[1]
 
 
942
                path = fp.get_path(trans_id)
 
 
943
                file_id = self.tt.final_file_id(trans_id)
 
 
944
                c = Conflict.factory(conflict_type, path=path, file_id=file_id)
 
 
945
                self.cooked_conflicts.append(c)
 
 
947
        for trans_id, conflicts in name_conflicts.iteritems():
 
 
949
                this_parent, other_parent = conflicts['parent conflict']
 
 
950
                assert this_parent != other_parent
 
 
952
                this_parent = other_parent = \
 
 
953
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
 
 
955
                this_name, other_name = conflicts['name conflict']
 
 
956
                assert this_name != other_name
 
 
958
                this_name = other_name = self.tt.final_name(trans_id)
 
 
959
            other_path = fp.get_path(trans_id)
 
 
960
            if this_parent is not None and this_name is not None:
 
 
962
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
 
 
963
                this_path = pathjoin(this_parent_path, this_name)
 
 
965
                this_path = "<deleted>"
 
 
966
            file_id = self.tt.final_file_id(trans_id)
 
 
967
            c = Conflict.factory('path conflict', path=this_path,
 
 
968
                                 conflict_path=other_path, file_id=file_id)
 
 
969
            self.cooked_conflicts.append(c)
 
 
970
        self.cooked_conflicts.sort(key=Conflict.sort_key)
 
 
973
class WeaveMerger(Merge3Merger):
 
 
974
    """Three-way tree merger, text weave merger."""
 
 
975
    supports_reprocess = True
 
 
976
    supports_show_base = False
 
 
978
    def __init__(self, working_tree, this_tree, base_tree, other_tree, 
 
 
979
                 interesting_ids=None, pb=DummyProgress(), pp=None,
 
 
980
                 reprocess=False, change_reporter=None,
 
 
981
                 interesting_files=None):
 
 
982
        super(WeaveMerger, self).__init__(working_tree, this_tree, 
 
 
983
                                          base_tree, other_tree, 
 
 
984
                                          interesting_ids=interesting_ids, 
 
 
985
                                          pb=pb, pp=pp, reprocess=reprocess,
 
 
986
                                          change_reporter=change_reporter)
 
 
988
    def _merged_lines(self, file_id):
 
 
989
        """Generate the merged lines.
 
 
990
        There is no distinction between lines that are meant to contain <<<<<<<
 
 
993
        plan = self.this_tree.plan_file_merge(file_id, self.other_tree)
 
 
994
        textmerge = PlanWeaveMerge(plan, '<<<<<<< TREE\n',
 
 
995
            '>>>>>>> MERGE-SOURCE\n')
 
 
996
        return textmerge.merge_lines(self.reprocess)
 
 
998
    def text_merge(self, file_id, trans_id):
 
 
999
        """Perform a (weave) text merge for a given file and file-id.
 
 
1000
        If conflicts are encountered, .THIS and .OTHER files will be emitted,
 
 
1001
        and a conflict will be noted.
 
 
1003
        lines, conflicts = self._merged_lines(file_id)
 
 
1005
        # Note we're checking whether the OUTPUT is binary in this case, 
 
 
1006
        # because we don't want to get into weave merge guts.
 
 
1007
        check_text_lines(lines)
 
 
1008
        self.tt.create_file(lines, trans_id)
 
 
1010
            self._raw_conflicts.append(('text conflict', trans_id))
 
 
1011
            name = self.tt.final_name(trans_id)
 
 
1012
            parent_id = self.tt.final_parent(trans_id)
 
 
1013
            file_group = self._dump_conflicts(name, parent_id, file_id, 
 
 
1015
            file_group.append(trans_id)
 
 
1018
class Diff3Merger(Merge3Merger):
 
 
1019
    """Three-way merger using external diff3 for text merging"""
 
 
1021
    def dump_file(self, temp_dir, name, tree, file_id):
 
 
1022
        out_path = pathjoin(temp_dir, name)
 
 
1023
        out_file = open(out_path, "wb")
 
 
1025
            in_file = tree.get_file(file_id)
 
 
1026
            for line in in_file:
 
 
1027
                out_file.write(line)
 
 
1032
    def text_merge(self, file_id, trans_id):
 
 
1033
        """Perform a diff3 merge using a specified file-id and trans-id.
 
 
1034
        If conflicts are encountered, .BASE, .THIS. and .OTHER conflict files
 
 
1035
        will be dumped, and a will be conflict noted.
 
 
1038
        temp_dir = osutils.mkdtemp(prefix="bzr-")
 
 
1040
            new_file = pathjoin(temp_dir, "new")
 
 
1041
            this = self.dump_file(temp_dir, "this", self.this_tree, file_id)
 
 
1042
            base = self.dump_file(temp_dir, "base", self.base_tree, file_id)
 
 
1043
            other = self.dump_file(temp_dir, "other", self.other_tree, file_id)
 
 
1044
            status = bzrlib.patch.diff3(new_file, this, base, other)
 
 
1045
            if status not in (0, 1):
 
 
1046
                raise BzrError("Unhandled diff3 exit code")
 
 
1047
            f = open(new_file, 'rb')
 
 
1049
                self.tt.create_file(f, trans_id)
 
 
1053
                name = self.tt.final_name(trans_id)
 
 
1054
                parent_id = self.tt.final_parent(trans_id)
 
 
1055
                self._dump_conflicts(name, parent_id, file_id)
 
 
1056
                self._raw_conflicts.append(('text conflict', trans_id))
 
 
1058
            osutils.rmtree(temp_dir)
 
 
1061
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
 
 
1063
                merge_type=Merge3Merger,
 
 
1064
                interesting_ids=None,
 
 
1068
                interesting_files=None,
 
 
1071
                change_reporter=None):
 
 
1072
    """Primary interface for merging. 
 
 
1074
        typical use is probably 
 
 
1075
        'merge_inner(branch, branch.get_revision_tree(other_revision),
 
 
1076
                     branch.get_revision_tree(base_revision))'
 
 
1078
    if this_tree is None:
 
 
1079
        raise BzrError("bzrlib.merge.merge_inner requires a this_tree "
 
 
1080
            "parameter as of bzrlib version 0.8.")
 
 
1081
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
 
 
1082
                    pb=pb, change_reporter=change_reporter)
 
 
1083
    merger.backup_files = backup_files
 
 
1084
    merger.merge_type = merge_type
 
 
1085
    merger.interesting_ids = interesting_ids
 
 
1086
    merger.ignore_zero = ignore_zero
 
 
1087
    if interesting_files:
 
 
1088
        assert not interesting_ids, ('Only supply interesting_ids'
 
 
1089
                                     ' or interesting_files')
 
 
1090
        merger.interesting_files = interesting_files
 
 
1091
    merger.show_base = show_base
 
 
1092
    merger.reprocess = reprocess
 
 
1093
    merger.other_rev_id = other_rev_id
 
 
1094
    merger.other_basis = other_rev_id
 
 
1095
    return merger.do_merge()
 
 
1097
def get_merge_type_registry():
 
 
1098
    """Merge type registry is in bzrlib.option to avoid circular imports.
 
 
1100
    This method provides a sanctioned way to retrieve it.
 
 
1102
    from bzrlib import option
 
 
1103
    return option._merge_type_registry
 
 
1106
def _plan_annotate_merge(annotated_a, annotated_b, ancestors_a, ancestors_b):
 
 
1107
    def status_a(revision, text):
 
 
1108
        if revision in ancestors_b:
 
 
1109
            return 'killed-b', text
 
 
1111
            return 'new-a', text
 
 
1113
    def status_b(revision, text):
 
 
1114
        if revision in ancestors_a:
 
 
1115
            return 'killed-a', text
 
 
1117
            return 'new-b', text
 
 
1119
    plain_a = [t for (a, t) in annotated_a]
 
 
1120
    plain_b = [t for (a, t) in annotated_b]
 
 
1121
    matcher = patiencediff.PatienceSequenceMatcher(None, plain_a, plain_b)
 
 
1122
    blocks = matcher.get_matching_blocks()
 
 
1125
    for ai, bi, l in blocks:
 
 
1126
        # process all mismatched sections
 
 
1127
        # (last mismatched section is handled because blocks always
 
 
1128
        # includes a 0-length last block)
 
 
1129
        for revision, text in annotated_a[a_cur:ai]:
 
 
1130
            yield status_a(revision, text)
 
 
1131
        for revision, text in annotated_b[b_cur:bi]:
 
 
1132
            yield status_b(revision, text)
 
 
1134
        # and now the matched section
 
 
1137
        for text_a, text_b in zip(plain_a[ai:a_cur], plain_b[bi:b_cur]):
 
 
1138
            assert text_a == text_b
 
 
1139
            yield "unchanged", text_a