33
32
revision as _mod_revision,
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
80
78
class TreeTransformBase(object):
81
79
"""The base class for TreeTransform and its kin."""
83
def __init__(self, tree, pb=None,
81
def __init__(self, tree, pb=DummyProgress(),
84
82
case_sensitive=True):
87
85
:param tree: The tree that will be transformed, but not necessarily
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.
164
162
def adjust_path(self, name, parent, trans_id):
165
163
"""Change the path that is assigned to a transaction id."""
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
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)
205
def fixup_new_roots(self):
206
"""Reinterpret requests to change the root directory
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.
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
216
new_roots = [k for k, v in self._new_parent.iteritems() if v is
218
if len(new_roots) < 1:
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]
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
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)
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)
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)
252
# Ensure old_new_root has no directory.
253
if old_new_root in self._new_contents:
254
self.cancel_creation(old_new_root)
256
self.delete_contents(old_new_root)
258
# prevent deletion of root directory.
259
if self._new_root in self._removed_contents:
260
self.cancel_deletion(self._new_root)
262
# destroy path info for old_new_root.
263
del self._new_parent[old_new_root]
264
del self._new_name[old_new_root]
266
205
def trans_id_tree_file_id(self, inventory_id):
267
206
"""Determine the transaction id of a working tree file.
922
854
def get_preview_tree(self):
923
855
"""Return a tree representing the result of the transform.
925
The tree is a snapshot, and altering the TreeTransform will invalidate
857
This tree only supports the subset of Tree functionality required
858
by show_diff_trees. It must only be compared to tt._tree.
928
860
return _PreviewTree(self)
930
def commit(self, branch, message, merge_parents=None, strict=False):
931
"""Commit the result of this TreeTransform to a branch.
933
:param branch: The branch to commit to.
934
:param message: The message to attach to the commit.
935
:param merge_parents: Additional parents specified by pending merges.
936
:return: The revision_id of the revision committed.
938
self._check_malformed()
940
unversioned = set(self._new_contents).difference(set(self._new_id))
941
for trans_id in unversioned:
942
if self.final_file_id(trans_id) is None:
943
raise errors.StrictCommitFailed()
945
revno, last_rev_id = branch.last_revision_info()
946
if last_rev_id == _mod_revision.NULL_REVISION:
947
if merge_parents is not None:
948
raise ValueError('Cannot supply merge parents for first'
952
parent_ids = [last_rev_id]
953
if merge_parents is not None:
954
parent_ids.extend(merge_parents)
955
if self._tree.get_revision_id() != last_rev_id:
956
raise ValueError('TreeTransform not based on branch basis: %s' %
957
self._tree.get_revision_id())
958
builder = branch.get_commit_builder(parent_ids)
959
preview = self.get_preview_tree()
960
list(builder.record_iter_changes(preview, last_rev_id,
961
self.iter_changes()))
962
builder.finish_inventory()
963
revision_id = builder.commit(message)
964
branch.set_last_revision_info(revno + 1, revision_id)
967
862
def _text_parent(self, trans_id):
968
863
file_id = self.tree_file_id(trans_id)
1063
958
class DiskTreeTransform(TreeTransformBase):
1064
959
"""Tree transform storing its contents on disk."""
1066
def __init__(self, tree, limbodir, pb=None,
961
def __init__(self, tree, limbodir, pb=DummyProgress(),
1067
962
case_sensitive=True):
1069
964
:param tree: The tree that will be transformed, but not necessarily
1070
965
the output tree.
1071
966
:param limbodir: A directory where new files can be stored until
1072
967
they are installed in their proper places
968
:param pb: A ProgressBar indicating how much progress is being made
1074
969
:param case_sensitive: If True, the target of the transform is
1075
970
case sensitive, not just case preserving.
1101
995
self._new_contents.iteritems()]
1102
996
entries.sort(reverse=True)
1103
997
for path, trans_id, kind in entries:
998
if kind == "directory":
1106
delete_any(self._limbodir)
1003
os.rmdir(self._limbodir)
1107
1004
except OSError:
1108
1005
# We don't especially care *why* the dir is immortal.
1109
1006
raise ImmortalLimbo(self._limbodir)
1111
1008
if self._deletiondir is not None:
1112
delete_any(self._deletiondir)
1009
os.rmdir(self._deletiondir)
1113
1010
except OSError:
1114
1011
raise errors.ImmortalPendingDeletion(self._deletiondir)
1118
1015
def _limbo_name(self, trans_id):
1119
1016
"""Generate the limbo name of a file"""
1120
1017
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
1018
if limbo_name is not None:
1020
parent = self._new_parent.get(trans_id)
1021
# if the parent directory is already in limbo (e.g. when building a
1022
# tree), choose a limbo name inside the parent, to reduce further
1024
use_direct_path = False
1025
if self._new_contents.get(parent) == 'directory':
1026
filename = self._new_name.get(trans_id)
1027
if filename is not None:
1028
if parent not in self._limbo_children:
1029
self._limbo_children[parent] = set()
1030
self._limbo_children_names[parent] = {}
1031
use_direct_path = True
1032
# the direct path can only be used if no other file has
1033
# already taken this pathname, i.e. if the name is unused, or
1034
# if it is already associated with this trans_id.
1035
elif self._case_sensitive_target:
1036
if (self._limbo_children_names[parent].get(filename)
1037
in (trans_id, None)):
1038
use_direct_path = True
1040
for l_filename, l_trans_id in\
1041
self._limbo_children_names[parent].iteritems():
1042
if l_trans_id == trans_id:
1044
if l_filename.lower() == filename.lower():
1047
use_direct_path = True
1050
limbo_name = pathjoin(self._limbo_files[parent], filename)
1051
self._limbo_children[parent].add(trans_id)
1052
self._limbo_children_names[parent][filename] = trans_id
1054
limbo_name = pathjoin(self._limbodir, trans_id)
1055
self._needs_rename.add(trans_id)
1056
self._limbo_files[trans_id] = limbo_name
1124
1057
return limbo_name
1126
def _generate_limbo_path(self, trans_id):
1127
"""Generate a limbo path using the trans_id as the relative path.
1129
This is suitable as a fallback, and when the transform should not be
1130
sensitive to the path encoding of the limbo directory.
1132
self._needs_rename.add(trans_id)
1133
return pathjoin(self._limbodir, trans_id)
1135
1059
def adjust_path(self, name, parent, trans_id):
1136
1060
previous_parent = self._new_parent.get(trans_id)
1137
1061
previous_name = self._new_name.get(trans_id)
1139
1063
if (trans_id in self._limbo_files and
1140
1064
trans_id not in self._needs_rename):
1141
1065
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]
1066
self._limbo_children[previous_parent].remove(trans_id)
1067
del self._limbo_children_names[previous_parent][previous_name]
1147
1069
def _rename_in_limbo(self, trans_ids):
1148
1070
"""Fix limbo names so that the right final path is produced.
1160
1082
if trans_id not in self._new_contents:
1162
1084
new_path = self._limbo_name(trans_id)
1163
osutils.rename(old_path, new_path)
1164
for descendant in self._limbo_descendants(trans_id):
1165
desc_path = self._limbo_files[descendant]
1166
desc_path = new_path + desc_path[len(old_path):]
1167
self._limbo_files[descendant] = desc_path
1169
def _limbo_descendants(self, trans_id):
1170
"""Return the set of trans_ids whose limbo paths descend from this."""
1171
descendants = set(self._limbo_children.get(trans_id, []))
1172
for descendant in list(descendants):
1173
descendants.update(self._limbo_descendants(descendant))
1085
os.rename(old_path, new_path)
1176
1087
def create_file(self, contents, trans_id, mode_id=None):
1177
1088
"""Schedule creation of a new file.
1212
1122
def _read_symlink_target(self, trans_id):
1213
1123
return os.readlink(self._limbo_name(trans_id))
1215
def _set_mtime(self, path):
1216
"""All files that are created get the same mtime.
1218
This time is set by the first object to be created.
1220
if self._creation_mtime is None:
1221
self._creation_mtime = time.time()
1222
os.utime(path, (self._creation_mtime, self._creation_mtime))
1224
1125
def create_hardlink(self, path, trans_id):
1225
1126
"""Schedule creation of a hard link"""
1226
1127
name = self._limbo_name(trans_id)
1457
1358
yield self.trans_id_tree_path(childpath)
1459
def _generate_limbo_path(self, trans_id):
1460
"""Generate a limbo path using the final path if possible.
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.
1466
If the final path cannot be used, falls back to using the trans_id as
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
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
1489
for l_filename, l_trans_id in\
1490
self._limbo_children_names[parent].iteritems():
1491
if l_trans_id == trans_id:
1493
if l_filename.lower() == filename.lower():
1496
use_direct_path = True
1498
if not use_direct_path:
1499
return DiskTreeTransform._generate_limbo_path(self, trans_id)
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
1507
1361
def apply(self, no_conflicts=False, precomputed_delta=None, _mover=None):
1508
1362
"""Apply all changes to the inventory and filesystem.
1629
1485
child_pb.update('removing file', num, len(tree_paths))
1630
1486
full_path = self._tree.abspath(path)
1631
1487
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):
1488
mover.pre_delete(full_path, os.path.join(self._deletiondir,
1490
elif trans_id in self._new_name or trans_id in \
1637
1493
mover.rename(full_path, self._limbo_name(trans_id))
1638
1494
except OSError, e:
1692
1548
unversioned files in the input tree.
1695
def __init__(self, tree, pb=None, case_sensitive=True):
1551
def __init__(self, tree, pb=DummyProgress(), case_sensitive=True):
1696
1552
tree.lock_read()
1697
1553
limbodir = osutils.mkdtemp(prefix='bzr-limbo-')
1698
1554
DiskTreeTransform.__init__(self, tree, limbodir, pb, case_sensitive)
1743
1599
self._all_children_cache = {}
1744
1600
self._path2trans_id_cache = {}
1745
1601
self._final_name_cache = {}
1746
self._iter_changes_cache = dict((c[0], c) for c in
1747
self._transform.iter_changes())
1603
def _changes(self, file_id):
1604
for changes in self._transform.iter_changes():
1605
if changes[0] == file_id:
1749
1608
def _content_change(self, file_id):
1750
1609
"""Return True if the content of this file changed"""
1751
changes = self._iter_changes_cache.get(file_id)
1610
changes = self._changes(file_id)
1752
1611
# changes[2] is true if the file content changed. See
1753
1612
# InterTree.iter_changes.
1754
1613
return (changes is not None and changes[2])
1826
1682
def __iter__(self):
1827
1683
return iter(self.all_file_ids())
1829
def _has_id(self, file_id, fallback_check):
1685
def has_id(self, file_id):
1830
1686
if file_id in self._transform._r_new_id:
1832
1688
elif file_id in set([self._transform.tree_file_id(trans_id) for
1833
1689
trans_id in self._transform._removed_id]):
1836
return fallback_check(file_id)
1838
def has_id(self, file_id):
1839
return self._has_id(file_id, self._transform._tree.has_id)
1841
def has_or_had_id(self, file_id):
1842
return self._has_id(file_id, self._transform._tree.has_or_had_id)
1692
return self._transform._tree.has_id(file_id)
1844
1694
def _path2trans_id(self, path):
1845
1695
# We must not use None here, because that is a valid value to store.
1898
1748
if self._transform.final_file_id(trans_id) is None:
1899
1749
yield self._final_paths._determine_path(trans_id)
1901
def _make_inv_entries(self, ordered_entries, specific_file_ids=None,
1902
yield_parents=False):
1751
def _make_inv_entries(self, ordered_entries, specific_file_ids):
1903
1752
for trans_id, parent_file_id in ordered_entries:
1904
1753
file_id = self._transform.final_file_id(trans_id)
1905
1754
if file_id is None:
1931
1780
ordered_ids.append((trans_id, parent_file_id))
1932
1781
return ordered_ids
1934
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
1783
def iter_entries_by_dir(self, specific_file_ids=None):
1935
1784
# This may not be a maximally efficient implementation, but it is
1936
1785
# reasonably straightforward. An implementation that grafts the
1937
1786
# TreeTransform changes onto the tree's iter_entries_by_dir results
1940
1789
ordered_ids = self._list_files_by_dir()
1941
1790
for entry, trans_id in self._make_inv_entries(ordered_ids,
1942
specific_file_ids, yield_parents=yield_parents):
1943
yield unicode(self._final_paths.get_path(trans_id)), entry
1945
def _iter_entries_for_dir(self, dir_path):
1946
"""Return path, entry for items in a directory without recursing down."""
1947
dir_file_id = self.path2id(dir_path)
1949
for file_id in self.iter_children(dir_file_id):
1950
trans_id = self._transform.trans_id_file_id(file_id)
1951
ordered_ids.append((trans_id, file_id))
1952
for entry, trans_id in self._make_inv_entries(ordered_ids):
1953
yield unicode(self._final_paths.get_path(trans_id)), entry
1955
def list_files(self, include_root=False, from_dir=None, recursive=True):
1956
"""See WorkingTree.list_files."""
1792
yield unicode(self._final_paths.get_path(trans_id)), entry
1794
def list_files(self, include_root=False):
1795
"""See Tree.list_files."""
1957
1796
# XXX This should behave like WorkingTree.list_files, but is really
1958
1797
# more like RevisionTree.list_files.
1962
prefix = from_dir + '/'
1963
entries = self.iter_entries_by_dir()
1964
for path, entry in entries:
1965
if entry.name == '' and not include_root:
1968
if not path.startswith(prefix):
1970
path = path[len(prefix):]
1971
yield path, 'V', entry.kind, entry.file_id, entry
1973
if from_dir is None and include_root is True:
1974
root_entry = inventory.make_entry('directory', '',
1975
ROOT_PARENT, self.get_root_id())
1976
yield '', 'V', 'directory', root_entry.file_id, root_entry
1977
entries = self._iter_entries_for_dir(from_dir or '')
1978
for path, entry in entries:
1979
yield path, 'V', entry.kind, entry.file_id, entry
1798
for path, entry in self.iter_entries_by_dir():
1799
if entry.name == '' and not include_root:
1801
yield path, 'V', entry.kind, entry.file_id, entry
1981
1803
def kind(self, file_id):
1982
1804
trans_id = self._transform.trans_id_file_id(file_id)
1992
1814
def get_file_mtime(self, file_id, path=None):
1993
1815
"""See Tree.get_file_mtime"""
1994
1816
if not self._content_change(file_id):
1995
return self._transform._tree.get_file_mtime(file_id)
1817
return self._transform._tree.get_file_mtime(file_id, path)
1996
1818
return self._stat_limbo_file(file_id).st_mtime
1998
1820
def _file_size(self, entry, stat_value):
2060
1882
executable = None
2061
1883
if kind == 'symlink':
2062
1884
link_or_sha1 = os.readlink(limbo_name).decode(osutils._fs_enc)
2063
executable = tt._new_executability.get(trans_id, executable)
1885
if supports_executable():
1886
executable = tt._new_executability.get(trans_id, executable)
2064
1887
return kind, size, executable, link_or_sha1
2066
1889
def iter_changes(self, from_tree, include_unchanged=False,
2115
1938
return old_annotation
2116
1939
if not changed_content:
2117
1940
return old_annotation
2118
# TODO: This is doing something similar to what WT.annotate_iter is
2119
# doing, however it fails slightly because it doesn't know what
2120
# the *other* revision_id is, so it doesn't know how to give the
2121
# other as the origin for some lines, they all get
2122
# 'default_revision'
2123
# It would be nice to be able to use the new Annotator based
2124
# approach, as well.
2125
1941
return annotate.reannotate([old_annotation],
2126
1942
self.get_file(file_id).readlines(),
2127
1943
default_revision)
2379
2195
new_desired_files = desired_files
2381
2197
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)
2198
unchanged = dict((f, p[1]) for (f, p, c, v, d, n, k, e)
2199
in iter if not (c or e[0] != e[1]))
2388
2200
new_desired_files = []
2390
2202
for file_id, (trans_id, tree_path) in desired_files:
2513
2325
tt.create_directory(trans_id)
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.
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.
2328
def create_from_tree(tt, trans_id, tree, file_id, bytes=None):
2329
"""Create new file contents according to tree contents."""
2524
2330
kind = tree.kind(file_id)
2525
2331
if kind == 'directory':
2526
2332
tt.create_directory(trans_id)
2531
2337
bytes = tree_file.readlines()
2533
2339
tree_file.close()
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
2340
tt.create_file(bytes, trans_id)
2540
2341
elif kind == "symlink":
2541
2342
tt.create_symlink(tree.get_symlink_target(file_id), trans_id)
2595
2396
def revert(working_tree, target_tree, filenames, backups=False,
2596
pb=None, change_reporter=None):
2397
pb=DummyProgress(), change_reporter=None):
2597
2398
"""Revert a working tree's contents to those of a target tree."""
2598
2399
target_tree.lock_read()
2599
pb = ui.ui_factory.nested_progress_bar()
2600
2400
tt = TreeTransform(working_tree, pb)
2602
2402
pp = ProgressPhase("Revert phase", 3, pb)
2728
2530
parent_trans = ROOT_PARENT
2730
2532
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)
2734
tt.adjust_path(name[1], parent_trans, trans_id)
2533
tt.adjust_path(name[1], parent_trans, trans_id)
2735
2534
if executable[0] != executable[1] and kind[1] == "file":
2736
2535
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)
2750
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2752
tt.create_file(bytes, trans_id, mode_id)
2753
tt.fixup_new_roots()
2536
for (trans_id, mode_id), bytes in target_tree.iter_files_bytes(
2538
tt.create_file(bytes, trans_id, mode_id)
2755
2540
if basis_tree is not None:
2756
2541
basis_tree.unlock()
2757
2542
return merge_modified
2760
def resolve_conflicts(tt, pb=None, pass_func=None):
2545
def resolve_conflicts(tt, pb=DummyProgress(), pass_func=None):
2761
2546
"""Make many conflict-resolution attempts, but die if they fail"""
2762
2547
if pass_func is None:
2763
2548
pass_func = conflict_pass
2764
2549
new_conflicts = set()
2765
pb = ui.ui_factory.nested_progress_bar()
2767
2551
for n in range(10):
2768
2552
pb.update('Resolution pass', n+1, 10)
2901
2685
self.pending_deletions = []
2903
2687
def rename(self, from_, to):
2904
"""Rename a file from one path to another."""
2688
"""Rename a file from one path to another. Functions like os.rename"""
2906
osutils.rename(from_, to)
2690
os.rename(from_, to)
2907
2691
except OSError, e:
2908
2692
if e.errno in (errno.EEXIST, errno.ENOTEMPTY):
2909
2693
raise errors.FileExists(to, str(e))