~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Martin von Gagern
  • Date: 2010-04-22 06:25:26 UTC
  • mfrom: (0.27.39 trunk)
  • mto: This revision was merged to the branch mainline in revision 5240.
  • Revision ID: martin.vgagern@gmx.net-20100422062526-4lyy2yoor932u80w
Join bzr-bash-completion plugin into core bzr tree.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 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
16
16
 
17
17
import warnings
18
18
 
19
 
from bzrlib.lazy_import import lazy_import
20
 
lazy_import(globals(), """
21
19
from bzrlib import (
22
20
    branch as _mod_branch,
23
 
    cleanup,
24
21
    conflicts as _mod_conflicts,
25
22
    debug,
26
 
    generate_ids,
 
23
    decorators,
 
24
    errors,
27
25
    graph as _mod_graph,
 
26
    hooks,
28
27
    merge3,
29
28
    osutils,
30
29
    patiencediff,
35
34
    tree as _mod_tree,
36
35
    tsort,
37
36
    ui,
38
 
    versionedfile,
39
 
    workingtree,
40
 
    )
41
 
""")
42
 
from bzrlib import (
43
 
    decorators,
44
 
    errors,
45
 
    hooks,
46
 
    )
 
37
    versionedfile
 
38
    )
 
39
from bzrlib.cleanup import OperationWithCleanups
47
40
from bzrlib.symbol_versioning import (
48
41
    deprecated_in,
49
42
    deprecated_method,
53
46
 
54
47
def transform_tree(from_tree, to_tree, interesting_ids=None):
55
48
    from_tree.lock_tree_write()
56
 
    operation = cleanup.OperationWithCleanups(merge_inner)
 
49
    operation = OperationWithCleanups(merge_inner)
57
50
    operation.add_cleanup(from_tree.unlock)
58
51
    operation.run_simple(from_tree.branch, to_tree, from_tree,
59
52
        ignore_zero=True, interesting_ids=interesting_ids, this_tree=from_tree)
62
55
class MergeHooks(hooks.Hooks):
63
56
 
64
57
    def __init__(self):
65
 
        hooks.Hooks.__init__(self, "bzrlib.merge", "Merger.hooks")
66
 
        self.add_hook('merge_file_content',
 
58
        hooks.Hooks.__init__(self)
 
59
        self.create_hook(hooks.HookPoint('merge_file_content',
67
60
            "Called with a bzrlib.merge.Merger object to create a per file "
68
61
            "merge object when starting a merge. "
69
62
            "Should return either None or a subclass of "
73
66
            "side has deleted the file and the other has changed it). "
74
67
            "See the AbstractPerFileMerger API docs for details on how it is "
75
68
            "used by merge.",
76
 
            (2, 1))
 
69
            (2, 1), None))
77
70
 
78
71
 
79
72
class AbstractPerFileMerger(object):
92
85
        """Attempt to merge the contents of a single file.
93
86
        
94
87
        :param merge_params: A bzrlib.merge.MergeHookParams
95
 
        :return: A tuple of (status, chunks), where status is one of
 
88
        :return : A tuple of (status, chunks), where status is one of
96
89
            'not_applicable', 'success', 'conflicted', or 'delete'.  If status
97
90
            is 'success' or 'conflicted', then chunks should be an iterable of
98
91
            strings for the new file contents.
100
93
        return ('not applicable', None)
101
94
 
102
95
 
103
 
class PerFileMerger(AbstractPerFileMerger):
104
 
    """Merge individual files when self.file_matches returns True.
105
 
 
106
 
    This class is intended to be subclassed.  The file_matches and
107
 
    merge_matching methods should be overridden with concrete implementations.
108
 
    """
109
 
 
110
 
    def file_matches(self, params):
111
 
        """Return True if merge_matching should be called on this file.
112
 
 
113
 
        Only called with merges of plain files with no clear winner.
114
 
 
115
 
        Subclasses must override this.
116
 
        """
117
 
        raise NotImplementedError(self.file_matches)
118
 
 
119
 
    def get_filename(self, params, tree):
120
 
        """Lookup the filename (i.e. basename, not path), given a Tree (e.g.
121
 
        self.merger.this_tree) and a MergeHookParams.
122
 
        """
123
 
        return osutils.basename(tree.id2path(params.file_id))
124
 
 
125
 
    def get_filepath(self, params, tree):
126
 
        """Calculate the path to the file in a tree.
127
 
 
128
 
        :param params: A MergeHookParams describing the file to merge
129
 
        :param tree: a Tree, e.g. self.merger.this_tree.
130
 
        """
131
 
        return tree.id2path(params.file_id)
132
 
 
133
 
    def merge_contents(self, params):
134
 
        """Merge the contents of a single file."""
135
 
        # Check whether this custom merge logic should be used.
136
 
        if (
137
 
            # OTHER is a straight winner, rely on default merge.
138
 
            params.winner == 'other' or
139
 
            # THIS and OTHER aren't both files.
140
 
            not params.is_file_merge() or
141
 
            # The filename doesn't match *.xml
142
 
            not self.file_matches(params)):
143
 
            return 'not_applicable', None
144
 
        return self.merge_matching(params)
145
 
 
146
 
    def merge_matching(self, params):
147
 
        """Merge the contents of a single file that has matched the criteria
148
 
        in PerFileMerger.merge_contents (is a conflict, is a file,
149
 
        self.file_matches is True).
150
 
 
151
 
        Subclasses must override this.
152
 
        """
153
 
        raise NotImplementedError(self.merge_matching)
154
 
 
155
 
 
156
 
class ConfigurableFileMerger(PerFileMerger):
 
96
class ConfigurableFileMerger(AbstractPerFileMerger):
157
97
    """Merge individual files when configured via a .conf file.
158
98
 
159
99
    This is a base class for concrete custom file merging logic. Concrete
182
122
        if self.name_prefix is None:
183
123
            raise ValueError("name_prefix must be set.")
184
124
 
185
 
    def file_matches(self, params):
 
125
    def filename_matches_config(self, params):
186
126
        """Check whether the file should call the merge hook.
187
127
 
188
128
        <name_prefix>_merge_files configuration variable is a list of files
202
142
                affected_files = self.default_files
203
143
            self.affected_files = affected_files
204
144
        if affected_files:
205
 
            filepath = self.get_filepath(params, self.merger.this_tree)
206
 
            if filepath in affected_files:
 
145
            filename = self.merger.this_tree.id2path(params.file_id)
 
146
            if filename in affected_files:
207
147
                return True
208
148
        return False
209
149
 
210
 
    def merge_matching(self, params):
 
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
211
163
        return self.merge_text(params)
212
164
 
213
165
    def merge_text(self, params):
428
380
        return self._cached_trees[revision_id]
429
381
 
430
382
    def _get_tree(self, treespec, possible_transports=None):
 
383
        from bzrlib import workingtree
431
384
        location, revno = treespec
432
385
        if revno is None:
433
386
            tree = workingtree.WorkingTree.open_containing(location)[0]
458
411
    @deprecated_method(deprecated_in((2, 1, 0)))
459
412
    def file_revisions(self, file_id):
460
413
        self.ensure_revision_trees()
 
414
        def get_id(tree, file_id):
 
415
            revision_id = tree.inventory[file_id].revision
 
416
            return revision_id
461
417
        if self.this_rev_id is None:
462
418
            if self.this_basis_tree.get_file_sha1(file_id) != \
463
419
                self.this_tree.get_file_sha1(file_id):
464
420
                raise errors.WorkingTreeNotRevision(self.this_tree)
465
421
 
466
422
        trees = (self.this_basis_tree, self.other_tree)
467
 
        return [tree.get_file_revision(file_id) for tree in trees]
 
423
        return [get_id(tree, file_id) for tree in trees]
468
424
 
469
425
    @deprecated_method(deprecated_in((2, 1, 0)))
470
426
    def check_basis(self, check_clean, require_commits=True):
498
454
    def _add_parent(self):
499
455
        new_parents = self.this_tree.get_parent_ids() + [self.other_rev_id]
500
456
        new_parent_trees = []
501
 
        operation = cleanup.OperationWithCleanups(
502
 
            self.this_tree.set_parent_trees)
 
457
        operation = OperationWithCleanups(self.this_tree.set_parent_trees)
503
458
        for revision_id in new_parents:
504
459
            try:
505
460
                tree = self.revision_tree(revision_id)
580
535
            elif len(lcas) == 1:
581
536
                self.base_rev_id = list(lcas)[0]
582
537
            else: # len(lcas) > 1
583
 
                self._is_criss_cross = True
584
538
                if len(lcas) > 2:
585
539
                    # find_unique_lca can only handle 2 nodes, so we have to
586
540
                    # start back at the beginning. It is a shame to traverse
591
545
                else:
592
546
                    self.base_rev_id = self.revision_graph.find_unique_lca(
593
547
                                            *lcas)
594
 
                sorted_lca_keys = self.revision_graph.find_merge_order(
595
 
                    revisions[0], lcas)
596
 
                if self.base_rev_id == _mod_revision.NULL_REVISION:
597
 
                    self.base_rev_id = sorted_lca_keys[0]
598
 
 
 
548
                self._is_criss_cross = True
599
549
            if self.base_rev_id == _mod_revision.NULL_REVISION:
600
550
                raise errors.UnrelatedBranches()
601
551
            if self._is_criss_cross:
602
552
                trace.warning('Warning: criss-cross merge encountered.  See bzr'
603
553
                              ' help criss-cross.')
604
554
                trace.mutter('Criss-cross lcas: %r' % lcas)
605
 
                if self.base_rev_id in lcas:
606
 
                    trace.mutter('Unable to find unique lca. '
607
 
                                 'Fallback %r as best option.'
608
 
                                 % self.base_rev_id)
609
 
                interesting_revision_ids = set(lcas)
610
 
                interesting_revision_ids.add(self.base_rev_id)
 
555
                interesting_revision_ids = [self.base_rev_id]
 
556
                interesting_revision_ids.extend(lcas)
611
557
                interesting_trees = dict((t.get_revision_id(), t)
612
558
                    for t in self.this_branch.repository.revision_trees(
613
559
                        interesting_revision_ids))
614
560
                self._cached_trees.update(interesting_trees)
615
 
                if self.base_rev_id in lcas:
616
 
                    self.base_tree = interesting_trees[self.base_rev_id]
617
 
                else:
618
 
                    self.base_tree = interesting_trees.pop(self.base_rev_id)
 
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)
619
564
                self._lca_trees = [interesting_trees[key]
620
565
                                   for key in sorted_lca_keys]
621
566
            else:
690
635
                    continue
691
636
                sub_merge = Merger(sub_tree.branch, this_tree=sub_tree)
692
637
                sub_merge.merge_type = self.merge_type
693
 
                other_branch = self.other_branch.reference_parent(file_id,
694
 
                                                                  relpath)
 
638
                other_branch = self.other_branch.reference_parent(file_id, relpath)
695
639
                sub_merge.set_other_revision(other_revision, other_branch)
696
640
                base_revision = self.base_tree.get_reference_revision(file_id)
697
641
                sub_merge.base_tree = \
701
645
        return merge
702
646
 
703
647
    def do_merge(self):
704
 
        operation = cleanup.OperationWithCleanups(self._do_merge_to)
 
648
        operation = OperationWithCleanups(self._do_merge_to)
705
649
        self.this_tree.lock_tree_write()
706
650
        operation.add_cleanup(self.this_tree.unlock)
707
651
        if self.base_tree is not None:
760
704
        :param this_tree: The local tree in the merge operation
761
705
        :param base_tree: The common tree in the merge operation
762
706
        :param other_tree: The other tree to merge changes from
763
 
        :param this_branch: The branch associated with this_tree.  Defaults to
764
 
            this_tree.branch if not supplied.
 
707
        :param this_branch: The branch associated with this_tree
765
708
        :param interesting_ids: The file_ids of files that should be
766
709
            participate in the merge.  May not be combined with
767
710
            interesting_files.
785
728
        if interesting_files is not None and interesting_ids is not None:
786
729
            raise ValueError(
787
730
                'specify either interesting_ids or interesting_files')
788
 
        if this_branch is None:
789
 
            this_branch = this_tree.branch
790
731
        self.interesting_ids = interesting_ids
791
732
        self.interesting_files = interesting_files
792
733
        self.this_tree = working_tree
813
754
            warnings.warn("pb argument to Merge3Merger is deprecated")
814
755
 
815
756
    def do_merge(self):
816
 
        operation = cleanup.OperationWithCleanups(self._do_merge)
 
757
        operation = OperationWithCleanups(self._do_merge)
817
758
        self.this_tree.lock_tree_write()
818
759
        operation.add_cleanup(self.this_tree.unlock)
819
760
        self.base_tree.lock_read()
834
775
            pass
835
776
 
836
777
    def make_preview_transform(self):
837
 
        operation = cleanup.OperationWithCleanups(self._make_preview_transform)
 
778
        operation = OperationWithCleanups(self._make_preview_transform)
838
779
        self.base_tree.lock_read()
839
780
        operation.add_cleanup(self.base_tree.unlock)
840
781
        self.other_tree.lock_read()
870
811
                    executable3, file_status, resolver=resolver)
871
812
        finally:
872
813
            child_pb.finished()
873
 
        self.tt.fixup_new_roots()
874
 
        self._finish_computing_transform()
875
 
 
876
 
    def _finish_computing_transform(self):
877
 
        """Finalize the transform and report the changes.
878
 
 
879
 
        This is the second half of _compute_transform.
880
 
        """
 
814
        self.fix_root()
881
815
        child_pb = ui.ui_factory.nested_progress_bar()
882
816
        try:
883
817
            fs_conflicts = transform.resolve_conflicts(self.tt, child_pb,
890
824
                self.tt.iter_changes(), self.change_reporter)
891
825
        self.cook_conflicts(fs_conflicts)
892
826
        for conflict in self.cooked_conflicts:
893
 
            trace.warning(unicode(conflict))
 
827
            trace.warning(conflict)
894
828
 
895
829
    def _entries3(self):
896
830
        """Gather data about files modified between three trees.
903
837
        """
904
838
        result = []
905
839
        iterator = self.other_tree.iter_changes(self.base_tree,
906
 
                specific_files=self.interesting_files,
 
840
                include_unchanged=True, specific_files=self.interesting_files,
907
841
                extra_trees=[self.this_tree])
908
842
        this_entries = dict((e.file_id, e) for p, e in
909
843
                            self.this_tree.iter_entries_by_dir(
935
869
        it then compares with THIS and BASE.
936
870
 
937
871
        For the multi-valued entries, the format will be (BASE, [lca1, lca2])
938
 
 
939
 
        :return: [(file_id, changed, parents, names, executable)], where:
940
 
 
941
 
            * file_id: Simple file_id of the entry
942
 
            * changed: Boolean, True if the kind or contents changed else False
943
 
            * parents: ((base, [parent_id, in, lcas]), parent_id_other,
944
 
                        parent_id_this)
945
 
            * names:   ((base, [name, in, lcas]), name_in_other, name_in_this)
946
 
            * executable: ((base, [exec, in, lcas]), exec_in_other,
947
 
                        exec_in_this)
 
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)
948
880
        """
949
881
        if self.interesting_files is not None:
950
882
            lookup_trees = [self.this_tree, self.base_tree]
992
924
                else:
993
925
                    lca_entries.append(lca_ie)
994
926
 
995
 
            if base_inventory.has_id(file_id):
 
927
            if file_id in base_inventory:
996
928
                base_ie = base_inventory[file_id]
997
929
            else:
998
930
                base_ie = _none_entry
999
931
 
1000
 
            if this_inventory.has_id(file_id):
 
932
            if file_id in this_inventory:
1001
933
                this_ie = this_inventory[file_id]
1002
934
            else:
1003
935
                this_ie = _none_entry
1095
1027
                          ))
