~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Launchpad Translations on behalf of bzr-core
  • Date: 2012-02-22 06:50:36 UTC
  • mto: (6437.35.1 2.5.0-dev)
  • mto: This revision was merged to the branch mainline in revision 6475.
  • Revision ID: launchpad_translations_on_behalf_of_bzr-core-20120222065036-ssi1nphovuqv8ou2
Launchpad automatic translations update.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 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
from __future__ import absolute_import
 
18
 
17
19
import warnings
18
20
 
 
21
from bzrlib.lazy_import import lazy_import
 
22
lazy_import(globals(), """
19
23
from bzrlib import (
20
24
    branch as _mod_branch,
 
25
    cleanup,
21
26
    conflicts as _mod_conflicts,
22
27
    debug,
23
 
    decorators,
24
 
    errors,
 
28
    generate_ids,
25
29
    graph as _mod_graph,
26
 
    hooks,
27
30
    merge3,
28
31
    osutils,
29
32
    patiencediff,
34
37
    tree as _mod_tree,
35
38
    tsort,
36
39
    ui,
37
 
    versionedfile
38
 
    )
39
 
from bzrlib.cleanup import OperationWithCleanups
 
40
    versionedfile,
 
41
    workingtree,
 
42
    )
 
43
from bzrlib.i18n import gettext
 
44
""")
 
45
from bzrlib import (
 
46
    decorators,
 
47
    errors,
 
48
    hooks,
 
49
    registry,
 
50
    )
