/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
93
93
        return ('not applicable', None)
94
94
 
95
95
 
96
 
class ConfigurableFileMerger(AbstractPerFileMerger):
 
96
class PerFileMerger(AbstractPerFileMerger):
 
97
    """Merge individual files when self.file_matches returns True.
 
98
 
 
99
    This class is intended to be subclassed.  The file_matches and
 
100
    merge_matching methods should be overridden with concrete implementations.
 
101
    """
 
102
 
 
103
    def file_matches(self, params):
 
104
        """Return True if merge_matching should be called on this file.
 
105
 
 
106
        Only called with merges of plain files with no clear winner.
 
107
 
 
108
        Subclasses must override this.
 
109
        """
 
110
        raise NotImplementedError(self.file_matches)
 
111
 
 
112
    def get_filename(self, params, tree):
 
113
        """Lookup the filename (i.e. basename, not path), given a Tree (e.g.
 
114
        self.merger.this_tree) and a MergeHookParams.
 
115
        """
 
116
        return osutils.basename(tree.id2path(params.file_id))
 
117
 
 
118
    def get_filepath(self, params, tree):
 
119
        """Calculate the path to the file in a tree.
 
120
 
 
121
        :param params: A MergeHookParams describing the file to merge
 
122
        :param tree: a Tree, e.g. self.merger.this_tree.
 
123
        """
 
124
        return tree.id2path(params.file_id)
 
125
 
 
126
    def merge_contents(self, params):
 
127
        """Merge the contents of a single file."""
 
128
        # Check whether this custom merge logic should be used.
 
129
        if (
 
130
            # OTHER is a straight winner, rely on default merge.
 
131
            params.winner == 'other' or
 
132
            # THIS and OTHER aren't both files.
 
133
            not params.is_file_merge() or
 
134
            # The filename doesn't match *.xml
 
135
            not self.file_matches(params)):
 
136
            return 'not_applicable', None
 
137
        return self.merge_matching(params)
 
138
 
 
139
    def merge_matching(self, params):
 
140
        """Merge the contents of a single file that has matched the criteria
 
141
        in PerFileMerger.merge_contents (is a conflict, is a file,
 
142
        self.file_matches is True).
 
143
 
 
144
        Subclasses must override this.
 
145
        """
 
146
        raise NotImplementedError(self.merge_matching)
 
147
 
 
148
 
 
149
class ConfigurableFileMerger(PerFileMerger):
97
150
    """Merge individual files when configured via a .conf file.
98
151
 
99
152
    This is a base class for concrete custom file merging logic. Concrete
122
175
        if self.name_prefix is None:
123
176
            raise ValueError("name_prefix must be set.")
124
177
 
125
 
    def filename_matches_config(self, params):
 
178
    def file_matches(self, params):
126
179
        """Check whether the file should call the merge hook.
127
180
 
128
181
        <name_prefix>_merge_files configuration variable is a list of files
142
195
                affected_files = self.default_files
143
196
            self.affected_files = affected_files
144
197
        if affected_files:
145
 
            filename = self.merger.this_tree.id2path(params.file_id)
146
 
            if filename in affected_files:
 
198
            filepath = self.get_filepath(params, self.merger.this_tree)
 
199
            if filepath in affected_files:
147
200
                return True
148
201
        return False
149
202
 
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
 
203
    def merge_matching(self, params):
163
204
        return self.merge_text(params)
164
205
 
165
206
    def merge_text(self, params):
704
745
        :param this_tree: The local tree in the merge operation
705
746
        :param base_tree: The common tree in the merge operation
706
747
        :param other_tree: The other tree to merge changes from
707
 
        :param this_branch: The branch associated with this_tree
 
748
        :param this_branch: The branch associated with this_tree.  Defaults to
 
749
            this_tree.branch if not supplied.
708
750
        :param interesting_ids: The file_ids of files that should be
709
751
            participate in the merge.  May not be combined with
710
752
            interesting_files.
728
770
        if interesting_files is not None and interesting_ids is not None:
729
771
            raise ValueError(
730
772
                'specify either interesting_ids or interesting_files')
 
773
        if this_branch is None:
 
774
            this_branch = this_tree.branch
731
775
        self.interesting_ids = interesting_ids
732
776
        self.interesting_files = interesting_files
733
777
        self.this_tree = working_tree
1015
1059
                        continue
1016
1060
                else:
1017
1061
                    raise AssertionError('unhandled kind: %s' % other_ie.kind)
1018
 
                # XXX: We need to handle kind == 'symlink'
1019
1062
 
1020
1063
            # If we have gotten this far, that means something has changed
1021
1064
            result.append((file_id, content_changed,
1043
1086
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1044
1087
        if other_root == self.tt.root:
1045
1088
            return
 
1089
        if self.other_tree.inventory.root.file_id in self.this_tree.inventory:
 
1090
            # the other tree's root is a non-root in the current tree (as when
 
1091
            # a previously unrelated branch is merged into another)
 
1092
            return
1046
1093
        try:
1047
1094
            self.tt.final_kind(other_root)
 
1095
            other_root_is_present = True
1048
1096
        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():
 
1097
            # other_root doesn't have a physical representation. We still need
 
1098
            # to move any references to the actual root of the tree.
 
1099
            other_root_is_present = False
 
1100
        # 'other_tree.inventory.root' is not present in this tree. We are
 
1101
        # calling adjust_path for children which *want* to be present with a
 
1102
        # correct place to go.
 
1103
        for thing, child in self.other_tree.inventory.root.children.iteritems():
1059
1104
            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)
 
1105
            if not other_root_is_present:
 
1106
                # FIXME: Make final_kind returns None instead of raising
 
1107
                # NoSuchFile to avoid the ugly construct below -- vila 20100402
 
1108
                try:
 
1109
                    self.tt.final_kind(trans_id)
 
1110
                    # The item exist in the final tree and has a defined place
 
1111
                    # to go already.
 
1112
                    continue
 
1113
                except errors.NoSuchFile, e:
 
1114
                    pass
 
1115
            # Move the item into the root
 
1116
            self.tt.adjust_path(self.tt.final_name(trans_id),
 
1117
                                self.tt.root, trans_id)
 
1118
        if other_root_is_present:
 
1119
            self.tt.cancel_creation(other_root)
 
1120
            self.tt.cancel_versioning(other_root)
1061
1121
 
1062
1122
    def write_modified(self, results):
1063
1123
        modified_hashes = {}
1110
1170
 
1111
1171
    @staticmethod
1112
1172
    def _three_way(base, other, this):
1113
 
        #if base == other, either they all agree, or only THIS has changed.
1114
1173
        if base == other:
 
1174
            # if 'base == other', either they all agree, or only 'this' has
 
1175
            # changed.
1115
1176
            return 'this'
1116
1177
        elif this not in (base, other):
 
1178
            # 'this' is neither 'base' nor 'other', so both sides changed
1117
1179
            return 'conflict'
1118
 
        # "Ambiguous clean merge" -- both sides have made the same change.
1119
1180
        elif this == other:
 
1181
            # "Ambiguous clean merge" -- both sides have made the same change.
1120
1182
            return "this"
1121
 
        # this == base: only other has changed.
1122
1183
        else:
 
1184
            # this == base: only other has changed.
1123
1185
            return "other"
1124
1186
 
1125
1187
    @staticmethod
1169
1231
                # only has an lca value
1170
1232
                return 'other'
1171
1233
 
1172
 
        # At this point, the lcas disagree, and the tips disagree
 
1234
        # At this point, the lcas disagree, and the tip disagree
1173
1235
        return 'conflict'
1174
1236
 
1175
1237
    @staticmethod