1096
1028
        return result
1097
1029
 
1098
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1030
 
1099
1031
    def fix_root(self):
1100
 
        if self.tt.final_kind(self.tt.root) is None:
 
1032
        try:
 
1033
            self.tt.final_kind(self.tt.root)
 
1034
        except errors.NoSuchFile:
1101
1035
            self.tt.cancel_deletion(self.tt.root)
1102
1036
        if self.tt.final_file_id(self.tt.root) is None:
1103
1037
            self.tt.version_file(self.tt.tree_file_id(self.tt.root),
1108
1042
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1109
1043
        if other_root == self.tt.root:
1110
1044
            return
1111
 
        if self.this_tree.inventory.has_id(
1112
 
            self.other_tree.inventory.root.file_id):
1113
 
            # the other tree's root is a non-root in the current tree (as
1114
 
            # when a previously unrelated branch is merged into another)
1115
 
            return
1116
 
        if self.tt.final_kind(other_root) is not None:
1117
 
            other_root_is_present = True
1118
 
        else:
1119
 
            # other_root doesn't have a physical representation. We still need
1120
 
            # to move any references to the actual root of the tree.
1121
 
            other_root_is_present = False
1122
 
        # 'other_tree.inventory.root' is not present in this tree. We are
1123
 
        # calling adjust_path for children which *want* to be present with a
1124
 
        # correct place to go.
1125
 
        for _, child in self.other_tree.inventory.root.children.iteritems():
 
1045
        try:
 
1046
            self.tt.final_kind(other_root)
 
1047
        except errors.NoSuchFile:
 
1048
            return
 
1049
        if self.this_tree.has_id(self.other_tree.inventory.root.file_id):
 
1050
            # the other tree's root is a non-root in the current tree
 
1051
            return
 
1052
        self.reparent_children(self.other_tree.inventory.root, self.tt.root)
 
1053
        self.tt.cancel_creation(other_root)
 
1054
        self.tt.cancel_versioning(other_root)
 
1055
 
 
1056
    def reparent_children(self, ie, target):
 
1057
        for thing, child in ie.children.iteritems():
1126
1058
            trans_id = self.tt.trans_id_file_id(child.file_id)
1127
 
            if not other_root_is_present:
1128
 
                if self.tt.final_kind(trans_id) is not None:
1129
 
                    # The item exist in the final tree and has a defined place
1130
 
                    # to go already.
1131
 
                    continue
1132
 
            # Move the item into the root
1133
 
            try:
1134
 
                final_name = self.tt.final_name(trans_id)
1135
 
            except errors.NoFinalPath:
1136
 
                # This file is not present anymore, ignore it.
1137
 
                continue
1138
 
            self.tt.adjust_path(final_name, self.tt.root, trans_id)
1139
 
        if other_root_is_present:
1140
 
            self.tt.cancel_creation(other_root)
1141
 
            self.tt.cancel_versioning(other_root)
 
1059
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
1142
1060
 
1143
1061
    def write_modified(self, results):
1144
1062
        modified_hashes = {}
1169
1087
    @staticmethod
1170
1088
    def contents_sha1(tree, file_id):
1171
1089
        """Determine the sha1 of the file contents (used as a key method)."""
1172
 
        if not tree.has_id(file_id):
 
1090
        if file_id not in tree:
1173
1091
            return None
1174
1092
        return tree.get_file_sha1(file_id)
1175
1093
 
1320
1238
            self._raw_conflicts.append(('path conflict', trans_id, file_id,
1321
1239
                                        this_parent, this_name,
1322
1240
                                        other_parent, other_name))
1323
 
        if not self.other_tree.has_id(file_id):
 
1241
        if other_name is None:
1324
1242
            # it doesn't matter whether the result was 'other' or
1325
 
            # 'conflict'-- if it has no file id, we leave it alone.
 
1243
            # 'conflict'-- if there's no 'other', we leave it alone.
1326
1244
            return
1327
1245
        parent_id = parents[self.winner_idx[parent_id_winner]]
1328
 
        name = names[self.winner_idx[name_winner]]
1329
 
        if parent_id is not None or name is not None:
 
1246
        if parent_id is not None:
1330
1247
            # if we get here, name_winner and parent_winner are set to safe
1331
1248
            # values.
1332
 
            if parent_id is None and name is not None:
1333
 
                # if parent_id is None and name is non-None, current file is
1334
 
                # the tree root.
1335
 
                if names[self.winner_idx[parent_id_winner]] != '':
1336
 
                    raise AssertionError(
1337
 
                        'File looks like a root, but named %s' %
1338
 
                        names[self.winner_idx[parent_id_winner]])
1339
 
                parent_trans_id = transform.ROOT_PARENT
1340
 
            else:
1341
 
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
1342
 
            self.tt.adjust_path(name, parent_trans_id,
 
1249
            self.tt.adjust_path(names[self.winner_idx[name_winner]],
 
1250
                                self.tt.trans_id_file_id(parent_id),
1343
1251
                                self.tt.trans_id_file_id(file_id))
1344
1252
 
1345
1253
    def _do_merge_contents(self, file_id):
1346
1254
        """Performs a merge on file_id contents."""
1347
1255
        def contents_pair(tree):
1348
 
            if not tree.has_id(file_id):
 
1256
            if file_id not in tree:
1349
1257
                return (None, None)
1350
1258
            kind = tree.kind(file_id)
1351
1259
            if kind == "file":
1389
1297
            if hook_status != 'not_applicable':
1390
1298
                # Don't try any more hooks, this one applies.
1391
1299
                break
1392
 
        # If the merge ends up replacing the content of the file, we get rid of
1393
 
        # it at the end of this method (this variable is used to track the
1394
 
        # exceptions to this rule).
1395
 
        keep_this = False
1396
1300
        result = "modified"
1397
1301
        if hook_status == 'not_applicable':
1398
 
            # No merge hook was able to resolve the situation. Two cases exist:
1399
 
            # a content conflict or a duplicate one.
 
1302
            # This is a contents conflict, because none of the available
 
1303
            # functions could merge it.
1400
1304
            result = None
1401
1305
            name = self.tt.final_name(trans_id)
1402
1306
            parent_id = self.tt.final_parent(trans_id)
1403
 
            duplicate = False
1404
 
            inhibit_content_conflict = False
1405
 
            if params.this_kind is None: # file_id is not in THIS
1406
 
                # Is the name used for a different file_id ?
1407
 
                dupe_path = self.other_tree.id2path(file_id)
1408
 
                this_id = self.this_tree.path2id(dupe_path)
1409
 
                if this_id is not None:
1410
 
                    # Two entries for the same path
1411
 
                    keep_this = True
1412
 
                    # versioning the merged file will trigger a duplicate
1413
 
                    # conflict
1414
 
                    self.tt.version_file(file_id, trans_id)
1415
 
                    transform.create_from_tree(
1416
 
                        self.tt, trans_id, self.other_tree, file_id,
1417
 
                        filter_tree_path=self._get_filter_tree_path(file_id))
1418
 
                    inhibit_content_conflict = True
1419
 
            elif params.other_kind is None: # file_id is not in OTHER
1420
 
                # Is the name used for a different file_id ?
1421
 
                dupe_path = self.this_tree.id2path(file_id)
1422
 
                other_id = self.other_tree.path2id(dupe_path)
1423
 
                if other_id is not None:
1424
 
                    # Two entries for the same path again, but here, the other
1425
 
                    # entry will also be merged.  We simply inhibit the
1426
 
                    # 'content' conflict creation because we know OTHER will
1427
 
                    # create (or has already created depending on ordering) an
1428
 
                    # entry at the same path. This will trigger a 'duplicate'
1429
 
                    # conflict later.
1430
 
                    keep_this = True
1431
 
                    inhibit_content_conflict = True
1432
 
            if not inhibit_content_conflict:
1433
 
                if params.this_kind is not None:
1434
 
                    self.tt.unversion_file(trans_id)
1435
 
                # This is a contents conflict, because none of the available
1436
 
                # functions could merge it.
1437
 
                file_group = self._dump_conflicts(name, parent_id, file_id,
1438
 
                                                  set_version=True)
1439
 
                self._raw_conflicts.append(('contents conflict', file_group))
 
1307
            if self.this_tree.has_id(file_id):
 
1308
                self.tt.unversion_file(trans_id)
 
1309
            file_group = self._dump_conflicts(name, parent_id, file_id,
 
1310
                                              set_version=True)
 
1311
            self._raw_conflicts.append(('contents conflict', file_group))
1440
1312
        elif hook_status == 'success':
1441
1313
            self.tt.create_file(lines, trans_id)
1442
1314
        elif hook_status == 'conflicted':
1458
1330
            raise AssertionError('unknown hook_status: %r' % (hook_status,))
1459
1331
        if not self.this_tree.has_id(file_id) and result == "modified":
1460
1332
            self.tt.version_file(file_id, trans_id)
1461
 
        if not keep_this:
1462
 
            # The merge has been performed and produced a new content, so the
1463
 
            # old contents should not be retained.
 
1333
        # The merge has been performed, so the old contents should not be
 
1334
        # retained.
 
1335
        try:
1464
1336
            self.tt.delete_contents(trans_id)
 
1337
        except errors.NoSuchFile:
 
1338
            pass
1465
1339
        return result
1466
1340
 
1467
1341
    def _default_other_winner_merge(self, merge_hook_params):
1468
1342
        """Replace this contents with other."""
1469
1343
        file_id = merge_hook_params.file_id
1470
1344
        trans_id = merge_hook_params.trans_id
 
1345
        file_in_this = self.this_tree.has_id(file_id)
1471
1346
        if self.other_tree.has_id(file_id):
1472
1347
            # OTHER changed the file
1473
 
            transform.create_from_tree(
1474
 
                self.tt, trans_id, self.other_tree, file_id,
1475
 
                filter_tree_path=self._get_filter_tree_path(file_id))
 
1348
            wt = self.this_tree
 
1349
            if wt.supports_content_filtering():
 
1350
                # We get the path from the working tree if it exists.
 
1351
                # That fails though when OTHER is adding a file, so
 
1352
                # we fall back to the other tree to find the path if
 
1353
                # it doesn't exist locally.
 
1354
                try:
 
1355
                    filter_tree_path = wt.id2path(file_id)
 
1356
                except errors.NoSuchId:
 
1357
                    filter_tree_path = self.other_tree.id2path(file_id)
 
1358
            else:
 
1359
                # Skip the id2path lookup for older formats
 
1360
                filter_tree_path = None
 
1361
            transform.create_from_tree(self.tt, trans_id,
 
1362
                             self.other_tree, file_id,
 
1363
                             filter_tree_path=filter_tree_path)
1476
1364
            return 'done', None
1477
 
        elif self.this_tree.has_id(file_id):
 
1365
        elif file_in_this:
1478
1366
            # OTHER deleted the file
1479
1367
            return 'delete', None
1480
1368
        else:
1506
1394
    def get_lines(self, tree, file_id):
1507
1395
        """Return the lines in a file, or an empty list."""
1508
1396
        if tree.has_id(file_id):
1509
 
            return tree.get_file_lines(file_id)
 
1397
            return tree.get_file(file_id).readlines()
1510
1398
        else:
1511
1399
            return []
1512
1400
 
1554
1442
                                              other_lines)
1555
1443
            file_group.append(trans_id)
1556
1444
 
1557
 
 
1558
 
    def _get_filter_tree_path(self, file_id):
1559
 
        if self.this_tree.supports_content_filtering():
1560
 
            # We get the path from the working tree if it exists.
1561
 
            # That fails though when OTHER is adding a file, so
1562
 
            # we fall back to the other tree to find the path if
1563
 
            # it doesn't exist locally.
1564
 
            try:
1565
 
                return self.this_tree.id2path(file_id)
1566
 
            except errors.NoSuchId:
1567
 
                return self.other_tree.id2path(file_id)
1568
 
        # Skip the id2path lookup for older formats
1569
 
        return None
1570
 
 
1571
1445
    def _dump_conflicts(self, name, parent_id, file_id, this_lines=None,
1572
1446
                        base_lines=None, other_lines=None, set_version=False,
1573
1447
                        no_base=False):
1639
1513
        if winner == 'this' and file_status != "modified":
1640
1514
            return
1641
1515
        trans_id = self.tt.trans_id_file_id(file_id)
1642
 
        if self.tt.final_kind(trans_id) != "file":
 
1516
        try:
 
1517
            if self.tt.final_kind(trans_id) != "file":
 
1518
                return
 
1519
        except errors.NoSuchFile:
1643
1520
            return
1644
1521
        if winner == "this":
1645
1522
            executability = this_executable
1656
1533
 
1657
1534
    def cook_conflicts(self, fs_conflicts):
1658
1535
        """Convert all conflicts into a form that doesn't depend on trans_id"""
1659
 
        content_conflict_file_ids = set()
1660
 
        cooked_conflicts = transform.cook_conflicts(fs_conflicts, self.tt)
 
1536
        self.cooked_conflicts.extend(transform.cook_conflicts(
 
1537
                fs_conflicts, self.tt))
1661
1538
        fp = transform.FinalPaths(self.tt)
1662
1539
        for conflict in self._raw_conflicts:
1663
1540
            conflict_type = conflict[0]
1674
1551
                if other_parent is None or other_name is None:
1675
1552
                    other_path = '<deleted>'
1676
1553
                else:
1677
 
                    if other_parent == self.other_tree.get_root_id():
1678
 
                        # The tree transform doesn't know about the other root,
1679
 
                        # so we special case here to avoid a NoFinalPath
1680
 
                        # exception
1681
 
                        parent_path = ''
1682
 
                    else:
1683
 
                        parent_path =  fp.get_path(
1684
 
                            self.tt.trans_id_file_id(other_parent))
 
1554
                    parent_path =  fp.get_path(
 
1555
                        self.tt.trans_id_file_id(other_parent))
1685
1556
                    other_path = osutils.pathjoin(parent_path, other_name)
1686
1557
                c = _mod_conflicts.Conflict.factory(
1687
1558
                    'path conflict', path=this_path,
1691
1562
                for trans_id in conflict[1]:
1692
1563
                    file_id = self.tt.final_file_id(trans_id)
1693
1564
                    if file_id is not None:
1694
 
                        # Ok we found the relevant file-id
1695
1565
                        break
1696
1566
                path = fp.get_path(trans_id)
1697
1567
                for suffix in ('.BASE', '.THIS', '.OTHER'):
1698
1568
                    if path.endswith(suffix):
1699
 
                        # Here is the raw path
1700
1569
                        path = path[:-len(suffix)]
1701
1570
                        break
1702
1571
                c = _mod_conflicts.Conflict.factory(conflict_type,
1703
1572
                                                    path=path, file_id=file_id)
1704
 
                content_conflict_file_ids.add(file_id)
1705
1573
            elif conflict_type == 'text conflict':
1706
1574
                trans_id = conflict[1]
1707
1575
                path = fp.get_path(trans_id)
1710
1578
                                                    path=path, file_id=file_id)
1711
1579
            else:
1712
1580
                raise AssertionError('bad conflict type: %r' % (conflict,))
1713
 
            cooked_conflicts.append(c)
1714
 
 
1715
 
        self.cooked_conflicts = []
1716
 
        # We want to get rid of path conflicts when a corresponding contents
1717
 
        # conflict exists. This can occur when one branch deletes a file while
1718
 
        # the other renames *and* modifies it. In this case, the content
1719
 
        # conflict is enough.
1720
 
        for c in cooked_conflicts:
1721
 
            if (c.typestring == 'path conflict'
1722
 
                and c.file_id in content_conflict_file_ids):
1723
 
                continue
1724
1581
            self.cooked_conflicts.append(c)
1725
1582
        self.cooked_conflicts.sort(key=_mod_conflicts.Conflict.sort_key)
1726
1583
 
1832
1689
            osutils.rmtree(temp_dir)
1833
1690
 
1834
1691
 
1835
 
class PathNotInTree(errors.BzrError):
1836
 
 
1837
 
    _fmt = """Merge-into failed because %(tree)s does not contain %(path)s."""
1838
 
 
1839
 
    def __init__(self, path, tree):
1840
 
        errors.BzrError.__init__(self, path=path, tree=tree)
1841
 
 
1842
 
 
1843
 
class MergeIntoMerger(Merger):
1844
 
    """Merger that understands other_tree will be merged into a subdir.
1845
 
 
1846
 
    This also changes the Merger api so that it uses real Branch, revision_id,
1847
 
    and RevisonTree objects, rather than using revision specs.
1848
 
    """
1849
 
 
1850
 
    def __init__(self, this_tree, other_branch, other_tree, target_subdir,
1851
 
            source_subpath, other_rev_id=None):
1852
 
        """Create a new MergeIntoMerger object.
1853
 
 
1854
 
        source_subpath in other_tree will be effectively copied to
1855
 
        target_subdir in this_tree.
1856
 
 
1857
 
        :param this_tree: The tree that we will be merging into.
1858
 
        :param other_branch: The Branch we will be merging from.
1859
 
        :param other_tree: The RevisionTree object we want to merge.
1860
 
        :param target_subdir: The relative path where we want to merge
1861
 
            other_tree into this_tree
1862
 
        :param source_subpath: The relative path specifying the subtree of
1863
 
            other_tree to merge into this_tree.
1864
 
        """
1865
 
        # It is assumed that we are merging a tree that is not in our current
1866
 
        # ancestry, which means we are using the "EmptyTree" as our basis.
1867
 
        null_ancestor_tree = this_tree.branch.repository.revision_tree(
1868
 
                                _mod_revision.NULL_REVISION)
1869
 
        super(MergeIntoMerger, self).__init__(
1870
 
            this_branch=this_tree.branch,
1871
 
            this_tree=this_tree,
1872
 
            other_tree=other_tree,
1873
 
            base_tree=null_ancestor_tree,
1874
 
            )
1875
 
        self._target_subdir = target_subdir
1876
 
        self._source_subpath = source_subpath
1877
 
        self.other_branch = other_branch
1878
 
        if other_rev_id is None:
1879
 
            other_rev_id = other_tree.get_revision_id()
1880
 
        self.other_rev_id = self.other_basis = other_rev_id
1881
 
        self.base_is_ancestor = True
1882
 
        self.backup_files = True
1883
 
        self.merge_type = Merge3Merger
1884
 
        self.show_base = False
1885
 
        self.reprocess = False
1886
 
        self.interesting_ids = None
1887
 
        self.merge_type = _MergeTypeParameterizer(MergeIntoMergeType,
1888
 
              target_subdir=self._target_subdir,
1889
 
              source_subpath=self._source_subpath)
1890
 
        if self._source_subpath != '':
1891
 
            # If this isn't a partial merge make sure the revisions will be
1892
 
            # present.
1893
 
            self._maybe_fetch(self.other_branch, self.this_branch,
1894
 
                self.other_basis)
1895
 
 
1896
 
    def set_pending(self):
1897
 
        if self._source_subpath != '':
1898
 
            return
1899
 
        Merger.set_pending(self)
1900
 
 
1901
 
 
1902
 
class _MergeTypeParameterizer(object):
1903
 
    """Wrap a merge-type class to provide extra parameters.
1904
 
    
1905
 
    This is hack used by MergeIntoMerger to pass some extra parameters to its
1906
 
    merge_type.  Merger.do_merge() sets up its own set of parameters to pass to
1907
 
    the 'merge_type' member.  It is difficult override do_merge without
1908
 
    re-writing the whole thing, so instead we create a wrapper which will pass
1909
 
    the extra parameters.
1910
 
    """
1911
 
 
1912
 
    def __init__(self, merge_type, **kwargs):
1913
 
        self._extra_kwargs = kwargs
1914
 
        self._merge_type = merge_type
1915
 
 
1916
 
    def __call__(self, *args, **kwargs):
1917
 
        kwargs.update(self._extra_kwargs)
1918
 
        return self._merge_type(*args, **kwargs)
1919
 
 
1920
 
    def __getattr__(self, name):
1921
 
        return getattr(self._merge_type, name)
1922
 
 
1923
 
 
1924
 
class MergeIntoMergeType(Merge3Merger):
1925
 
    """Merger that incorporates a tree (or part of a tree) into another."""
1926
 
 
1927
 
    def __init__(self, *args, **kwargs):
1928
 
        """Initialize the merger object.
1929
 
 
1930
 
        :param args: See Merge3Merger.__init__'s args.
1931
 
        :param kwargs: See Merge3Merger.__init__'s keyword args, except for
1932
 
            source_subpath and target_subdir.
1933
 
        :keyword source_subpath: The relative path specifying the subtree of
1934
 
            other_tree to merge into this_tree.
1935
 
        :keyword target_subdir: The relative path where we want to merge
1936
 
            other_tree into this_tree
1937
 
        """
1938
 
        # All of the interesting work happens during Merge3Merger.__init__(),
1939
 
        # so we have have to hack in to get our extra parameters set.
1940
 
        self._source_subpath = kwargs.pop('source_subpath')
1941
 
        self._target_subdir = kwargs.pop('target_subdir')
1942
 
        super(MergeIntoMergeType, self).__init__(*args, **kwargs)
1943
 
 
1944
 
    def _compute_transform(self):
1945
 
        child_pb = ui.ui_factory.nested_progress_bar()
1946
 
        try:
1947
 
            entries = self._entries_to_incorporate()
1948
 
            entries = list(entries)
1949
 
            for num, (entry, parent_id) in enumerate(entries):
1950
 
                child_pb.update('Preparing file merge', num, len(entries))
1951
 
                parent_trans_id = self.tt.trans_id_file_id(parent_id)
1952
 
                trans_id = transform.new_by_entry(self.tt, entry,
1953
 
                    parent_trans_id, self.other_tree)
1954
 
        finally:
1955
 
            child_pb.finished()
1956
 
        self._finish_computing_transform()
1957
 
 
1958
 
    def _entries_to_incorporate(self):
1959
 
        """Yields pairs of (inventory_entry, new_parent)."""
1960
 
        other_inv = self.other_tree.inventory
1961
 
        subdir_id = other_inv.path2id(self._source_subpath)
1962
 
        if subdir_id is None:
1963
 
            # XXX: The error would be clearer if it gave the URL of the source
1964
 
            # branch, but we don't have a reference to that here.
1965
 
            raise PathNotInTree(self._source_subpath, "Source tree")
1966
 
        subdir = other_inv[subdir_id]
1967
 
        parent_in_target = osutils.dirname(self._target_subdir)
1968
 
        target_id = self.this_tree.inventory.path2id(parent_in_target)
1969
 
        if target_id is None:
1970
 
            raise PathNotInTree(self._target_subdir, "Target tree")
1971
 
        name_in_target = osutils.basename(self._target_subdir)
1972
 
        merge_into_root = subdir.copy()
1973
 
        merge_into_root.name = name_in_target
1974
 
        if self.this_tree.inventory.has_id(merge_into_root.file_id):
1975
 
            # Give the root a new file-id.
1976
 
            # This can happen fairly easily if the directory we are
1977
 
            # incorporating is the root, and both trees have 'TREE_ROOT' as
1978
 
            # their root_id.  Users will expect this to Just Work, so we
1979
 
            # change the file-id here.
1980
 
            # Non-root file-ids could potentially conflict too.  That's really
1981
 
            # an edge case, so we don't do anything special for those.  We let
1982
 
            # them cause conflicts.
1983
 
            merge_into_root.file_id = generate_ids.gen_file_id(name_in_target)
1984
 
        yield (merge_into_root, target_id)
1985
 
        if subdir.kind != 'directory':
1986
 
            # No children, so we are done.
1987
 
            return
1988
 
        for ignored_path, entry in other_inv.iter_entries_by_dir(subdir_id):
1989
 
            parent_id = entry.parent_id
1990
 
            if parent_id == subdir.file_id:
1991
 
                # The root's parent ID has changed, so make sure children of
1992
 
                # the root refer to the new ID.
1993
 
                parent_id = merge_into_root.file_id
1994
 
            yield (entry, parent_id)
1995
 
 
1996
 
 
1997
1692
def merge_inner(this_branch, other_tree, base_tree, ignore_zero=False,
1998
1693
                backup_files=False,
1999
1694
                merge_type=Merge3Merger,
2007
1702
                change_reporter=None):
2008
1703
    """Primary interface for merging.
2009
1704
 
2010
 
    Typical use is probably::
2011
 
 
2012
 
        merge_inner(branch, branch.get_revision_tree(other_revision),
2013
 
                    branch.get_revision_tree(base_revision))
2014
 
    """
 
1705
        typical use is probably
 
1706
        'merge_inner(branch, branch.get_revision_tree(other_revision),
 
1707
                     branch.get_revision_tree(base_revision))'
 