40
51
from bzrlib.symbol_versioning import (
41
52
    deprecated_in,
42
53
    deprecated_method,
46
57
 
47
58
def transform_tree(from_tree, to_tree, interesting_ids=None):
48
59
    from_tree.lock_tree_write()
49
 
    operation = OperationWithCleanups(merge_inner)
 
60
    operation = cleanup.OperationWithCleanups(merge_inner)
50
61
    operation.add_cleanup(from_tree.unlock)
51
62
    operation.run_simple(from_tree.branch, to_tree, from_tree,
52
63
        ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
55
66
class MergeHooks(hooks.Hooks):
56
67
 
57
68
    def __init__(self):
58
 
        hooks.Hooks.__init__(self)
59
 
        self.create_hook(hooks.HookPoint('merge_file_content',
 
69
        hooks.Hooks.__init__(self, "bzrlib.merge", "Merger.hooks")
 
70
        self.add_hook('merge_file_content',
60
71
            "Called with a bzrlib.merge.Merger object to create a per file "
61
72
            "merge object when starting a merge. "
62
73
            "Should return either None or a subclass of "
66
77
            "side has deleted the file and the other has changed it). "
67
78
            "See the AbstractPerFileMerger API docs for details on how it is "
68
79
            "used by merge.",
69
 
            (2, 1), None))
 
80
            (2, 1))
 
81
        self.add_hook('pre_merge',
 
82
            'Called before a merge. '
 
83
            'Receives a Merger object as the single argument.',
 
84
            (2, 5))
 
85
        self.add_hook('post_merge',
 
86
            'Called after a merge. '
 
87
            'Receives a Merger object as the single argument. '
 
88
            'The return value is ignored.',
 
89
            (2, 5))
70
90
 
71
91
 
72
92
class AbstractPerFileMerger(object):
84
104
    def merge_contents(self, merge_params):
85
105
        """Attempt to merge the contents of a single file.
86
106
        
87
 
        :param merge_params: A bzrlib.merge.MergeHookParams
88
 
        :return : A tuple of (status, chunks), where status is one of
 
107
        :param merge_params: A bzrlib.merge.MergeFileHookParams
 
108
        :return: A tuple of (status, chunks), where status is one of
89
109
            'not_applicable', 'success', 'conflicted', or 'delete'.  If status
90
110
            is 'success' or 'conflicted', then chunks should be an iterable of
91
111
            strings for the new file contents.
93
113
        return ('not applicable', None)
94
114
 
95
115
 
96
 
class ConfigurableFileMerger(AbstractPerFileMerger):
 
116
class PerFileMerger(AbstractPerFileMerger):
 
117
    """Merge individual files when self.file_matches returns True.
 
118
 
 
119
    This class is intended to be subclassed.  The file_matches and
 
120
    merge_matching methods should be overridden with concrete implementations.
 
121
    """
 
122
 
 
123
    def file_matches(self, params):
 
124
        """Return True if merge_matching should be called on this file.
 
125
 
 
126
        Only called with merges of plain files with no clear winner.
 
127
 
 
128
        Subclasses must override this.
 
129
        """
 
130
        raise NotImplementedError(self.file_matches)
 
131
 
 
132
    def get_filename(self, params, tree):
 
133
        """Lookup the filename (i.e. basename, not path), given a Tree (e.g.
 
134
        self.merger.this_tree) and a MergeFileHookParams.
 
135
        """
 
136
        return osutils.basename(tree.id2path(params.file_id))
 
137
 
 
138
    def get_filepath(self, params, tree):
 
139
        """Calculate the path to the file in a tree.
 
140
 
 
141
        :param params: A MergeFileHookParams describing the file to merge
 
142
        :param tree: a Tree, e.g. self.merger.this_tree.
 
143
        """
 
144
        return tree.id2path(params.file_id)
 
145
 
 
146
    def merge_contents(self, params):
 
147
        """Merge the contents of a single file."""
 
148
        # Check whether this custom merge logic should be used.
 
149
        if (
 
150
            # OTHER is a straight winner, rely on default merge.
 
151
            params.winner == 'other' or
 
152
            # THIS and OTHER aren't both files.
 
153
            not params.is_file_merge() or
 
154
            # The filename doesn't match
 
155
            not self.file_matches(params)):
 
156
            return 'not_applicable', None
 
157
        return self.merge_matching(params)
 
158
 
 
159
    def merge_matching(self, params):
 
160
        """Merge the contents of a single file that has matched the criteria
 
161
        in PerFileMerger.merge_contents (is a conflict, is a file,
 
162
        self.file_matches is True).
 
163
 
 
164
        Subclasses must override this.
 
165
        """
 
166
        raise NotImplementedError(self.merge_matching)
 
167
 
 
168
 
 
169
class ConfigurableFileMerger(PerFileMerger):
97
170
    """Merge individual files when configured via a .conf file.
98
171
 
99
172
    This is a base class for concrete custom file merging logic. Concrete
122
195
        if self.name_prefix is None:
123
196
            raise ValueError("name_prefix must be set.")
124
197
 
125
 
    def filename_matches_config(self, params):
 
198
    def file_matches(self, params):
126
199
        """Check whether the file should call the merge hook.
127
200
 
128
201
        <name_prefix>_merge_files configuration variable is a list of files
142
215
                affected_files = self.default_files
143
216
            self.affected_files = affected_files
144
217
        if affected_files:
145
 
            filename = self.merger.this_tree.id2path(params.file_id)
146
 
            if filename in affected_files:
 
218
            filepath = self.get_filepath(params, self.merger.this_tree)
 
219
            if filepath in affected_files:
147
220
                return True
148
221
        return False
149
222
 
150
 
    def merge_contents(self, params):
151
 
        """Merge the contents of a single file."""
152
 
        # First, check whether this custom merge logic should be used.  We
153
 
        # expect most files should not be merged by this handler.
154
 
        if (
155
 
            # OTHER is a straight winner, rely on default merge.
156
 
            params.winner == 'other' or
157
 
            # THIS and OTHER aren't both files.
158
 
            not params.is_file_merge() or
159
 
            # The filename isn't listed in the 'NAME_merge_files' config
160
 
            # option.
161
 
            not self.filename_matches_config(params)):
162
 
            return 'not_applicable', None
 
223
    def merge_matching(self, params):
163
224
        return self.merge_text(params)
164
225
 
165
226
    def merge_text(self, params):
172
233
        raise NotImplementedError(self.merge_text)
173
234
 
174
235
 
175
 
class MergeHookParams(object):
 
236
class MergeFileHookParams(object):
176
237
    """Object holding parameters passed to merge_file_content hooks.
177
238
 
178
239
    There are some fields hooks can access:
380
441
        return self._cached_trees[revision_id]
381
442
 
382
443
    def _get_tree(self, treespec, possible_transports=None):
383
 
        from bzrlib import workingtree
384
444
        location, revno = treespec
385
445
        if revno is None:
386
446
            tree = workingtree.WorkingTree.open_containing(location)[0]
394
454
        revision_id = _mod_revision.ensure_null(revision_id)
395
455
        return branch, self.revision_tree(revision_id, branch)
396
456
 
397
 
    @deprecated_method(deprecated_in((2, 1, 0)))
398
 
    def ensure_revision_trees(self):
399
 
        if self.this_revision_tree is None:
400
 
            self.this_basis_tree = self.revision_tree(self.this_basis)
401
 
            if self.this_basis == self.this_rev_id:
402
 
                self.this_revision_tree = self.this_basis_tree
403
 
 
404
 
        if self.other_rev_id is None:
405
 
            other_basis_tree = self.revision_tree(self.other_basis)
406
 
            if other_basis_tree.has_changes(self.other_tree):
407
 
                raise errors.WorkingTreeNotRevision(self.this_tree)
408
 
            other_rev_id = self.other_basis
409
 
            self.other_tree = other_basis_tree
410
 
 
411
 
    @deprecated_method(deprecated_in((2, 1, 0)))
412
 
    def file_revisions(self, file_id):
413
 
        self.ensure_revision_trees()
414
 
        def get_id(tree, file_id):
415
 
            revision_id = tree.inventory[file_id].revision
416
 
            return revision_id
417
 
        if self.this_rev_id is None:
418
 
            if self.this_basis_tree.get_file_sha1(file_id) != \
419
 
                self.this_tree.get_file_sha1(file_id):
420
 
                raise errors.WorkingTreeNotRevision(self.this_tree)
421
 
 
422
 
        trees = (self.this_basis_tree, self.other_tree)
423
 
        return [get_id(tree, file_id) for tree in trees]
424
 
 
425
 
    @deprecated_method(deprecated_in((2, 1, 0)))
426
 
    def check_basis(self, check_clean, require_commits=True):
427
 
        if self.this_basis is None and require_commits is True:
428
 
            raise errors.BzrCommandError(
429
 
                "This branch has no commits."
430
 
                " (perhaps you would prefer 'bzr pull')")
431
 
        if check_clean:
432
 
            self.compare_basis()
433
 
            if self.this_basis != self.this_rev_id:
434
 
                raise errors.UncommittedChanges(self.this_tree)
435
 
 
436
 
    @deprecated_method(deprecated_in((2, 1, 0)))
437
 
    def compare_basis(self):
438
 
        try:
439
 
            basis_tree = self.revision_tree(self.this_tree.last_revision())
440
 
        except errors.NoSuchRevision:
441
 
            basis_tree = self.this_tree.basis_tree()
442
 
        if not self.this_tree.has_changes(basis_tree):
443
 
            self.this_rev_id = self.this_basis
444
 
 
445
457
    def set_interesting_files(self, file_list):
446
458
        self.interesting_files = file_list
447
459
 
454
466
    def _add_parent(self):
455
467
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
456
468
        new_parent_trees = []
457
 
        operation = OperationWithCleanups(self.this_tree.set_parent_trees)
 
469
        operation = cleanup.OperationWithCleanups(
 
470
            self.this_tree.set_parent_trees)
458
471
        for revision_id in new_parents:
459
472
            try:
460
473
                tree = self.revision_tree(revision_id)
491
504
                raise errors.NoCommits(self.other_branch)
492
505
        if self.other_rev_id is not None:
493
506
            self._cached_trees[self.other_rev_id] = self.other_tree
494
 
        self._maybe_fetch(self.other_branch,self.this_branch, self.other_basis)
 
507
        self._maybe_fetch(self.other_branch, self.this_branch, self.other_basis)
495
508
 
496
509
    def set_other_revision(self, revision_id, other_branch):
497
510
        """Set 'other' based on a branch and revision id
535
548
            elif len(lcas) == 1:
536
549
                self.base_rev_id = list(lcas)[0]
537
550
            else: # len(lcas) > 1
 
551
                self._is_criss_cross = True
538
552
                if len(lcas) > 2:
539
553
                    # find_unique_lca can only handle 2 nodes, so we have to
540
554
                    # start back at the beginning. It is a shame to traverse
545
559
                else:
546
560
                    self.base_rev_id = self.revision_graph.find_unique_lca(
547
561
                                            *lcas)
548
 
                self._is_criss_cross = True
 
562
                sorted_lca_keys = self.revision_graph.find_merge_order(
 
563
                    revisions[0], lcas)
 
564
                if self.base_rev_id == _mod_revision.NULL_REVISION:
 
565
                    self.base_rev_id = sorted_lca_keys[0]
 
566
 
549
567
            if self.base_rev_id == _mod_revision.NULL_REVISION:
550
568
                raise errors.UnrelatedBranches()
551
569
            if self._is_criss_cross:
552
570
                trace.warning('Warning: criss-cross merge encountered.  See bzr'
553
571
                              ' help criss-cross.')
554
572
                trace.mutter('Criss-cross lcas: %r' % lcas)
555
 
                interesting_revision_ids = [self.base_rev_id]
556
 
                interesting_revision_ids.extend(lcas)
 
573
                if self.base_rev_id in lcas:
 
574
                    trace.mutter('Unable to find unique lca. '
 
575
                                 'Fallback %r as best option.'
 
576
                                 % self.base_rev_id)
 
577
                interesting_revision_ids = set(lcas)
 
578
                interesting_revision_ids.add(self.base_rev_id)
557
579
                interesting_trees = dict((t.get_revision_id(), t)
558
580
                    for t in self.this_branch.repository.revision_trees(
559
581
                        interesting_revision_ids))
560
582
                self._cached_trees.update(interesting_trees)
561
 
                self.base_tree = interesting_trees.pop(self.base_rev_id)
562
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
563
 
                    revisions[0], lcas)
 
583
                if self.base_rev_id in lcas:
 
584
                    self.base_tree = interesting_trees[self.base_rev_id]
 
585
                else:
 
586
                    self.base_tree = interesting_trees.pop(self.base_rev_id)
564
587
                self._lca_trees = [interesting_trees[key]
565
588
                                   for key in sorted_lca_keys]
566
589
            else:
589
612
            self._maybe_fetch(base_branch, self.this_branch, self.base_rev_id)
590
613
 
591
614
    def make_merger(self):
592
 
        kwargs = {'working_tree':self.this_tree, 'this_tree': self.this_tree,
 
615
        kwargs = {'working_tree': self.this_tree, 'this_tree': self.this_tree,
593
616
                  'other_tree': self.other_tree,
594
617
                  'interesting_ids': self.interesting_ids,
595
618
                  'interesting_files': self.interesting_files,
596
619
                  'this_branch': self.this_branch,
 
620
                  'other_branch': self.other_branch,
597
621
                  'do_merge': False}
598
622
        if self.merge_type.requires_base:
599
623
            kwargs['base_tree'] = self.base_tree
625
649
        merge = self.make_merger()
626
650
        if self.other_branch is not None:
627
651
            self.other_branch.update_references(self.this_branch)
 
652
        for hook in Merger.hooks['pre_merge']:
 
653
            hook(merge)
628
654
        merge.do_merge()
 
655
        for hook in Merger.hooks['post_merge']:
 
656
            hook(merge)
629
657
        if self.recurse == 'down':
630
658
            for relpath, file_id in self.this_tree.iter_references():
631
659
                sub_tree = self.this_tree.get_nested_tree(file_id, relpath)
635
663
                    continue
636
664
                sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
637
665
                sub_merge.merge_type = self.merge_type
638
 
                other_branch = self.other_branch.reference_parent(file_id, relpath)
 
666
                other_branch = self.other_branch.reference_parent(file_id,
 
667
                                                                  relpath)
639
668
                sub_merge.set_other_revision(other_revision, other_branch)
640
669
                base_revision = self.base_tree.get_reference_revision(file_id)
641
670
                sub_merge.base_tree = \
645
674
        return merge
646
675
 
647
676
    def do_merge(self):
648
 
        operation = OperationWithCleanups(self._do_merge_to)
 
677
        operation = cleanup.OperationWithCleanups(self._do_merge_to)
649
678
        self.this_tree.lock_tree_write()
650
679
        operation.add_cleanup(self.this_tree.unlock)
651
680
        if self.base_tree is not None:
657
686
        merge = operation.run_simple()
658
687
        if len(merge.cooked_conflicts) == 0:
659
688
            if not self.ignore_zero and not trace.is_quiet():
660
 
                trace.note("All changes applied successfully.")
 
689
                trace.note(gettext("All changes applied successfully."))
661
690
        else:
662
 
            trace.note("%d conflicts encountered."
 
691
            trace.note(gettext("%d conflicts encountered.")
663
692
                       % len(merge.cooked_conflicts))
664
693
 
665
694
        return len(merge.cooked_conflicts)
697
726
                 interesting_ids=None, reprocess=False, show_base=False,
698
727
                 pb=None, pp=None, change_reporter=None,
699
728
                 interesting_files=None, do_merge=True,
700
 
                 cherrypick=False, lca_trees=None, this_branch=None):
 
729
                 cherrypick=False, lca_trees=None, this_branch=None,
 
730
                 other_branch=None):
701
731
        """Initialize the merger object and perform the merge.
702
732
 
703
733
        :param working_tree: The working tree to apply the merge to
704
734
        :param this_tree: The local tree in the merge operation
705
735
        :param base_tree: The common tree in the merge operation
706
736
        :param other_tree: The other tree to merge changes from
707
 
        :param this_branch: The branch associated with this_tree
 
737
        :param this_branch: The branch associated with this_tree.  Defaults to
 
738
            this_tree.branch if not supplied.
 
739
        :param other_branch: The branch associated with other_tree, if any.
708
740
        :param interesting_ids: The file_ids of files that should be
709
741
            participate in the merge.  May not be combined with
710
742
            interesting_files.
728
760
        if interesting_files is not None and interesting_ids is not None:
729
761
            raise ValueError(
730
762
                'specify either interesting_ids or interesting_files')
 
763
        if this_branch is None:
 
764
            this_branch = this_tree.branch
731
765
        self.interesting_ids = interesting_ids
732
766
        self.interesting_files = interesting_files
733
 
        self.this_tree = working_tree
 
767
        self.working_tree = working_tree
 
768
        self.this_tree = this_tree
734
769
        self.base_tree = base_tree
735
770
        self.other_tree = other_tree
736
771
        self.this_branch = this_branch
 
772
        self.other_branch = other_branch
737
773
        self._raw_conflicts = []
738
774
        self.cooked_conflicts = []
739
775
        self.reprocess = reprocess
754
790
            warnings.warn("pb argument to Merge3Merger is deprecated")
755
791
 
756
792
    def do_merge(self):
757
 
        operation = OperationWithCleanups(self._do_merge)
758
 
        self.this_tree.lock_tree_write()
 
793
        operation = cleanup.OperationWithCleanups(self._do_merge)
 
794
        self.working_tree.lock_tree_write()
 
795
        operation.add_cleanup(self.working_tree.unlock)
 
796
        self.this_tree.lock_read()
759
797
        operation.add_cleanup(self.this_tree.unlock)
760
798
        self.base_tree.lock_read()
761
799
        operation.add_cleanup(self.base_tree.unlock)
764
802
        operation.run()
765
803
 
766
804
    def _do_merge(self, operation):
767
 
        self.tt = transform.TreeTransform(self.this_tree, None)
 
805
        self.tt = transform.TreeTransform(self.working_tree, None)
768
806
        operation.add_cleanup(self.tt.finalize)
769
807
        self._compute_transform()
770
808
        results = self.tt.apply(no_conflicts=True)
771
809
        self.write_modified(results)
772
810
        try:
773
 
            self.this_tree.add_conflicts(self.cooked_conflicts)
 
811
            self.working_tree.add_conflicts(self.cooked_conflicts)
774
812
        except errors.UnsupportedOperation:
775
813
            pass
776
814
 
777
815
    def make_preview_transform(self):
778
 
        operation = OperationWithCleanups(self._make_preview_transform)
 
816
        operation = cleanup.OperationWithCleanups(self._make_preview_transform)
779
817
        self.base_tree.lock_read()
780
818
        operation.add_cleanup(self.base_tree.unlock)
781
819
        self.other_tree.lock_read()
783
821
        return operation.run_simple()
784
822
 
785
823
    def _make_preview_transform(self):
786
 
        self.tt = transform.TransformPreview(self.this_tree)
 
824
        self.tt = transform.TransformPreview(self.working_tree)
787
825
        self._compute_transform()
788
826
        return self.tt
789
827
 
794
832
        else:
795
833
            entries = self._entries_lca()
796
834
            resolver = self._lca_multi_way
 
835
        # Prepare merge hooks
 
836
        factories = Merger.hooks['merge_file_content']
 
837
        # One hook for each registered one plus our default merger
 
838
        hooks = [factory(self) for factory in factories] + [self]
 
839
        self.active_hooks = [hook for hook in hooks if hook is not None]
797
840
        child_pb = ui.ui_factory.nested_progress_bar()
798
841
        try:
799
 
            factories = Merger.hooks['merge_file_content']
800
 
            hooks = [factory(self) for factory in factories] + [self]
801
 
            self.active_hooks = [hook for hook in hooks if hook is not None]
802
842
            for num, (file_id, changed, parents3, names3,
803
843
                      executable3) in enumerate(entries):
804
 
                child_pb.update('Preparing file merge', num, len(entries))
 
844
                # Try merging each entry
 
845
                child_pb.update(gettext('Preparing file merge'),
 
846
                                num, len(entries))
805
847
                self._merge_names(file_id, parents3, names3, resolver=resolver)
806
848
                if changed:
807
849
                    file_status = self._do_merge_contents(file_id)
811
853
                    executable3, file_status, resolver=resolver)
812
854
        finally:
813
855
            child_pb.finished()
814
 
        self.fix_root()
 
856
        self.tt.fixup_new_roots()
 
857
        self._finish_computing_transform()
 
858
 
 
859
    def _finish_computing_transform(self):
 
860
        """Finalize the transform and report the changes.
 
861
 
 
862
        This is the second half of _compute_transform.
 
863
        """
815
864
        child_pb = ui.ui_factory.nested_progress_bar()
816
865
        try:
817
866
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
824
873
                self.tt.iter_changes(), self.change_reporter)
