~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-05-20 09:27:48 UTC
  • mfrom: (5241.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20100520092748-oa7knubtqio8l2rc
(lifeless) Merge 2.1 into trunk with a number of fixes including pyrex 0.9.9
 support. (Robert Collins)

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
1042
1086
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1043
1087
        if other_root == self.tt.root:
1044
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
1045
1093
        try:
1046
1094
            self.tt.final_kind(other_root)
 
1095
            other_root_is_present = True
1047
1096
        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():
 
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():
1058
1104
            trans_id = self.tt.trans_id_file_id(child.file_id)
1059
 
            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)
1060
1121
 
1061
1122
    def write_modified(self, results):
1062
1123
        modified_hashes = {}