/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/transform.py

  • Committer: Andrew Bennetts
  • Date: 2009-10-21 11:13:40 UTC
  • mto: This revision was merged to the branch mainline in revision 4762.
  • Revision ID: andrew.bennetts@canonical.com-20091021111340-w7x4d5yf83qwjncc
Add test that WSGI glue allows request handlers to access paths above that request's. backing transport, so long as it is within the WSGI app's backing transport.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008, 2009 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
17
17
import os
18
18
import errno
19
19
from stat import S_ISREG, S_IEXEC
20
 
import time
21
20
 
22
21
from bzrlib.lazy_import import lazy_import
23
22
lazy_import(globals(), """
31
30
    multiparent,
32
31
    osutils,
33
32
    revision as _mod_revision,
34
 
    ui,
35
33
    )
36
34
""")
37
35
from bzrlib.errors import (DuplicateKey, MalformedTransform, NoSuchFile,
38
 
                           ReusingTransform, CantMoveRoot,
 
36
                           ReusingTransform, NotVersionedError, CantMoveRoot,
39
37
                           ExistingLimbo, ImmortalLimbo, NoFinalPath,
40
38
                           UnableCreateSymlink)
41
39
from bzrlib.filters import filtered_output_bytes, ContentFilterContext
50
48
    splitpath,
51
49
    supports_executable,
52
50
)
53
 
from bzrlib.progress import ProgressPhase
 