825
874
        self.cook_conflicts(fs_conflicts)
826
875
        for conflict in self.cooked_conflicts:
827
 
            trace.warning(conflict)
 
876
            trace.warning(unicode(conflict))
828
877
 
829
878
    def _entries3(self):
830
879
        """Gather data about files modified between three trees.
837
886
        """
838
887
        result = []
839
888
        iterator = self.other_tree.iter_changes(self.base_tree,
840
 
                include_unchanged=True, specific_files=self.interesting_files,
 
889
                specific_files=self.interesting_files,
841
890
                extra_trees=[self.this_tree])
842
891
        this_entries = dict((e.file_id, e) for p, e in
843
892
                            self.this_tree.iter_entries_by_dir(
869
918
        it then compares with THIS and BASE.
870
919
 
871
920
        For the multi-valued entries, the format will be (BASE, [lca1, lca2])
872
 
        :return: [(file_id, changed, parents, names, executable)]
873
 
            file_id     Simple file_id of the entry
874
 
            changed     Boolean, True if the kind or contents changed
875
 
                        else False
876
 
            parents     ((base, [parent_id, in, lcas]), parent_id_other,
877
 
                         parent_id_this)
878
 
            names       ((base, [name, in, lcas]), name_in_other, name_in_this)
879
 
            executable  ((base, [exec, in, lcas]), exec_in_other, exec_in_this)
 
921
 
 
922
        :return: [(file_id, changed, parents, names, executable)], where:
 
923
 
 
924
            * file_id: Simple file_id of the entry
 
925
            * changed: Boolean, True if the kind or contents changed else False
 
926
            * parents: ((base, [parent_id, in, lcas]), parent_id_other,
 
927
                        parent_id_this)
 
928
            * names:   ((base, [name, in, lcas]), name_in_other, name_in_this)
 
929
            * executable: ((base, [exec, in, lcas]), exec_in_other,
 
930
                        exec_in_this)
880
931
        """
881
932
        if self.interesting_files is not None:
882
933
            lookup_trees = [self.this_tree, self.base_tree]
924
975
                else:
925
976
                    lca_entries.append(lca_ie)
926
977
 
927
 
            if file_id in base_inventory:
 
978
            if base_inventory.has_id(file_id):
928
979
                base_ie = base_inventory[file_id]
929
980
            else:
930
981
                base_ie = _none_entry
931
982
 
932
 
            if file_id in this_inventory:
 
983
            if this_inventory.has_id(file_id):
933
984
                this_ie = this_inventory[file_id]
934
985
            else:
935
986
                this_ie = _none_entry
1015
1066
                        continue
1016
1067
                else:
1017
1068
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
1018
 
                # XXX: We need to handle kind == 'symlink'
1019
1069
 
1020
1070
            # If we have gotten this far, that means something has changed
1021
1071
            result.append((file_id, content_changed,
1028
1078
                          ))
1029
1079
        return result
1030
1080
 
1031
 
 
 
1081
    @deprecated_method(deprecated_in((2, 4, 0)))
1032
1082
    def fix_root(self):
1033
 
        try:
1034
 
            self.tt.final_kind(self.tt.root)
1035
 
        except errors.NoSuchFile:
 
1083
        if self.tt.final_kind(self.tt.root) is None:
1036
1084
            self.tt.cancel_deletion(self.tt.root)
1037
1085
        if self.tt.final_file_id(self.tt.root) is None:
1038
1086
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
1043
1091
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1044
1092
        if other_root == self.tt.root:
1045
1093
            return
1046
 
        try:
1047
 
            self.tt.final_kind(other_root)
1048
 
        except errors.NoSuchFile:
1049
 
            return
1050
 
        if self.this_tree.has_id(self.other_tree.inventory.root.file_id):
1051
 
            # the other tree's root is a non-root in the current tree
1052
 
            return
1053
 
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
1054
 
        self.tt.cancel_creation(other_root)
1055
 
        self.tt.cancel_versioning(other_root)
1056
 
 
1057
 
    def reparent_children(self, ie, target):
1058
 
        for thing, child in ie.children.iteritems():
 
1094
        if self.this_tree.inventory.has_id(
 
1095
            self.other_tree.inventory.root.file_id):
 
1096
            # the other tree's root is a non-root in the current tree (as
 
1097
            # when a previously unrelated branch is merged into another)
 
1098
            return
 
1099
        if self.tt.final_kind(other_root) is not None:
 
1100
            other_root_is_present = True
 
1101
        else:
 
1102
            # other_root doesn't have a physical representation. We still need
 
1103
            # to move any references to the actual root of the tree.
 
1104
            other_root_is_present = False
 
1105
        # 'other_tree.inventory.root' is not present in this tree. We are
 
1106
        # calling adjust_path for children which *want* to be present with a
 
1107
        # correct place to go.
 
1108
        for _, child in self.other_tree.inventory.root.children.iteritems():
1059
1109
            trans_id = self.tt.trans_id_file_id(child.file_id)
1060
 
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
 
1110
            if not other_root_is_present:
 
1111
                if self.tt.final_kind(trans_id) is not None:
 
1112
                    # The item exist in the final tree and has a defined place
 
1113
                    # to go already.
 
1114
                    continue
 
1115
            # Move the item into the root
 
1116
            try:
 
1117
                final_name = self.tt.final_name(trans_id)
 
1118
            except errors.NoFinalPath:
 
1119
                # This file is not present anymore, ignore it.
 
1120
                continue
 
1121
            self.tt.adjust_path(final_name, self.tt.root, trans_id)
 
1122
        if other_root_is_present:
 
1123
            self.tt.cancel_creation(other_root)
 
1124
            self.tt.cancel_versioning(other_root)
1061
1125
 
1062
1126
    def write_modified(self, results):
1063
1127
        modified_hashes = {}
1064
1128
        for path in results.modified_paths:
1065
 
            file_id = self.this_tree.path2id(self.this_tree.relpath(path))
 
1129
            file_id = self.working_tree.path2id(self.working_tree.relpath(path))
1066
1130
            if file_id is None:
1067
1131
                continue
1068
 
            hash = self.this_tree.get_file_sha1(file_id)
 
1132
            hash = self.working_tree.get_file_sha1(file_id)
1069
1133
            if hash is None:
1070
1134
                continue
1071
1135
            modified_hashes[file_id] = hash
1072
 
        self.this_tree.set_merge_modified(modified_hashes)
 
1136
        self.working_tree.set_merge_modified(modified_hashes)
1073
1137
 
1074
1138
    @staticmethod
1075
1139
    def parent(entry, file_id):
1088
1152
    @staticmethod
1089
1153
    def contents_sha1(tree, file_id):
1090
1154
        """Determine the sha1 of the file contents (used as a key method)."""
1091
 
        if file_id not in tree:
 
1155
        if not tree.has_id(file_id):
1092
1156
            return None
1093
1157
        return tree.get_file_sha1(file_id)
1094
1158
 
1110
1174
 
1111
1175
    @staticmethod
1112
1176
    def _three_way(base, other, this):
1113
 
        #if base == other, either they all agree, or only THIS has changed.
1114
1177
        if base == other:
 
1178
            # if 'base == other', either they all agree, or only 'this' has
 
1179
            # changed.
1115
1180
            return 'this'
1116
1181
        elif this not in (base, other):
 
1182
            # 'this' is neither 'base' nor 'other', so both sides changed
1117
1183
            return 'conflict'
1118
 
        # "Ambiguous clean merge" -- both sides have made the same change.
1119
1184
        elif this == other:
 
1185
            # "Ambiguous clean merge" -- both sides have made the same change.
1120
1186
            return "this"
1121
 
        # this == base: only other has changed.
1122
1187
        else:
 
1188
            # this == base: only other has changed.
1123
1189
            return "other"
1124
1190
 
1125
1191
    @staticmethod
1169
1235
                # only has an lca value
1170
1236
                return 'other'
1171
1237
 
1172
 
        # At this point, the lcas disagree, and the tips disagree
 
1238
        # At this point, the lcas disagree, and the tip disagree
1173
1239
        return 'conflict'
1174
1240
 
1175
1241
    @staticmethod
 
1242
    @deprecated_method(deprecated_in((2, 2, 0)))
1176
1243
    def scalar_three_way(this_tree, base_tree, other_tree, file_id, key):
1177
1244
        """Do a three-way test on a scalar.
1178
1245
        Return "this", "other" or "conflict", depending whether a value wins.
1228
1295
                parent_id_winner = "other"
1229
1296
        if name_winner == "this" and parent_id_winner == "this":
1230
1297
            return
1231
 
        if name_winner == "conflict":
1232
 
            trans_id = self.tt.trans_id_file_id(file_id)
1233
 
            self._raw_conflicts.append(('name conflict', trans_id,
1234
 
                                        this_name, other_name))
1235
 
        if parent_id_winner == "conflict":
1236
 
            trans_id = self.tt.trans_id_file_id(file_id)
1237
 
            self._raw_conflicts.append(('parent conflict', trans_id,
1238
 
                                        this_parent, other_parent))
1239
 
        if other_name is None:
 
1298
        if name_winner == 'conflict' or parent_id_winner == 'conflict':
 
1299
            # Creating helpers (.OTHER or .THIS) here cause problems down the
 
1300
            # road if a ContentConflict needs to be created so we should not do
 
1301
            # that
 
1302
            trans_id = self.tt.trans_id_file_id(file_id)
 
1303
            self._raw_conflicts.append(('path conflict', trans_id, file_id,
 
1304
                                        this_parent, this_name,
 
1305
                                        other_parent, other_name))
 
1306
        if not self.other_tree.has_id(file_id):
1240
1307
            # it doesn't matter whether the result was 'other' or
1241
 
            # 'conflict'-- if there's no 'other', we leave it alone.
 
1308
            # 'conflict'-- if it has no file id, we leave it alone.
1242
1309
            return
1243
 
        # if we get here, name_winner and parent_winner are set to safe values.
1244
 
        trans_id = self.tt.trans_id_file_id(file_id)
1245
1310
        parent_id = parents[self.winner_idx[parent_id_winner]]
1246
 
        if parent_id is not None:
1247
 
            parent_trans_id = self.tt.trans_id_file_id(parent_id)
1248
 
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
1249
 
                                parent_trans_id, trans_id)
 
1311
        name = names[self.winner_idx[name_winner]]
 
1312
        if parent_id is not None or name is not None:
 
1313
            # if we get here, name_winner and parent_winner are set to safe
 
1314
            # values.
 
1315
            if parent_id is None and name is not None:
 
1316
                # if parent_id is None and name is non-None, current file is
 
1317
                # the tree root.
 
1318
                if names[self.winner_idx[parent_id_winner]] != '':
 
1319
                    raise AssertionError(
 
1320
                        'File looks like a root, but named %s' %
 
1321
                        names[self.winner_idx[parent_id_winner]])
 
1322
                parent_trans_id = transform.ROOT_PARENT
 
1323
            else:
 
1324
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1325
            self.tt.adjust_path(name, parent_trans_id,
 
1326
                                self.tt.trans_id_file_id(file_id))
1250
1327
 
1251
1328
    def _do_merge_contents(self, file_id):
1252
1329
        """Performs a merge on file_id contents."""
1253
1330
        def contents_pair(tree):
1254
 
            if file_id not in tree:
 
1331
            if not tree.has_id(file_id):
1255
1332
                return (None, None)
1256
1333
            kind = tree.kind(file_id)
1257
1334
            if kind == "file":
1286
1363
        # We have a hypothetical conflict, but if we have files, then we
1287
1364
        # can try to merge the content
1288
1365
        trans_id = self.tt.trans_id_file_id(file_id)
1289
 
        params = MergeHookParams(self, file_id, trans_id, this_pair[0],
 
1366
        params = MergeFileHookParams(self, file_id, trans_id, this_pair[0],
1290
1367
            other_pair[0], winner)
1291
1368
        hooks = self.active_hooks
1292
1369
        hook_status = 'not_applicable'
1295
1372
            if hook_status != 'not_applicable':
1296
1373
                # Don't try any more hooks, this one applies.
1297
1374
                break
 
1375
        # If the merge ends up replacing the content of the file, we get rid of
 
1376
        # it at the end of this method (this variable is used to track the
 
1377
        # exceptions to this rule).
 
1378
        keep_this = False
1298
1379
        result = "modified"
1299
1380
        if hook_status == 'not_applicable':
1300
 
            # This is a contents conflict, because none of the available
1301
 
            # functions could merge it.
 
1381
            # No merge hook was able to resolve the situation. Two cases exist:
 
1382
            # a content conflict or a duplicate one.
1302
1383
            result = None
1303
1384
            name = self.tt.final_name(trans_id)
1304
1385
            parent_id = self.tt.final_parent(trans_id)
1305
 
            if self.this_tree.has_id(file_id):
1306
 
                self.tt.unversion_file(trans_id)
1307
 
            file_group = self._dump_conflicts(name, parent_id, file_id,
1308
 
                                              set_version=True)
1309
 
            self._raw_conflicts.append(('contents conflict', file_group))
 
1386
            duplicate = False
 
1387
            inhibit_content_conflict = False
 
1388
            if params.this_kind is None: # file_id is not in THIS
 
1389
                # Is the name used for a different file_id ?
 
1390
                dupe_path = self.other_tree.id2path(file_id)
 
1391
                this_id = self.this_tree.path2id(dupe_path)
 
1392
                if this_id is not None:
 
1393
                    # Two entries for the same path
 
1394
                    keep_this = True
 
1395
                    # versioning the merged file will trigger a duplicate
 
1396
                    # conflict
 
1397
                    self.tt.version_file(file_id, trans_id)
 
1398
                    transform.create_from_tree(
 
1399
                        self.tt, trans_id, self.other_tree, file_id,
 
1400
                        filter_tree_path=self._get_filter_tree_path(file_id))
 
1401
                    inhibit_content_conflict = True
 
1402
            elif params.other_kind is None: # file_id is not in OTHER
 
1403
                # Is the name used for a different file_id ?
 
1404
                dupe_path = self.this_tree.id2path(file_id)
 
1405
                other_id = self.other_tree.path2id(dupe_path)
 
1406
                if other_id is not None:
 
1407
                    # Two entries for the same path again, but here, the other
 
1408
                    # entry will also be merged.  We simply inhibit the
 
1409
                    # 'content' conflict creation because we know OTHER will
 
1410
                    # create (or has already created depending on ordering) an
 
1411
                    # entry at the same path. This will trigger a 'duplicate'
 
1412
                    # conflict later.
 
1413
                    keep_this = True
 
1414
                    inhibit_content_conflict = True
 
1415
            if not inhibit_content_conflict:
 
1416
                if params.this_kind is not None:
 
1417
                    self.tt.unversion_file(trans_id)
 
1418
                # This is a contents conflict, because none of the available
 
1419
                # functions could merge it.
 
1420
                file_group = self._dump_conflicts(name, parent_id, file_id,
 
1421
                                                  set_version=True)
 
1422
                self._raw_conflicts.append(('contents conflict', file_group))
1310
1423
        elif hook_status == 'success':
1311
1424
            self.tt.create_file(lines, trans_id)
1312
1425
        elif hook_status == 'conflicted':
1328
1441
            raise AssertionError('unknown hook_status: %r' % (hook_status,))
1329
1442
        if not self.this_tree.has_id(file_id) and result == "modified":
1330
1443
            self.tt.version_file(file_id, trans_id)
1331
 
        # The merge has been performed, so the old contents should not be
1332
 
        # retained.
1333
 
        try:
 
1444
        if not keep_this:
 
1445
            # The merge has been performed and produced a new content, so the
 
1446
            # old contents should not be retained.
1334
1447
            self.tt.delete_contents(trans_id)
1335
 
        except errors.NoSuchFile:
1336
 
            pass
1337
1448
        return result
1338
1449
 
1339
1450
    def _default_other_winner_merge(self, merge_hook_params):
1340
1451
        """Replace this contents with other."""
1341
1452
        file_id = merge_hook_params.file_id
1342
1453
        trans_id = merge_hook_params.trans_id
1343
 
        file_in_this = self.this_tree.has_id(file_id)
1344
1454
        if self.other_tree.has_id(file_id):
1345
1455
            # OTHER changed the file
1346
 
            wt = self.this_tree
1347
 
            if wt.supports_content_filtering():
1348
 
                # We get the path from the working tree if it exists.
1349
 
                # That fails though when OTHER is adding a file, so
1350
 
                # we fall back to the other tree to find the path if
1351
 
                # it doesn't exist locally.
1352
 
                try:
1353
 
                    filter_tree_path = wt.id2path(file_id)
1354
 
                except errors.NoSuchId:
1355
 
                    filter_tree_path = self.other_tree.id2path(file_id)
1356
 
            else:
1357
 
                # Skip the id2path lookup for older formats
1358
 
                filter_tree_path = None
1359
 
            transform.create_from_tree(self.tt, trans_id,
1360
 
                             self.other_tree, file_id,
1361
 
                             filter_tree_path=filter_tree_path)
 
1456
            transform.create_from_tree(
 
1457
                self.tt, trans_id, self.other_tree, file_id,
 
1458
                filter_tree_path=self._get_filter_tree_path(file_id))
1362
1459
            return 'done', None
1363
 
        elif file_in_this:
 
1460
        elif self.this_tree.has_id(file_id):
1364
1461
            # OTHER deleted the file
1365
1462
            return 'delete', None
1366
1463
        else:
1392
1489
    def get_lines(self, tree, file_id):
1393
1490
        """Return the lines in a file, or an empty list."""
1394
1491
        if tree.has_id(file_id):
1395
 
            return tree.get_file(file_id).readlines()
 
1492
            return tree.get_file_lines(file_id)
1396
1493
        else:
1397
1494
            return []
1398
1495
 
1440
1537
                                              other_lines)
1441
1538
            file_group.append(trans_id)
1442
1539
 
 
1540
 
 
1541
    def _get_filter_tree_path(self, file_id):
 
1542
        if self.this_tree.supports_content_filtering():
 
1543
            # We get the path from the working tree if it exists.
 
1544
            # That fails though when OTHER is adding a file, so
 
1545
            # we fall back to the other tree to find the path if
 
1546
            # it doesn't exist locally.
 
1547
            try:
 
1548
                return self.this_tree.id2path(file_id)
 
1549
            except errors.NoSuchId:
 
1550
                return self.other_tree.id2path(file_id)
 
1551
        # Skip the id2path lookup for older formats
 
1552
        return None
 
1553
 
1443
1554
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1444
1555
                        base_lines=None, other_lines=None, set_version=False,
1445
1556
                        no_base=False):