1708
        """
2015
1709
    if this_tree is None:
2016
1710
        raise errors.BzrError("bzrlib.merge.merge_inner requires a this_tree "
2017
 
                              "parameter")
 
1711
                              "parameter as of bzrlib version 0.8.")
2018
1712
    merger = Merger(this_branch, other_tree, base_tree, this_tree=this_tree,
2019
1713
                    pb=pb, change_reporter=change_reporter)
2020
1714
    merger.backup_files = backup_files
2474
2168
class _PlanLCAMerge(_PlanMergeBase):
2475
2169
    """
2476
2170
    This merge algorithm differs from _PlanMerge in that:
2477
 
 
2478
2171
    1. comparisons are done against LCAs only
2479
2172
    2. cases where a contested line is new versus one LCA but old versus
2480
2173
       another are marked as conflicts, by emitting the line as conflicted-a
2521
2214
 
2522
2215
        If a line is killed and new, this indicates that the two merge
2523
2216
        revisions contain differing conflict resolutions.
2524
 
 
2525
2217
        :param revision_id: The id of the revision in which the lines are
2526
2218
            unique
2527
2219
        :param unique_line_numbers: The line numbers of unique lines.
2528
 
        :return: a tuple of (new_this, killed_other)
 
2220
        :return a tuple of (new_this, killed_other):
2529
2221
        """
2530
2222
        new = set()
2531
2223
        killed = set()