51
from bzrlib.progress import DummyProgress, ProgressPhase
54
52
from bzrlib.symbol_versioning import (
55
53
        deprecated_function,
56
54
        deprecated_in,
80
78
class TreeTransformBase(object):
81
79
    """The base class for TreeTransform and its kin."""
82
80
 
83
 
    def __init__(self, tree, pb=None,
 
81
    def __init__(self, tree, pb=DummyProgress(),
84
82
                 case_sensitive=True):
85
83
        """Constructor.
86
84
 
87
85
        :param tree: The tree that will be transformed, but not necessarily
88
86
            the output tree.
89
 
        :param pb: ignored
 
87
        :param pb: A ProgressTask 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
        """
163
161
 
164
162
    def adjust_path(self, name, parent, trans_id):
165
163
        """Change the path that is assigned to a transaction id."""
166
 
        if parent is None:
167
 
            raise ValueError("Parent trans-id may not be None")
168
164
        if trans_id == self._new_root:
169
165
            raise CantMoveRoot
170
166
        self._new_name[trans_id] = name
171
167
        self._new_parent[trans_id] = parent
 
168
        if parent == ROOT_PARENT:
 
169
            if self._new_root is not None:
 
170
                raise ValueError("Cannot have multiple roots.")
 
171
            self._new_root = trans_id
172
172
 
173
173
    def adjust_root_path(self, name, parent):
174
174
        """Emulate moving the root by moving all children, instead.
202
202
        self.version_file(old_root_file_id, old_root)
203
203
        self.unversion_file(self._new_root)
204
204
 
205
 
    def fixup_new_roots(self):
206
 
        """Reinterpret requests to change the root directory
207
 
 
208
 
        Instead of creating a root directory, or moving an existing directory,
209
 
        all the attributes and children of the new root are applied to the
210
 
        existing root directory.
211
 
 
212
 
        This means that the old root trans-id becomes obsolete, so it is
213
 
        recommended only to invoke this after the root trans-id has become
214
 
        irrelevant.
215
 
        """
216
 
        new_roots = [k for k, v in self._new_parent.iteritems() if v is
217
 
                     ROOT_PARENT]
218
 
        if len(new_roots) < 1:
219
 
            return
220
 
        if len(new_roots) != 1:
221
 
            raise ValueError('A tree cannot have two roots!')
222
 
        if self._new_root is None:
223
 
            self._new_root = new_roots[0]
224
 
            return
225
 
        old_new_root = new_roots[0]
226
 
        # TODO: What to do if a old_new_root is present, but self._new_root is
227
 
        #       not listed as being removed? This code explicitly unversions
228
 
        #       the old root and versions it with the new file_id. Though that
229
 
        #       seems like an incomplete delta
230
 
 
231
 
        # unversion the new root's directory.
232
 
        file_id = self.final_file_id(old_new_root)
233
 
        if old_new_root in self._new_id:
234
 
            self.cancel_versioning(old_new_root)
235
 
        else:
236
 
            self.unversion_file(old_new_root)
237
 
        # if, at this stage, root still has an old file_id, zap it so we can
238
 
        # stick a new one in.
239
 
        if (self.tree_file_id(self._new_root) is not None and
240
 
            self._new_root not in self._removed_id):
241
 
            self.unversion_file(self._new_root)
242
 
        self.version_file(file_id, self._new_root)
243
 
 
244
 
        # Now move children of new root into old root directory.
245
 
        # Ensure all children are registered with the transaction, but don't
246
 
        # use directly-- some tree children have new parents
247
 
        list(self.iter_tree_children(old_new_root))
248
 
        # Move all children of new root into old root directory.
249
 
        for child in self.by_parent().get(old_new_root, []):
250
 
            self.adjust_path(self.final_name(child), self._new_root, child)
251
 
 
252
 
        # Ensure old_new_root has no directory.
253
 
        if old_new_root in self._new_contents:
254
 
            self.cancel_creation(old_new_root)
255
 
        else:
256
 
            self.delete_contents(old_new_root)
257
 
 
258
 
        # prevent deletion of root directory.
259
 
        if self._new_root in self._removed_contents:
260
 
            self.cancel_deletion(self._new_root)
261
 
 
262
 
        # destroy path info for old_new_root.
263
 
        del self._new_parent[old_new_root]
264
 
        del self._new_name[old_new_root]
265
 
 
266
205
    def trans_id_tree_file_id(self, inventory_id):
267
206
        """Determine the transaction id of a working tree file.
268
207
 
314
253
 
315
254
    def delete_contents(self, trans_id):
316
255
        """Schedule the contents of a path entry for deletion"""
317
 
        # Ensure that the object exists in the WorkingTree, this will raise an
318
 
        # exception if there is a problem
319
256
        self.tree_kind(trans_id)
320
257
        self._removed_contents.add(trans_id)
321
258
 
1063
1000
class DiskTreeTransform(TreeTransformBase):
1064
1001
    """Tree transform storing its contents on disk."""
1065
1002
 
1066
 
    def __init__(self, tree, limbodir, pb=None,
 
1003
    def __init__(self, tree, limbodir, pb=DummyProgress(),
1067
1004
                 case_sensitive=True):
1068
1005
        """Constructor.
1069
1006
        :param tree: The tree that will be transformed, but not necessarily
1070
1007
            the output tree.
1071
1008
        :param limbodir: A directory where new files can be stored until
1072
1009
            they are installed in their proper places
1073
 
        :param pb: ignored
 
1010
        :param pb: A ProgressBar indicating how much progress is being made
1074
1011
        :param case_sensitive: If True, the target of the transform is
1075
1012
            case sensitive, not just case preserving.
1076
1013
        """
1086
1023
        self._limbo_children_names = {}
1087
1024
        # List of transform ids that need to be renamed from limbo into place
1088
1025
        self._needs_rename = set()
1089
 
        self._creation_mtime = None
1090
1026
 
1091
1027
    def finalize(self):
1092
1028
        """Release the working tree lock, if held, clean up limbo dir.
1118
1054
    def _limbo_name(self, trans_id):
1119
1055
        """Generate the limbo name of a file"""
1120
1056
        limbo_name = self._limbo_files.get(trans_id)
1121
 
        if limbo_name is None:
1122
 
            limbo_name = self._generate_limbo_path(trans_id)
1123
 
            self._limbo_files[trans_id] = limbo_name
 
1057
        if limbo_name is not None:
 
1058
            return limbo_name
 
1059
        parent = self._new_parent.get(trans_id)
 
1060
        # if the parent directory is already in limbo (e.g. when building a
 
1061
        # tree), choose a limbo name inside the parent, to reduce further
 
1062
        # renames.
 
1063
        use_direct_path = False
 
1064
        if self._new_contents.get(parent) == 'directory':
 
1065
            filename = self._new_name.get(trans_id)
 
1066
            if filename is not None:
 
1067
                if parent not in self._limbo_children:
 
1068
                    self._limbo_children[parent] = set()
 
1069
                    self._limbo_children_names[parent] = {}
 
1070
                    use_direct_path = True
 
1071
                # the direct path can only be used if no other file has
 
1072
                # already taken this pathname, i.e. if the name is unused, or
 
1073
                # if it is already associated with this trans_id.
 
1074
                elif self._case_sensitive_target:
 
1075
                    if (self._limbo_children_names[parent].get(filename)
 
1076
                        in (trans_id, None)):
 
1077
                        use_direct_path = True
 
1078
                else:
 
1079
                    for l_filename, l_trans_id in\
 
1080
                        self._limbo_children_names[parent].iteritems():
 
1081
                        if l_trans_id == trans_id:
 
1082
                            continue
 
1083
                        if l_filename.lower() == filename.lower():
 
1084
                            break
 
1085
                    else:
 
1086
                        use_direct_path = True
 
1087
 
 
1088
        if use_direct_path:
 
1089
            limbo_name = pathjoin(self._limbo_files[parent], filename)
 
1090
            self._limbo_children[parent].add(trans_id)
 
1091
            self._limbo_children_names[parent][filename] = trans_id
 
1092
        else:
 
1093
            limbo_name = pathjoin(self._limbodir, trans_id)
 
1094
            self._needs_rename.add(trans_id)
 
1095
        self._limbo_files[trans_id] = limbo_name
1124
1096
        return limbo_name
1125
1097
 
1126
 
    def _generate_limbo_path(self, trans_id):
1127
 
        """Generate a limbo path using the trans_id as the relative path.
1128
 
 
1129
 
        This is suitable as a fallback, and when the transform should not be
1130
 
        sensitive to the path encoding of the limbo directory.
1131
 
        """
1132
 
        self._needs_rename.add(trans_id)
1133
 
        return pathjoin(self._limbodir, trans_id)
1134
 
 
1135
1098
    def adjust_path(self, name, parent, trans_id):
1136
1099
        previous_parent = self._new_parent.get(trans_id)
1137
1100
        previous_name = self._new_name.get(trans_id)
1139
1102
        if (trans_id in self._limbo_files and
1140
1103
            trans_id not in self._needs_rename):
1141
1104
            self._rename_in_limbo([trans_id])
1142
 
            if previous_parent != parent:
1143
 
                self._limbo_children[previous_parent].remove(trans_id)
1144
 
            if previous_parent != parent or previous_name != name:
1145
 
                del self._limbo_children_names[previous_parent][previous_name]
 
1105
            self._limbo_children[previous_parent].remove(trans_id)
 
1106
            del self._limbo_children_names[previous_parent][previous_name]
1146
1107
 
1147
1108
    def _rename_in_limbo(self, trans_ids):
1148
1109
        """Fix limbo names so that the right final path is produced.
1160
1121
            if trans_id not in self._new_contents:
1161
1122
                continue
1162
1123
            new_path = self._limbo_name(trans_id)
1163
 
            osutils.rename(old_path, new_path)
 
1124
            os.rename(old_path, new_path)
1164
1125
            for descendant in self._limbo_descendants(trans_id):
1165
1126
                desc_path = self._limbo_files[descendant]
1166
1127
                desc_path = new_path + desc_path[len(old_path):]
1199
1160
            f.writelines(contents)
1200
1161
        finally:
1201
1162
            f.close()
1202
 
        self._set_mtime(name)
1203
1163
        self._set_mode(trans_id, mode_id, S_ISREG)
1204
1164
 
1205
1165
    def _read_file_chunks(self, trans_id):
1212
1172
    def _read_symlink_target(self, trans_id):
1213
1173
        return os.readlink(self._limbo_name(trans_id))
1214
1174
 
1215
 
    def _set_mtime(self, path):
1216
 
        """All files that are created get the same mtime.
1217
 
 
1218
 
        This time is set by the first object to be created.
1219
 
        """
1220
 
        if self._creation_mtime is None:
1221
 
            self._creation_mtime = time.time()
1222
 
        os.utime(path, (self._creation_mtime, self._creation_mtime))
1223
 
 
1224
1175
    def create_hardlink(self, path, trans_id):
1225
1176
        """Schedule creation of a hard link"""
1226
1177
        name = self._limbo_name(trans_id)
1340
1291
    FileMover does not delete files until it is sure that a rollback will not
1341
1292
    happen.
1342
1293
    """
1343
 
    def __init__(self, tree, pb=None):
 
1294
    def __init__(self, tree, pb=DummyProgress()):
1344
1295
        """Note: a tree_write lock is taken on the tree.
1345
1296
 
1346
1297
        Use TreeTransform.finalize() to release the lock (can be omitted if
1456
1407
                continue
1457
1408
            yield self.trans_id_tree_path(childpath)
1458
1409
 
1459
 
    def _generate_limbo_path(self, trans_id):
1460
 
        """Generate a limbo path using the final path if possible.
1461
 
 
1462
 
        This optimizes the performance of applying the tree transform by
1463
 
        avoiding renames.  These renames can be avoided only when the parent
1464
 
        directory is already scheduled for creation.
1465
 
 
1466
 
        If the final path cannot be used, falls back to using the trans_id as
1467
 
        the relpath.
1468
 
        """
1469
 
        parent = self._new_parent.get(trans_id)
1470
 
        # if the parent directory is already in limbo (e.g. when building a
1471
 
        # tree), choose a limbo name inside the parent, to reduce further
1472
 
        # renames.
1473
 
        use_direct_path = False
1474
 
        if self._new_contents.get(parent) == 'directory':
1475
 
            filename = self._new_name.get(trans_id)
1476
 
            if filename is not None:
1477
 
                if parent not in self._limbo_children:
1478
 
                    self._limbo_children[parent] = set()
1479
 
                    self._limbo_children_names[parent] = {}
1480
 
                    use_direct_path = True
1481
 
                # the direct path can only be used if no other file has
1482
 
                # already taken this pathname, i.e. if the name is unused, or
1483
 
                # if it is already associated with this trans_id.
1484
 
                elif self._case_sensitive_target:
1485
 
                    if (self._limbo_children_names[parent].get(filename)
1486
 
                        in (trans_id, None)):
1487
 
                        use_direct_path = True
1488
 
                else:
1489
 
                    for l_filename, l_trans_id in\
1490
 
                        self._limbo_children_names[parent].iteritems():
1491
 
                        if l_trans_id == trans_id:
1492
 
                            continue
1493
 
                        if l_filename.lower() == filename.lower():
1494
 
                            break
1495
 
                    else:
1496
 
                        use_direct_path = True
1497
 
 
1498
 
        if not use_direct_path:
1499
 
            return DiskTreeTransform._generate_limbo_path(self, trans_id)
1500
 
 
1501
 
        limbo_name = pathjoin(self._limbo_files[parent], filename)
1502
 
        self._limbo_children[parent].add(trans_id)
1503
 
        self._limbo_children_names[parent][filename] = trans_id
1504
 
        return limbo_name
1505
 
 
1506
 
 
1507
1410
    def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1508
1411
        """Apply all changes to the inventory and filesystem.
1509
1412
 
1629
1532
                child_pb.update('removing file', num, len(tree_paths))
1630
1533
                full_path = self._tree.abspath(path)
1631
1534
                if trans_id in self._removed_contents:
1632
 
                    delete_path = os.path.join(self._deletiondir, trans_id)
1633
 
                    mover.pre_delete(full_path, delete_path)
1634
 
                elif (trans_id in self._new_name
1635
 
                      or trans_id in self._new_parent):
 
1535
                    mover.pre_delete(full_path, os.path.join(self._deletiondir,
 
1536
                                     trans_id))
 
1537
                elif trans_id in self._new_name or trans_id in \
 
1538
                    self._new_parent:
1636
1539
                    try:
1637
1540
                        mover.rename(full_path, self._limbo_name(trans_id))
1638
1541
                    except OSError, e:
1692
1595
    unversioned files in the input tree.
1693
1596
    """
1694
1597
 
1695
 
    def __init__(self, tree, pb=None, case_sensitive=True):
 
1598
    def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1696
1599
        tree.lock_read()
1697
1600
        limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1698
1601
        DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1798
1701
            executable = self.is_executable(file_id, path)
1799
1702
        return kind, executable, None
1800
1703
 
1801
 
    def is_locked(self):
1802
 
        return False
1803
 
 
1804
1704
    def lock_read(self):
1805
1705
        # Perhaps in theory, this should lock the TreeTransform?
1806
 
        return self
 
1706
        pass
1807
1707
 
1808
1708
    def unlock(self):
1809
1709
        pass
2052
1952
                statval = os.lstat(limbo_name)
2053
1953
                size = statval.st_size
2054
1954
                if not supports_executable():
2055
 
                    executable = False
 
1955
                    executable = None
2056
1956
                else:
2057
1957
                    executable = statval.st_mode & S_IEXEC
2058
1958
            else:
2060
1960
                executable = None
2061
1961
            if kind == 'symlink':
2062
1962
                link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
2063
 
        executable = tt._new_executability.get(trans_id, executable)
 
1963
        if supports_executable():
 
1964
            executable = tt._new_executability.get(trans_id, executable)
2064
1965
        return kind, size, executable, link_or_sha1
2065
1966
 
2066
1967
    def iter_changes(self, from_tree, include_unchanged=False,
2379
2280
        new_desired_files = desired_files
2380
2281
    else:
2381
2282
        iter = accelerator_tree.iter_changes(tree, include_unchanged=True)
2382
 
        unchanged = [(f, p[1]) for (f, p, c, v, d, n, k, e)
2383
 
                     in iter if not (c or e[0] != e[1])]
2384
 
        if accelerator_tree.supports_content_filtering():
2385
 
            unchanged = [(f, p) for (f, p) in unchanged
2386
 
                         if not accelerator_tree.iter_search_rules([p]).next()]
2387
 
        unchanged = dict(unchanged)
 
2283
        unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
 
2284
                         in iter if not (c or e[0] != e[1]))
2388
2285
        new_desired_files = []
2389
2286
        count = 0
2390
2287
        for file_id, (trans_id, tree_path) in desired_files:
2513
2410
        tt.create_directory(trans_id)
2514
2411
 
2515
2412
 
2516
 
def create_from_tree(tt, trans_id, tree, file_id, bytes=None,
2517
 
    filter_tree_path=None):
2518
 
    """Create new file contents according to tree contents.
2519
 
    
2520
 
    :param filter_tree_path: the tree path to use to lookup
2521
 
      content filters to apply to the bytes output in the working tree.
2522
 
      This only applies if the working tree supports content filtering.
2523
 
    """
 
2413
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
 
2414
    """Create new file contents according to tree contents."""
2524
2415
    kind = tree.kind(file_id)
2525
2416
    if kind == 'directory':
2526
2417
        tt.create_directory(trans_id)
2531
2422
                bytes = tree_file.readlines()
2532
2423
            finally:
2533
2424
                tree_file.close()
2534
 
        wt = tt._tree
2535
 
        if wt.supports_content_filtering() and filter_tree_path is not None:
2536
 
            filters = wt._content_filter_stack(filter_tree_path)
2537
 
            bytes = filtered_output_bytes(bytes, filters,
2538
 
                ContentFilterContext(filter_tree_path, tree))
2539
2425
        tt.create_file(bytes, trans_id)
2540
2426
    elif kind == "symlink":
2541
2427
        tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2593
2479
 
2594
2480
 
2595
2481
def revert(working_tree, target_tree, filenames, backups=False,
2596
 
           pb=None, change_reporter=None):
 
2482
           pb=DummyProgress(), change_reporter=None):
2597
2483
    """Revert a working tree's contents to those of a target tree."""
2598
2484
    target_tree.lock_read()
2599
 
    pb = ui.ui_factory.nested_progress_bar()
2600
2485
    tt = TreeTransform(working_tree, pb)
2601
2486
    try:
2602
2487
        pp = ProgressPhase("Revert phase", 3, pb)
2621
2506
def _prepare_revert_transform(working_tree, target_tree, tt, filenames,
2622
2507
                              backups, pp, basis_tree=None,
2623
2508
                              merge_modified=None):
 
2509
    pp.next_phase()
2624
2510
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2625
2511
    try:
2626
2512
        if merge_modified is None:
2630
2516
                                      merge_modified, basis_tree)
2631
2517
    finally:
2632
2518
        child_pb.finished()
 
2519
    pp.next_phase()
2633
2520
    child_pb = bzrlib.ui.ui_factory.nested_progress_bar()
2634
2521
    try:
2635
2522
        raw_conflicts = resolve_conflicts(tt, child_pb,
2728
2615
                    parent_trans = ROOT_PARENT
2729
2616
                else:
2730
2617
                    parent_trans = tt.trans_id_file_id(parent[1])
2731
 
                if parent[0] is None and versioned[0]:
2732
 
                    tt.adjust_root_path(name[1], parent_trans)
2733
 
                else:
2734
 
                    tt.adjust_path(name[1], parent_trans, trans_id)
 
2618
                tt.adjust_path(name[1], parent_trans, trans_id)
2735
2619
            if executable[0] != executable[1] and kind[1] == "file":
2736
2620
                tt.set_executability(executable[1], trans_id)
2737
 
        if working_tree.supports_content_filtering():
2738
 
            for index, ((trans_id, mode_id), bytes) in enumerate(
2739
 
                target_tree.iter_files_bytes(deferred_files)):
2740
 
                file_id = deferred_files[index][0]
2741
 
                # We're reverting a tree to the target tree so using the
2742
 
                # target tree to find the file path seems the best choice
2743
 
                # here IMO - Ian C 27/Oct/2009
2744
 
                filter_tree_path = target_tree.id2path(file_id)
2745
 
                filters = working_tree._content_filter_stack(filter_tree_path)
2746
 
                bytes = filtered_output_bytes(bytes, filters,
2747
 
                    ContentFilterContext(filter_tree_path, working_tree))
2748
 
                tt.create_file(bytes, trans_id, mode_id)
2749
 
        else:
2750
 
            for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2751
 
                deferred_files):
2752
 
                tt.create_file(bytes, trans_id, mode_id)
2753
 
        tt.fixup_new_roots()
 
2621
        for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
 
2622
            deferred_files):
 
2623
            tt.create_file(bytes, trans_id, mode_id)
2754
2624
    finally:
2755
2625
        if basis_tree is not None:
2756
2626
            basis_tree.unlock()
2757
2627
    return merge_modified
2758
2628
 
2759
2629
 
2760
 
def resolve_conflicts(tt, pb=None, pass_func=None):
 
2630
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2761
2631
    """Make many conflict-resolution attempts, but die if they fail"""
2762
2632
    if pass_func is None:
2763
2633
        pass_func = conflict_pass
2764
2634
    new_conflicts = set()
2765
 
    pb = ui.ui_factory.nested_progress_bar()
2766
2635
    try:
2767
2636
        for n in range(10):
2768
2637
            pb.update('Resolution pass', n+1, 10)
2772
2641
            new_conflicts.update(pass_func(tt, conflicts))
2773
2642
        raise MalformedTransform(conflicts=conflicts)
2774
2643
    finally:
2775
 
        pb.finished()
 
2644
        pb.clear()
2776
2645
 
2777
2646
 
2778
2647
def conflict_pass(tt, conflicts, path_tree=None):
2827
2696
                        # special-case the other tree root (move its
2828
2697
                        # children to current root)
2829
2698
                        if entry.parent_id is None:
2830
 
                            create = False
 
2699
                            create=False
2831
2700
                            moved = _reparent_transform_children(
2832
2701
                                tt, trans_id, tt.root)
2833
2702
                            for child in moved:
2901
2770
        self.pending_deletions = []
2902
2771
 
2903
2772
    def rename(self, from_, to):
2904
 
        """Rename a file from one path to another."""
 
2773
        """Rename a file from one path to another.  Functions like os.rename"""
2905
2774
        try:
2906
 
            osutils.rename(from_, to)
 
2775
            os.rename(from_, to)
2907
2776
        except OSError, e:
2908
2777
            if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2909
2778
                raise errors.FileExists(to, str(e))
2923
2792
    def rollback(self):
2924
2793
        """Reverse all renames that have been performed"""
2925
2794
        for from_, to in reversed(self.past_renames):
2926
 
            osutils.rename(to, from_)
 
2795
            os.rename(to, from_)
2927
2796
        # after rollback, don't reuse _FileMover
2928
2797
        past_renames = None
2929
2798
        pending_deletions = None