1511
1622
        if winner == 'this' and file_status != "modified":
1512
1623
            return
1513
1624
        trans_id = self.tt.trans_id_file_id(file_id)
1514
 
        try:
1515
 
            if self.tt.final_kind(trans_id) != "file":
1516
 
                return
1517
 
        except errors.NoSuchFile:
 
1625
        if self.tt.final_kind(trans_id) != "file":
1518
1626
            return
1519
1627
        if winner == "this":
1520
1628
            executability = this_executable
1531
1639
 
1532
1640
    def cook_conflicts(self, fs_conflicts):
1533
1641
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1534
 
        name_conflicts = {}
1535
 
        self.cooked_conflicts.extend(transform.cook_conflicts(
1536
 
                fs_conflicts, self.tt))
 
1642
        content_conflict_file_ids = set()
 
1643
        cooked_conflicts = transform.cook_conflicts(fs_conflicts, self.tt)
1537
1644
        fp = transform.FinalPaths(self.tt)
1538
1645
        for conflict in self._raw_conflicts:
1539
1646
            conflict_type = conflict[0]
1540
 
            if conflict_type in ('name conflict', 'parent conflict'):
1541
 
                trans_id = conflict[1]
1542
 
                conflict_args = conflict[2:]
1543
 
                if trans_id not in name_conflicts:
1544
 
                    name_conflicts[trans_id] = {}
1545
 
                transform.unique_add(name_conflicts[trans_id], conflict_type,
1546
 
                                     conflict_args)
1547
 
            if conflict_type == 'contents conflict':
 
1647
            if conflict_type == 'path conflict':
 
1648
                (trans_id, file_id,
 
1649
                this_parent, this_name,
 
1650
                other_parent, other_name) = conflict[1:]
 
1651
                if this_parent is None or this_name is None:
 
1652
                    this_path = '<deleted>'
 
1653
                else:
 
1654
                    parent_path =  fp.get_path(
 
1655
                        self.tt.trans_id_file_id(this_parent))
 
1656
                    this_path = osutils.pathjoin(parent_path, this_name)
 
1657
                if other_parent is None or other_name is None:
 
1658
                    other_path = '<deleted>'
 
1659
                else:
 
1660
                    if other_parent == self.other_tree.get_root_id():
 
1661
                        # The tree transform doesn't know about the other root,
 
1662
                        # so we special case here to avoid a NoFinalPath
 
1663
                        # exception
 
1664
                        parent_path = ''
 
1665
                    else:
 
1666
                        parent_path =  fp.get_path(
 
1667
                            self.tt.trans_id_file_id(other_parent))
 
1668
                    other_path = osutils.pathjoin(parent_path, other_name)
 
