~abentley/bzr/sha1-transform

« back to all changes in this revision

Viewing changes to bzrlib/transform.py

  • Committer: Aaron Bentley
  • Date: 2009-05-15 20:31:15 UTC
  • Revision ID: aaron@aaronbentley.com-20090515203115-45vb6dp9lm5m6k9l
Split out a DiskTreeTransform class that manages Limbo.

Show diffs side-by-side

added added

removed removed

Lines of Context:
78
78
class TreeTransformBase(object):
79
79
    """The base class for TreeTransform and TreeTransformBase"""
80
80
 
81
 
    def __init__(self, tree, limbodir, pb=DummyProgress(),
 
81
    def __init__(self, tree, pb=DummyProgress(),
82
82
                 case_sensitive=True):
83
83
        """Constructor.
84
84
 
85
85
        :param tree: The tree that will be transformed, but not necessarily
86
86
            the output tree.
87
 
        :param limbodir: A directory where new files can be stored until
88
 
            they are installed in their proper places
89
87
        :param pb: A ProgressBar indicating how much progress is being made
90
88
        :param case_sensitive: If True, the target of the transform is
91
89
            case sensitive, not just case preserving.
92
90
        """
93
91
        object.__init__(self)
94
92
        self._tree = tree
95
 
        self._limbodir = limbodir
96
 
        self._deletiondir = None
97
93
        self._id_number = 0
98
94
        # mapping of trans_id -> new basename
99
95
        self._new_name = {}
101
97
        self._new_parent = {}
102
98
        # mapping of trans_id with new contents -> new file_kind
103
99
        self._new_contents = {}
104
 
        # A mapping of transform ids to their limbo filename
105
 
        self._limbo_files = {}
106
 
        # A mapping of transform ids to a set of the transform ids of children
107
 
        # that their limbo directory has
108
 
        self._limbo_children = {}
109
 
        # Map transform ids to maps of child filename to child transform id
110
 
        self._limbo_children_names = {}
111
 
        # List of transform ids that need to be renamed from limbo into place
112
 
        self._needs_rename = set()
113
100
        # Set of trans_ids whose contents will be removed
114
101
        self._removed_contents = set()
115
102
        # Mapping of trans_id -> new execute-bit value
147
134
        # A counter of how many files have been renamed
148
135
        self.rename_count = 0
149
136
 
 
137
    def finalize(self):
 
138
        """Release the working tree lock, if held.
 
139
 
 
140
        This is required if apply has not been invoked, but can be invoked
 
141
        even after apply.
 
142
        """
 
143
        if self._tree is None:
 
144
            return
 
145
        self._tree.unlock()
 
146
        self._tree = None
 
147
 
150
148
    def __get_root(self):
151
149
        return self._new_root
152
150
 
153
151
    root = property(__get_root)
154
152
 
155
 
    def finalize(self):
156
 
        """Release the working tree lock, if held, clean up limbo dir.
157
 
 
158
 
        This is required if apply has not been invoked, but can be invoked
159
 
        even after apply.
160
 
        """
161
 
        if self._tree is None:
162
 
            return
163
 
        try:
164
 
            entries = [(self._limbo_name(t), t, k) for t, k in
165
 
                       self._new_contents.iteritems()]
166
 
            entries.sort(reverse=True)
167
 
            for path, trans_id, kind in entries:
168
 
                if kind == "directory":
169
 
                    os.rmdir(path)
170
 
                else:
171
 
                    os.unlink(path)
172
 
            try:
173
 
                os.rmdir(self._limbodir)
174
 
            except OSError:
175
 
                # We don't especially care *why* the dir is immortal.
176
 
                raise ImmortalLimbo(self._limbodir)
177
 
            try:
178
 
                if self._deletiondir is not None:
179
 
                    os.rmdir(self._deletiondir)
180
 
            except OSError:
181
 
                raise errors.ImmortalPendingDeletion(self._deletiondir)
182
 
        finally:
183
 
            self._tree.unlock()
184
 
            self._tree = None
185
 
 
186
153
    def _assign_id(self):
187
154
        """Produce a new tranform id"""
188
155
        new_id = "new-%s" % self._id_number
200
167
        """Change the path that is assigned to a transaction id."""
201
168
        if trans_id == self._new_root:
202
169
            raise CantMoveRoot
203
 
        previous_parent = self._new_parent.get(trans_id)
204
 
        previous_name = self._new_name.get(trans_id)
205
170
        self._new_name[trans_id] = name
206
171
        self._new_parent[trans_id] = parent
207
172
        if parent == ROOT_PARENT:
208
173
            if self._new_root is not None:
209
174
                raise ValueError("Cannot have multiple roots.")
210
175
            self._new_root = trans_id
211
 
        if (trans_id in self._limbo_files and
212
 
            trans_id not in self._needs_rename):
213
 
            self._rename_in_limbo([trans_id])
214
 
            self._limbo_children[previous_parent].remove(trans_id)
215
 
            del self._limbo_children_names[previous_parent][previous_name]
216
 
 
217
 
    def _rename_in_limbo(self, trans_ids):
218
 
        """Fix limbo names so that the right final path is produced.
219
 
 
220
 
        This means we outsmarted ourselves-- we tried to avoid renaming
221
 
        these files later by creating them with their final names in their
222
 
        final parents.  But now the previous name or parent is no longer
223
 
        suitable, so we have to rename them.
224
 
 
225
 
        Even for trans_ids that have no new contents, we must remove their
226
 
        entries from _limbo_files, because they are now stale.
227
 
        """
228
 
        for trans_id in trans_ids:
229
 
            old_path = self._limbo_files.pop(trans_id)
230
 
            if trans_id not in self._new_contents:
231
 
                continue
232
 
            new_path = self._limbo_name(trans_id)
233
 
            os.rename(old_path, new_path)
234
176
 
235
177
    def adjust_root_path(self, name, parent):
236
178
        """Emulate moving the root by moving all children, instead.
332
274
            return ROOT_PARENT
333
275
        return self.trans_id_tree_path(os.path.dirname(path))
334
276
 
335
 
    def create_file(self, contents, trans_id, mode_id=None):
336
 
        """Schedule creation of a new file.
337
 
 
338
 
        See also new_file.
339
 
 
340
 
        Contents is an iterator of strings, all of which will be written
341
 
        to the target destination.
342
 
 
343
 
        New file takes the permissions of any existing file with that id,
344
 
        unless mode_id is specified.
345
 
        """
346
 
        name = self._limbo_name(trans_id)
347
 
        f = open(name, 'wb')
348
 
        try:
349
 
            try:
350
 
                unique_add(self._new_contents, trans_id, 'file')
351
 
            except:
352
 
                # Clean up the file, it never got registered so
353
 
                # TreeTransform.finalize() won't clean it up.
354
 
                f.close()
355
 
                os.unlink(name)
356
 
                raise
357
 
 
358
 
            f.writelines(contents)
359
 
        finally:
360
 
            f.close()
361
 
        self._set_mode(trans_id, mode_id, S_ISREG)
362
 
 
363
 
    def _set_mode(self, trans_id, mode_id, typefunc):
364
 
        """Set the mode of new file contents.
365
 
        The mode_id is the existing file to get the mode from (often the same
366
 
        as trans_id).  The operation is only performed if there's a mode match
367
 
        according to typefunc.
368
 
        """
369
 
        if mode_id is None:
370
 
            mode_id = trans_id
371
 
        try:
372
 
            old_path = self._tree_id_paths[mode_id]
373
 
        except KeyError:
374
 
            return
375
 
        try:
376
 
            mode = os.stat(self._tree.abspath(old_path)).st_mode
377
 
        except OSError, e:
378
 
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
379
 
                # Either old_path doesn't exist, or the parent of the
380
 
                # target is not a directory (but will be one eventually)
381
 
                # Either way, we know it doesn't exist *right now*
382
 
                # See also bug #248448
383
 
                return
384
 
            else:
385
 
                raise
386
 
        if typefunc(mode):
387
 
            os.chmod(self._limbo_name(trans_id), mode)
388
 
 
389
 
    def create_hardlink(self, path, trans_id):
390
 
        """Schedule creation of a hard link"""
391
 
        name = self._limbo_name(trans_id)
392
 
        try:
393
 
            os.link(path, name)
394
 
        except OSError, e:
395
 
            if e.errno != errno.EPERM:
396
 
                raise
397
 
            raise errors.HardLinkNotSupported(path)
398
 
        try:
399
 
            unique_add(self._new_contents, trans_id, 'file')
400
 
        except:
401
 
            # Clean up the file, it never got registered so
402
 
            # TreeTransform.finalize() won't clean it up.
403
 
            os.unlink(name)
404
 
            raise
405
 
 
406
 
    def create_directory(self, trans_id):
407
 
        """Schedule creation of a new directory.
408
 
 
409
 
        See also new_directory.
410
 
        """
411
 
        os.mkdir(self._limbo_name(trans_id))
412
 
        unique_add(self._new_contents, trans_id, 'directory')
413
 
 
414
 
    def create_symlink(self, target, trans_id):
415
 
        """Schedule creation of a new symbolic link.
416
 
 
417
 
        target is a bytestring.
418
 
        See also new_symlink.
419
 
        """
420
 
        if has_symlinks():
421
 
            os.symlink(target, self._limbo_name(trans_id))
422
 
            unique_add(self._new_contents, trans_id, 'symlink')
423
 
        else:
424
 
            try:
425
 
                path = FinalPaths(self).get_path(trans_id)
426
 
            except KeyError:
427
 
                path = None
428
 
            raise UnableCreateSymlink(path=path)
429
 
 
430
 
    def cancel_creation(self, trans_id):
431
 
        """Cancel the creation of new file contents."""
432
 
        del self._new_contents[trans_id]
433
 
        children = self._limbo_children.get(trans_id)
434
 
        # if this is a limbo directory with children, move them before removing
435
 
        # the directory
436
 
        if children is not None:
437
 
            self._rename_in_limbo(children)
438
 
            del self._limbo_children[trans_id]
439
 
            del self._limbo_children_names[trans_id]
440
 
        delete_any(self._limbo_name(trans_id))
441
 
 
442
277
    def delete_contents(self, trans_id):
443
278
        """Schedule the contents of a path entry for deletion"""
444
279
        self.tree_kind(trans_id)
867
702
            return True
868
703
        return False
869
704
 
870
 
    def _limbo_name(self, trans_id):
871
 
        """Generate the limbo name of a file"""
872
 
        limbo_name = self._limbo_files.get(trans_id)
873
 
        if limbo_name is not None:
874
 
            return limbo_name
875
 
        parent = self._new_parent.get(trans_id)
876
 
        # if the parent directory is already in limbo (e.g. when building a
877
 
        # tree), choose a limbo name inside the parent, to reduce further
878
 
        # renames.
879
 
        use_direct_path = False
880
 
        if self._new_contents.get(parent) == 'directory':
881
 
            filename = self._new_name.get(trans_id)
882
 
            if filename is not None:
883
 
                if parent not in self._limbo_children:
884
 
                    self._limbo_children[parent] = set()
885
 
                    self._limbo_children_names[parent] = {}
886
 
                    use_direct_path = True
887
 
                # the direct path can only be used if no other file has
888
 
                # already taken this pathname, i.e. if the name is unused, or
889
 
                # if it is already associated with this trans_id.
890
 
                elif self._case_sensitive_target:
891
 
                    if (self._limbo_children_names[parent].get(filename)
892
 
                        in (trans_id, None)):
893
 
                        use_direct_path = True
894
 
                else:
895
 
                    for l_filename, l_trans_id in\
896
 
                        self._limbo_children_names[parent].iteritems():
897
 
                        if l_trans_id == trans_id:
898
 
                            continue
899
 
                        if l_filename.lower() == filename.lower():
900
 
                            break
901
 
                    else:
902
 
                        use_direct_path = True
903
 
 
904
 
        if use_direct_path:
905
 
            limbo_name = pathjoin(self._limbo_files[parent], filename)
906
 
            self._limbo_children[parent].add(trans_id)
907
 
            self._limbo_children_names[parent][filename] = trans_id
908
 
        else:
909
 
            limbo_name = pathjoin(self._limbodir, trans_id)
910
 
            self._needs_rename.add(trans_id)
911
 
        self._limbo_files[trans_id] = limbo_name
912
 
        return limbo_name
913
 
 
914
705
    def _set_executability(self, path, trans_id):
915
706
        """Set the executability of versioned files """
916
707
        if supports_executable():
1176
967
                                      (('attribs',),))
1177
968
        for trans_id, kind in self._new_contents.items():
1178
969
            if kind == 'file':
1179
 
                cur_file = open(self._limbo_name(trans_id), 'rb')
1180
 
                try:
1181
 
                    lines = osutils.chunks_to_lines(cur_file.readlines())
1182
 
                finally:
1183
 
                    cur_file.close()
 
970
                lines = osutils.chunks_to_lines(
 
971
                    self._read_file_chunks(trans_id))
1184
972
                parents = self._get_parents_lines(trans_id)
1185
973
                mpdiff = multiparent.MultiParent.from_lines(lines, parents)
1186
974
                content = ''.join(mpdiff.to_patch())
1187
975
            if kind == 'directory':
1188
976
                content = ''
1189
977
            if kind == 'symlink':
1190
 
                content = os.readlink(self._limbo_name(trans_id))
 
978
                content = self._read_symlink_target(trans_id)
1191
979
            yield serializer.bytes_record(content, ((trans_id, kind),))
1192
980
 
1193
 
 
1194
981
    def deserialize(self, records):
1195
982
        """Deserialize a stored TreeTransform.
1196
983
 
1227
1014
                self.create_symlink(content.decode('utf-8'), trans_id)
1228
1015
 
1229
1016
 
1230
 
class TreeTransform(TreeTransformBase):
 
1017
class DiskTreeTransform(TreeTransformBase):
 
1018
    """Tree transform storing its contents on disk."""
 
1019
 
 
1020
    def __init__(self, tree, limbodir, pb=DummyProgress(),
 
1021
                 case_sensitive=True):
 
1022
        """Constructor.
 
1023
        :param tree: The tree that will be transformed, but not necessarily
 
1024
            the output tree.
 
1025
        :param limbodir: A directory where new files can be stored until
 
1026
            they are installed in their proper places
 
1027
        :param pb: A ProgressBar indicating how much progress is being made
 
1028
        :param case_sensitive: If True, the target of the transform is
 
1029
            case sensitive, not just case preserving.
 
1030
        """
 
1031
        TreeTransformBase.__init__(self, tree, pb, case_sensitive)
 
1032
        self._limbodir = limbodir
 
1033
        self._deletiondir = None
 
1034
        # A mapping of transform ids to their limbo filename
 
1035
        self._limbo_files = {}
 
1036
        # A mapping of transform ids to a set of the transform ids of children
 
1037
        # that their limbo directory has
 
1038
        self._limbo_children = {}
 
1039
        # Map transform ids to maps of child filename to child transform id
 
1040
        self._limbo_children_names = {}
 
1041
        # List of transform ids that need to be renamed from limbo into place
 
1042
        self._needs_rename = set()
 
1043
 
 
1044
    def finalize(self):
 
1045
        """Release the working tree lock, if held, clean up limbo dir.
 
1046
 
 
1047
        This is required if apply has not been invoked, but can be invoked
 
1048
        even after apply.
 
1049
        """
 
1050
        if self._tree is None:
 
1051
            return
 
1052
        try:
 
1053
            entries = [(self._limbo_name(t), t, k) for t, k in
 
1054
                       self._new_contents.iteritems()]
 
1055
            entries.sort(reverse=True)
 
1056
            for path, trans_id, kind in entries:
 
1057
                if kind == "directory":
 
1058
                    os.rmdir(path)
 
1059
                else:
 
1060
                    os.unlink(path)
 
1061
            try:
 
1062
                os.rmdir(self._limbodir)
 
1063
            except OSError:
 
1064
                # We don't especially care *why* the dir is immortal.
 
1065
                raise ImmortalLimbo(self._limbodir)
 
1066
            try:
 
1067
                if self._deletiondir is not None:
 
1068
                    os.rmdir(self._deletiondir)
 
1069
            except OSError:
 
1070
                raise errors.ImmortalPendingDeletion(self._deletiondir)
 
1071
        finally:
 
1072
            TreeTransformBase.finalize(self)
 
1073
 
 
1074
    def _limbo_name(self, trans_id):
 
1075
        """Generate the limbo name of a file"""
 
1076
        limbo_name = self._limbo_files.get(trans_id)
 
1077
        if limbo_name is not None:
 
1078
            return limbo_name
 
1079
        parent = self._new_parent.get(trans_id)
 
1080
        # if the parent directory is already in limbo (e.g. when building a
 
1081
        # tree), choose a limbo name inside the parent, to reduce further
 
1082
        # renames.
 
1083
        use_direct_path = False
 
1084
        if self._new_contents.get(parent) == 'directory':
 
1085
            filename = self._new_name.get(trans_id)
 
1086
            if filename is not None:
 
1087
                if parent not in self._limbo_children:
 
1088
                    self._limbo_children[parent] = set()
 
1089
                    self._limbo_children_names[parent] = {}
 
1090
                    use_direct_path = True
 
1091
                # the direct path can only be used if no other file has
 
1092
                # already taken this pathname, i.e. if the name is unused, or
 
1093
                # if it is already associated with this trans_id.
 
1094
                elif self._case_sensitive_target:
 
1095
                    if (self._limbo_children_names[parent].get(filename)
 
1096
                        in (trans_id, None)):
 
1097
                        use_direct_path = True
 
1098
                else:
 
1099
                    for l_filename, l_trans_id in\
 
1100
                        self._limbo_children_names[parent].iteritems():
 
1101
                        if l_trans_id == trans_id:
 
1102
                            continue
 
1103
                        if l_filename.lower() == filename.lower():
 
1104
                            break
 
1105
                    else:
 
1106
                        use_direct_path = True
 
1107
 
 
1108
        if use_direct_path:
 
1109
            limbo_name = pathjoin(self._limbo_files[parent], filename)
 
1110
            self._limbo_children[parent].add(trans_id)
 
1111
            self._limbo_children_names[parent][filename] = trans_id
 
1112
        else:
 
1113
            limbo_name = pathjoin(self._limbodir, trans_id)
 
1114
            self._needs_rename.add(trans_id)
 
1115
        self._limbo_files[trans_id] = limbo_name
 
1116
        return limbo_name
 
1117
 
 
1118
    def adjust_path(self, name, parent, trans_id):
 
1119
        previous_parent = self._new_parent.get(trans_id)
 
1120
        previous_name = self._new_name.get(trans_id)
 
1121
        TreeTransformBase.adjust_path(self, name, parent, trans_id)
 
1122
        if (trans_id in self._limbo_files and
 
1123
            trans_id not in self._needs_rename):
 
1124
            self._rename_in_limbo([trans_id])
 
1125
            self._limbo_children[previous_parent].remove(trans_id)
 
1126
            del self._limbo_children_names[previous_parent][previous_name]
 
1127
 
 
1128
    def _rename_in_limbo(self, trans_ids):
 
1129
        """Fix limbo names so that the right final path is produced.
 
1130
 
 
1131
        This means we outsmarted ourselves-- we tried to avoid renaming
 
1132
        these files later by creating them with their final names in their
 
1133
        final parents.  But now the previous name or parent is no longer
 
1134
        suitable, so we have to rename them.
 
1135
 
 
1136
        Even for trans_ids that have no new contents, we must remove their
 
1137
        entries from _limbo_files, because they are now stale.
 
1138
        """
 
1139
        for trans_id in trans_ids:
 
1140
            old_path = self._limbo_files.pop(trans_id)
 
1141
            if trans_id not in self._new_contents:
 
1142
                continue
 
1143
            new_path = self._limbo_name(trans_id)
 
1144
            os.rename(old_path, new_path)
 
1145
 
 
1146
    def create_file(self, contents, trans_id, mode_id=None):
 
1147
        """Schedule creation of a new file.
 
1148
 
 
1149
        See also new_file.
 
1150
 
 
1151
        Contents is an iterator of strings, all of which will be written
 
1152
        to the target destination.
 
1153
 
 
1154
        New file takes the permissions of any existing file with that id,
 
1155
        unless mode_id is specified.
 
1156
        """
 
1157
        name = self._limbo_name(trans_id)
 
1158
        f = open(name, 'wb')
 
1159
        try:
 
1160
            try:
 
1161
                unique_add(self._new_contents, trans_id, 'file')
 
1162
            except:
 
1163
                # Clean up the file, it never got registered so
 
1164
                # TreeTransform.finalize() won't clean it up.
 
1165
                f.close()
 
1166
                os.unlink(name)
 
1167
                raise
 
1168
 
 
1169
            f.writelines(contents)
 
1170
        finally:
 
1171
            f.close()
 
1172
        self._set_mode(trans_id, mode_id, S_ISREG)
 
1173
 
 
1174
    def _read_file_chunks(self, trans_id):
 
1175
        cur_file = open(self._limbo_name(trans_id), 'rb')
 
1176
        try:
 
1177
            return cur_file.readlines()
 
1178
        finally:
 
1179
            cur_file.close()
 
1180
 
 
1181
    def _read_symlink_target(self, trans_id):
 
1182
        return os.readlink(self._limbo_name(trans_id))
 
1183
 
 
1184
    def _set_mode(self, trans_id, mode_id, typefunc):
 
1185
        """Set the mode of new file contents.
 
1186
        The mode_id is the existing file to get the mode from (often the same
 
1187
        as trans_id).  The operation is only performed if there's a mode match
 
1188
        according to typefunc.
 
1189
        """
 
1190
        if mode_id is None:
 
1191
            mode_id = trans_id
 
1192
        try:
 
1193
            old_path = self._tree_id_paths[mode_id]
 
1194
        except KeyError:
 
1195
            return
 
1196
        try:
 
1197
            mode = os.stat(self._tree.abspath(old_path)).st_mode
 
1198
        except OSError, e:
 
1199
            if e.errno in (errno.ENOENT, errno.ENOTDIR):
 
1200
                # Either old_path doesn't exist, or the parent of the
 
1201
                # target is not a directory (but will be one eventually)
 
1202
                # Either way, we know it doesn't exist *right now*
 
1203
                # See also bug #248448
 
1204
                return
 
1205
            else:
 
1206
                raise
 
1207
        if typefunc(mode):
 
1208
            os.chmod(self._limbo_name(trans_id), mode)
 
1209
 
 
1210
    def create_hardlink(self, path, trans_id):
 
1211
        """Schedule creation of a hard link"""
 
1212
        name = self._limbo_name(trans_id)
 
1213
        try:
 
1214
            os.link(path, name)
 
1215
        except OSError, e:
 
1216
            if e.errno != errno.EPERM:
 
1217
                raise
 
1218
            raise errors.HardLinkNotSupported(path)
 
1219
        try:
 
1220
            unique_add(self._new_contents, trans_id, 'file')
 
1221
        except:
 
1222
            # Clean up the file, it never got registered so
 
1223
            # TreeTransform.finalize() won't clean it up.
 
1224
            os.unlink(name)
 
1225
            raise
 
1226
 
 
1227
    def create_directory(self, trans_id):
 
1228
        """Schedule creation of a new directory.
 
1229
 
 
1230
        See also new_directory.
 
1231
        """
 
1232
        os.mkdir(self._limbo_name(trans_id))
 
1233
        unique_add(self._new_contents, trans_id, 'directory')
 
1234
 
 
1235
    def create_symlink(self, target, trans_id):
 
1236
        """Schedule creation of a new symbolic link.
 
1237
 
 
1238
        target is a bytestring.
 
1239
        See also new_symlink.
 
1240
        """
 
1241
        if has_symlinks():
 
1242
            os.symlink(target, self._limbo_name(trans_id))
 
1243
            unique_add(self._new_contents, trans_id, 'symlink')
 
1244
        else:
 
1245
            try:
 
1246
                path = FinalPaths(self).get_path(trans_id)
 
1247
            except KeyError:
 
1248
                path = None
 
1249
            raise UnableCreateSymlink(path=path)
 
1250
 
 
1251
    def cancel_creation(self, trans_id):
 
1252
        """Cancel the creation of new file contents."""
 
1253
        del self._new_contents[trans_id]
 
1254
        children = self._limbo_children.get(trans_id)
 
1255
        # if this is a limbo directory with children, move them before removing
 
1256
        # the directory
 
1257
        if children is not None:
 
1258
            self._rename_in_limbo(children)
 
1259
            del self._limbo_children[trans_id]
 
1260
            del self._limbo_children_names[trans_id]
 
1261
        delete_any(self._limbo_name(trans_id))
 
1262
 
 
1263
 
 
1264
class TreeTransform(DiskTreeTransform):
1231
1265
    """Represent a tree transformation.
1232
1266
 
1233
1267
    This object is designed to support incremental generation of the transform,
1319
1353
            tree.unlock()
1320
1354
            raise
1321
1355
 
1322
 
        TreeTransformBase.__init__(self, tree, limbodir, pb,
 
1356
        DiskTreeTransform.__init__(self, tree, limbodir, pb,
1323
1357
                                   tree.case_sensitive)
1324
1358
        self._deletiondir = deletiondir
1325
1359
 
1505
1539
        return modified_paths
1506
1540
 
1507
1541
 
1508
 
class TransformPreview(TreeTransformBase):
 
1542
class TransformPreview(DiskTreeTransform):
1509
1543
    """A TreeTransform for generating preview trees.
1510
1544
 
1511
1545
    Unlike TreeTransform, this version works when the input tree is a
1516
1550
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1517
1551
        tree.lock_read()
1518
1552
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1519
 
        TreeTransformBase.__init__(self, tree, limbodir, pb, case_sensitive)
 
1553
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1520
1554
 
1521
1555
    def canonical_path(self, path):
1522
1556
        return path