~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/merge.py

  • Committer: Martin
  • Date: 2010-04-14 00:11:32 UTC
  • mto: This revision was merged to the branch mainline in revision 5164.
  • Revision ID: gzlist@googlemail.com-20100414001132-6m1ru11mbfu87x6m
Fix os.utime test failures, three on FAT filesystems and one with readonly files

Show diffs side-by-side

added added

removed removed

Lines of Context:
93
93
        return ('not applicable', None)
94
94
 
95
95
 
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):
 
96
class ConfigurableFileMerger(AbstractPerFileMerger):
150
97
    """Merge individual files when configured via a .conf file.
151
98
 
152
99
    This is a base class for concrete custom file merging logic. Concrete
175
122
        if self.name_prefix is None:
176
123
            raise ValueError("name_prefix must be set.")
177
124
 
178
 
    def file_matches(self, params):
 
125
    def filename_matches_config(self, params):
179
126
        """Check whether the file should call the merge hook.
180
127
 
181
128
        <name_prefix>_merge_files configuration variable is a list of files
195
142
                affected_files = self.default_files
196
143
            self.affected_files = affected_files
197
144
        if affected_files:
198
 
            filepath = self.get_filepath(params, self.merger.this_tree)
199
 
            if filepath in affected_files:
 
145
            filename = self.merger.this_tree.id2path(params.file_id)
 
146
            if filename in affected_files:
200
147
                return True
201
148
        return False
202
149
 
203
 
    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
204
163
        return self.merge_text(params)
205
164
 
206
165
    def merge_text(self, params):
745
704
        :param this_tree: The local tree in the merge operation
746
705
        :param base_tree: The common tree in the merge operation
747
706
        :param other_tree: The other tree to merge changes from
748
 
        :param this_branch: The branch associated with this_tree.  Defaults to
749
 
            this_tree.branch if not supplied.
 
707
        :param this_branch: The branch associated with this_tree
750
708
        :param interesting_ids: The file_ids of files that should be
751
709
            participate in the merge.  May not be combined with
752
710
            interesting_files.
770
728
        if interesting_files is not None and interesting_ids is not None:
771
729
            raise ValueError(
772
730
                'specify either interesting_ids or interesting_files')
773
 
        if this_branch is None:
774
 
            this_branch = this_tree.branch
775
731
        self.interesting_ids = interesting_ids
776
732
        self.interesting_files = interesting_files
777
733
        self.this_tree = working_tree
1086
1042
        other_root = self.tt.trans_id_file_id(other_root_file_id)
1087
1043
        if other_root == self.tt.root:
1088
1044
            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
1093
1045
        try:
1094
1046
            self.tt.final_kind(other_root)
1095
 
            other_root_is_present = True
1096
1047
        except errors.NoSuchFile:
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():
 
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():
1104
1058
            trans_id = self.tt.trans_id_file_id(child.file_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)
 
1059
            self.tt.adjust_path(self.tt.final_name(trans_id), target, trans_id)
1121
1060
 
1122
1061
    def write_modified(self, results):
1123
1062
        modified_hashes = {}