1669
                c = _mod_conflicts.Conflict.factory(
 
1670
                    'path conflict', path=this_path,
 
1671
                    conflict_path=other_path,
 
1672
                    file_id=file_id)
 
1673
            elif conflict_type == 'contents conflict':
1548
1674
                for trans_id in conflict[1]:
1549
1675
                    file_id = self.tt.final_file_id(trans_id)
1550
1676
                    if file_id is not None:
 
1677
                        # Ok we found the relevant file-id
1551
1678
                        break
1552
1679
                path = fp.get_path(trans_id)
1553
1680
                for suffix in ('.BASE', '.THIS', '.OTHER'):
1554
1681
                    if path.endswith(suffix):
 
1682
                        # Here is the raw path
1555
1683
                        path = path[:-len(suffix)]
1556
1684
                        break
1557
1685
                c = _mod_conflicts.Conflict.factory(conflict_type,
1558
1686
                                                    path=path, file_id=file_id)
1559
 
                self.cooked_conflicts.append(c)
1560
 
            if conflict_type == 'text conflict':
 
1687
                content_conflict_file_ids.add(file_id)
 
1688
            elif conflict_type == 'text conflict':
1561
1689
                trans_id = conflict[1]
1562
1690
                path = fp.get_path(trans_id)
1563
1691
                file_id = self.tt.final_file_id(trans_id)
1564
1692
                c = _mod_conflicts.Conflict.factory(conflict_type,
1565
1693
                                                    path=path, file_id=file_id)
1566
 
                self.cooked_conflicts.append(c)
 
1694
            else:
 
1695
                raise AssertionError('bad conflict type: %r' % (conflict,))
 
1696
            cooked_conflicts.append(c)
1567
1697
 
1568
 
        for trans_id, conflicts in name_conflicts.iteritems():
1569
 
            try:
1570
 
                this_parent, other_parent = conflicts['parent conflict']
1571
 
                if this_parent == other_parent:
1572
 
                    raise AssertionError()
1573
 
            except KeyError:
1574
 
                this_parent = other_parent = \
1575
 
                    self.tt.final_file_id(self.tt.final_parent(trans_id))
1576
 
            try:
1577
 
                this_name, other_name = conflicts['name conflict']
1578
 
                if this_name == other_name:
1579
 
                    raise AssertionError()
1580
 
            except KeyError:
1581
 
                this_name = other_name = self.tt.final_name(trans_id)
1582
 
            other_path = fp.get_path(trans_id)
1583
 
            if this_parent is not None and this_name is not None:
1584
 
                this_parent_path = \
1585
 
                    fp.get_path(self.tt.trans_id_file_id(this_parent))
1586
 
                this_path = osutils.pathjoin(this_parent_path, this_name)
1587
 
            else:
1588
 
                this_path = "<deleted>"
1589
 
            file_id = self.tt.final_file_id(trans_id)
1590
 
            c = _mod_conflicts.Conflict.factory('path conflict', path=this_path,
1591
 
                                                conflict_path=other_path,
1592
 
                                                file_id=file_id)
 
1698
        self.cooked_conflicts = []
 
1699
        # We want to get rid of path conflicts when a corresponding contents
 
1700
        # conflict exists. This can occur when one branch deletes a file while
 
1701
        # the other renames *and* modifies it. In this case, the content
 
1702
        # conflict is enough.
 
1703
        for c in cooked_conflicts:
 
1704
            if (c.typestring == 'path conflict'
 
1705
                and c.file_id in content_conflict_file_ids):
 
1706
                continue
1593
1707
            self.cooked_conflicts.append(c)
1594
1708
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1595
1709
 
1701
1815
            osutils.rmtree(temp_dir)
1702
1816
 
1703
1817
 
 
1818
class PathNotInTree(errors.BzrError):
 
1819
 
 
1820
    _fmt = """Merge-into failed because %(tree)s does not contain %(path)s."""
 
1821
 
 
1822
    def __init__(self, path, tree):
 
1823
        errors.BzrError.__init__(self, path=path, tree=tree)
 
1824
 
 
1825
 
 
1826
class MergeIntoMerger(Merger):
 
1827
    """Merger that understands other_tree will be merged into a subdir.
 
1828
 
 
1829
    This also changes the Merger api so that it uses real Branch, revision_id,
 
1830
    and RevisonTree objects, rather than using revision specs.
 
1831
    """
 
1832
 
 
1833
    def __init__(self, this_tree, other_branch, other_tree, target_subdir,
 
1834
            source_subpath, other_rev_id=None):
 
1835
        """Create a new MergeIntoMerger object.
 
1836
 
 
1837
        source_subpath in other_tree will be effectively copied to
 
1838
        target_subdir in this_tree.
 
1839
 
 
1840
        :param this_tree: The tree that we will be merging into.
 
1841
        :param other_branch: The Branch we will be merging from.
 
1842
        :param other_tree: The RevisionTree object we want to merge.
 
1843
        :param target_subdir: The relative path where we want to merge
 
1844
            other_tree into this_tree
 
1845
        :param source_subpath: The relative path specifying the subtree of
 
1846
            other_tree to merge into this_tree.
 
1847
        """
 
1848
        # It is assumed that we are merging a tree that is not in our current
 
1849
        # ancestry, which means we are using the "EmptyTree" as our basis.
 
1850
        null_ancestor_tree = this_tree.branch.repository.revision_tree(
 
1851
                                _mod_revision.NULL_REVISION)
 
1852
        super(MergeIntoMerger, self).__init__(
 
1853
            this_branch=this_tree.branch,
 
1854
            this_tree=this_tree,
 
1855
            other_tree=other_tree,
 
1856
            base_tree=null_ancestor_tree,
 
1857
            )
 
1858
        self._target_subdir = target_subdir
 
1859
        self._source_subpath = source_subpath
 
1860
        self.other_branch = other_branch
 
1861
        if other_rev_id is None:
 
1862
            other_rev_id = other_tree.get_revision_id()
 
1863
        self.other_rev_id = self.other_basis = other_rev_id
 
1864
        self.base_is_ancestor = True
 
1865
        self.backup_files = True
 
1866
        self.merge_type = Merge3Merger
 
1867
        self.show_base = False
 
1868
        self.reprocess = False
 
1869
        self.interesting_ids = None
 
1870
        self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
 
1871
              target_subdir=self._target_subdir,
 
1872
              source_subpath=self._source_subpath)
 
1873
        if self._source_subpath != '':
 
1874
            # If this isn't a partial merge make sure the revisions will be
 
1875
            # present.
 
1876
            self._maybe_fetch(self.other_branch, self.this_branch,
 
1877
                self.other_basis)
 
1878
 
 
1879
    def set_pending(self):
 
1880
        if self._source_subpath != '':
 
1881
            return
 
1882
        Merger.set_pending(self)
 
1883
 
 
1884
 
 
1885
class _MergeTypeParameterizer(object):
 
1886
    """Wrap a merge-type class to provide extra parameters.
 
1887
    
 
1888
    This is hack used by MergeIntoMerger to pass some extra parameters to its
 
1889
    merge_type.  Merger.do_merge() sets up its own set of parameters to pass to
 
1890
    the 'merge_type' member.  It is difficult override do_merge without
 
1891
    re-writing the whole thing, so instead we create a wrapper which will pass
 
1892
    the extra parameters.
 
1893
    """
 
1894
 
 
1895
    def __init__(self, merge_type, **kwargs):
 
1896
        self._extra_kwargs = kwargs
 
1897
        self._merge_type = merge_type
 
1898
 
 
1899
    def __call__(self, *args, **kwargs):
 
1900
        kwargs.update(self._extra_kwargs)
 
1901
        return self._merge_type(*args, **kwargs)
 
1902
 
 
1903
    def __getattr__(self, name):
 
1904
        return getattr(self._merge_type, name)
 
1905
 
 
1906
 
 
1907
class MergeIntoMergeType(Merge3Merger):
 
1908
    """Merger that incorporates a tree (or part of a tree) into another."""
 
1909
 
 
1910
    def __init__(self, *args, **kwargs):
 
1911
        """Initialize the merger object.
 
1912
 
 
1913
        :param args: See Merge3Merger.__init__'s args.
 
1914
        :param kwargs: See Merge3Merger.__init__'s keyword args, except for
 
1915
            source_subpath and target_subdir.
 
1916
        :keyword source_subpath: The relative path specifying the subtree of
 
1917
            other_tree to merge into this_tree.
 
1918
        :keyword target_subdir: The relative path where we want to merge
 
1919
            other_tree into this_tree
 
1920
        """
 
1921
        # All of the interesting work happens during Merge3Merger.__init__(),
 
1922
        # so we have have to hack in to get our extra parameters set.
 
1923
        self._source_subpath = kwargs.pop('source_subpath')
 
1924
        self._target_subdir = kwargs.pop('target_subdir')
 
1925
        super(MergeIntoMergeType, self).__init__(*args, **kwargs)
 
1926
 
 
1927
    def _compute_transform(self):
 
1928
        child_pb = ui.ui_factory.nested_progress_bar()
 
1929
        try:
 
1930
            entries = self._entries_to_incorporate()
 
1931
            entries = list(entries)
 
1932
            for num, (entry, parent_id) in enumerate(entries):
 
1933
                child_pb.update(gettext('Preparing file merge'), num, len(entries))
 
1934
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
 
1935
                trans_id = transform.new_by_entry(self.tt, entry,
 
1936
                    parent_trans_id, self.other_tree)
 
1937
        finally:
 
1938
            child_pb.finished()
 
1939
        self._finish_computing_transform()
 
1940
 
 
1941
    def _entries_to_incorporate(self):
 
1942
        """Yields pairs of (inventory_entry, new_parent)."""
 
1943
        other_inv = self.other_tree.inventory
 
1944
        subdir_id = other_inv.path2id(self._source_subpath)
 
1945
        if subdir_id is None:
 
1946
            # XXX: The error would be clearer if it gave the URL of the source
 
1947
            # branch, but we don't have a reference to that here.
 
1948
            raise PathNotInTree(self._source_subpath, "Source tree")
 
1949
        subdir = other_inv[subdir_id]
 
1950
        parent_in_target = osutils.dirname(self._target_subdir)
 
1951
        target_id = self.this_tree.inventory.path2id(parent_in_target)
 
1952
        if target_id is None:
 
1953
            raise PathNotInTree(self._target_subdir, "Target tree")
 
1954
        name_in_target = osutils.basename(self._target_subdir)
 
1955
        merge_into_root = subdir.copy()
 
1956
        merge_into_root.name = name_in_target
 
1957
        if self.this_tree.inventory.has_id(merge_into_root.file_id):
 
1958
            # Give the root a new file-id.
 
1959
            # This can happen fairly easily if the directory we are
 
1960
            # incorporating is the root, and both trees have 'TREE_ROOT' as
 
1961
            # their root_id.  Users will expect this to Just Work, so we
 
1962
            # change the file-id here.
 
1963
            # Non-root file-ids could potentially conflict too.  That's really
 
1964
            # an edge case, so we don't do anything special for those.  We let
 
1965
            # them cause conflicts.
 
1966
            merge_into_root.file_id = generate_ids.gen_file_id(name_in_target)
 
1967
        yield (merge_into_root, target_id)
 
1968
        if subdir.kind != 'directory':
 
1969
            # No children, so we are done.
 
1970
            return
 
1971
        for ignored_path, entry in other_inv.iter_entries_by_dir(subdir_id):
 
1972
            parent_id = entry.parent_id
 
1973
            if parent_id == subdir.file_id:
 
1974
                # The root's parent ID has changed, so make sure children of
 
1975
                # the root refer to the new ID.
 
1976
                parent_id = merge_into_root.file_id
 
1977
            yield (entry, parent_id)
 
1978
 
 
1979
 
1704
1980
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1705
1981
                backup_files=False,
1706
1982
                merge_type=Merge3Merger,
1714
1990
                change_reporter=None):
1715
1991
    """Primary interface for merging.
1716
1992
 
1717
 
        typical use is probably
1718
 
        'merge_inner(branch, branch.get_revision_tree(other_revision),
1719
 
                     branch.get_revision_tree(base_revision))'
1720
 
        """
 
1993
    Typical use is probably::
 
1994
 
 
1995
        merge_inner(branch, branch.get_revision_tree(other_revision),
 
1996
                    branch.get_revision_tree(base_revision))
 
1997
    """
1721
1998
    if this_tree is None:
1722
1999
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
1723
 
                              "parameter as of bzrlib version 0.8.")
 
2000
                              "parameter")
1724
2001
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
1725
2002
                    pb=pb, change_reporter=change_reporter)
1726
2003
    merger.backup_files = backup_files
1743
2020
    merger.set_base_revision(get_revision_id(), this_branch)
1744
2021
    return merger.do_merge()
1745
2022
 
 
2023
 
 
2024
merge_type_registry = registry.Registry()
 
2025
merge_type_registry.register('diff3', Diff3Merger,
 
2026
                             "Merge using external diff3.")
 
2027
merge_type_registry.register('lca', LCAMerger,
 
2028
                             "LCA-newness merge.")
 
2029
merge_type_registry.register('merge3', Merge3Merger,
 
2030
                             "Native diff3-style merge.")
 
2031
merge_type_registry.register('weave', WeaveMerger,
 
2032
                             "Weave-based merge.")
 
2033
 
 
2034
 
1746
2035
def get_merge_type_registry():
1747
 
    """Merge type registry is in bzrlib.option to avoid circular imports.
 
2036
    """Merge type registry was previously in bzrlib.option
1748
2037
 
1749
 
    This method provides a sanctioned way to retrieve it.
 
2038
    This method provides a backwards compatible way to retrieve it.
1750
2039
    """
1751
 
    from bzrlib import option
1752
 
    return option._merge_type_registry
 
2040
    return merge_type_registry
1753
2041
 
1754
2042
 
1755
2043
def _plan_annotate_merge(annotated_a, annotated_b, ancestors_a, ancestors_b):
2180
2468
class _PlanLCAMerge(_PlanMergeBase):
2181
2469
    """
2182
2470
    This merge algorithm differs from _PlanMerge in that:
 
2471
 
2183
2472
    1. comparisons are done against LCAs only
2184
2473
    2. cases where a contested line is new versus one LCA but old versus
2185
2474
       another are marked as conflicts, by emitting the line as conflicted-a
2226
2515
 
2227
2516
        If a line is killed and new, this indicates that the two merge
2228
2517
        revisions contain differing conflict resolutions.
 
2518
 
2229
2519
        :param revision_id: The id of the revision in which the lines are
2230
2520
            unique
2231
2521
        :param unique_line_numbers: The line numbers of unique lines.
2232
 
        :return a tuple of (new_this, killed_other):
 
2522
        :return: a tuple of (new_this, killed_other)
2233
2523
        """
2234
2524
        new = set()
2235
2525
        killed = set()