978
971
new_children.sort()
979
972
new_children = collections.deque(new_children)
980
973
stack.append((f_ie.file_id, fp, fap, new_children))
981
# Break out of inner loop, so that we start outer loop with child
974
# Break out of inner loop,
975
# so that we start outer loop with child
984
978
# if we finished all children, pop it off the stack
987
981
@needs_tree_write_lock
988
def move(self, from_paths, to_name):
982
def move(self, from_paths, to_dir=None, after=False, **kwargs):
991
to_name must exist in the inventory.
985
to_dir must exist in the inventory.
993
If to_name exists and is a directory, the files are moved into
987
If to_dir exists and is a directory, the files are moved into
994
988
it, keeping their old names.
996
Note that to_name is only the last component of the new name;
990
Note that to_dir is only the last component of the new name;
997
991
this doesn't change the directory.
993
For each entry in from_paths the move mode will be determined
996
The first mode moves the file in the filesystem and updates the
997
inventory. The second mode only updates the inventory without
998
touching the file on the filesystem. This is the new mode introduced
1001
move uses the second mode if 'after == True' and the target is not
1002
versioned but present in the working tree.
1004
move uses the second mode if 'after == False' and the source is
1005
versioned but no longer in the working tree, and the target is not
1006
versioned but present in the working tree.
1008
move uses the first mode if 'after == False' and the source is
1009
versioned and present in the working tree, and the target is not
1010
versioned and not present in the working tree.
1012
Everything else results in an error.
999
1014
This returns a list of (from_path, to_path) pairs for each
1000
1015
entry that is moved.
1003
## TODO: Option to move IDs only
1020
# check for deprecated use of signature
1022
to_dir = kwargs.get('to_name', None)
1024
raise TypeError('You must supply a target directory')
1026
symbol_versioning.warn('The parameter to_name was deprecated'
1027
' in version 0.13. Use to_dir instead',
1030
# check destination directory
1004
1031
assert not isinstance(from_paths, basestring)
1005
1032
inv = self.inventory
1006
to_abs = self.abspath(to_name)
1033
to_abs = self.abspath(to_dir)
1007
1034
if not isdir(to_abs):
1008
raise BzrError("destination %r is not a directory" % to_abs)
1009
if not self.has_filename(to_name):
1010
raise BzrError("destination %r not in working directory" % to_abs)
1011
to_dir_id = inv.path2id(to_name)
1012
if to_dir_id is None and to_name != '':
1013
raise BzrError("destination %r is not a versioned directory" % to_name)
1035
raise errors.BzrMoveFailedError('',to_dir,
1036
errors.NotADirectory(to_abs))
1037
if not self.has_filename(to_dir):
1038
raise errors.BzrMoveFailedError('',to_dir,
1039
errors.NotInWorkingDirectory(to_dir))
1040
to_dir_id = inv.path2id(to_dir)
1041
if to_dir_id is None:
1042
raise errors.BzrMoveFailedError('',to_dir,
1043
errors.NotVersionedError(path=str(to_dir)))
1014
1045
to_dir_ie = inv[to_dir_id]
1015
1046
if to_dir_ie.kind != 'directory':
1016
raise BzrError("destination %r is not a directory" % to_abs)
1018
to_idpath = inv.get_idpath(to_dir_id)
1020
for f in from_paths:
1021
if not self.has_filename(f):
1022
raise BzrError("%r does not exist in working tree" % f)
1023
f_id = inv.path2id(f)
1025
raise BzrError("%r is not versioned" % f)
1026
name_tail = splitpath(f)[-1]
1027
dest_path = pathjoin(to_name, name_tail)
1028
if self.has_filename(dest_path):
1029
raise BzrError("destination %r already exists" % dest_path)
1030
if f_id in to_idpath:
1031
raise BzrError("can't move %r to a subdirectory of itself" % f)
1033
# OK, so there's a race here, it's possible that someone will
1034
# create a file in this interval and then the rename might be
1035
# left half-done. But we should have caught most problems.
1036
orig_inv = deepcopy(self.inventory)
1047
raise errors.BzrMoveFailedError('',to_dir,
1048
errors.NotADirectory(to_abs))
1050
# create rename entries and tuples
1051
for from_rel in from_paths:
1052
from_tail = splitpath(from_rel)[-1]
1053
from_id = inv.path2id(from_rel)
1055
raise errors.BzrMoveFailedError(from_rel,to_dir,
1056
errors.NotVersionedError(path=str(from_rel)))
1058
from_entry = inv[from_id]
1059
from_parent_id = from_entry.parent_id
1060
to_rel = pathjoin(to_dir, from_tail)
1061
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1063
from_tail=from_tail,
1064
from_parent_id=from_parent_id,
1065
to_rel=to_rel, to_tail=from_tail,
1066
to_parent_id=to_dir_id)
1067
rename_entries.append(rename_entry)
1068
rename_tuples.append((from_rel, to_rel))
1070
# determine which move mode to use. checks also for movability
1071
rename_entries = self._determine_mv_mode(rename_entries, after)
1037
1073
original_modified = self._inventory_is_modified
1039
1075
if len(from_paths):
1040
1076
self._inventory_is_modified = True
1041
for f in from_paths:
1042
name_tail = splitpath(f)[-1]
1043
dest_path = pathjoin(to_name, name_tail)
1044
result.append((f, dest_path))
1045
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1047
rename(self.abspath(f), self.abspath(dest_path))
1049
raise BzrError("failed to rename %r to %r: %s" %
1050
(f, dest_path, e[1]),
1051
["rename rolled back"])
1077
self._move(rename_entries)
1053
1079
# restore the inventory on error
1054
self._set_inventory(orig_inv, dirty=original_modified)
1080
self._inventory_is_modified = original_modified
1056
1082
self._write_inventory(inv)
1083
return rename_tuples
1085
def _determine_mv_mode(self, rename_entries, after=False):
1086
"""Determines for each from-to pair if both inventory and working tree
1087
or only the inventory has to be changed.
1089
Also does basic plausability tests.
1091
inv = self.inventory
1093
for rename_entry in rename_entries:
1094
# store to local variables for easier reference
1095
from_rel = rename_entry.from_rel
1096
from_id = rename_entry.from_id
1097
to_rel = rename_entry.to_rel
1098
to_id = inv.path2id(to_rel)
1099
only_change_inv = False
1101
# check the inventory for source and destination
1103
raise errors.BzrMoveFailedError(from_rel,to_rel,
1104
errors.NotVersionedError(path=str(from_rel)))
1105
if to_id is not None:
1106
raise errors.BzrMoveFailedError(from_rel,to_rel,
1107
errors.AlreadyVersionedError(path=str(to_rel)))
1109
# try to determine the mode for rename (only change inv or change
1110
# inv and file system)
1112
if not self.has_filename(to_rel):
1113
raise errors.BzrMoveFailedError(from_id,to_rel,
1114
errors.NoSuchFile(path=str(to_rel),
1115
extra="New file has not been created yet"))
1116
only_change_inv = True
1117
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1118
only_change_inv = True
1119
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1120
only_change_inv = False
1122
# something is wrong, so lets determine what exactly
1123
if not self.has_filename(from_rel) and \
1124
not self.has_filename(to_rel):
1125
raise errors.BzrRenameFailedError(from_rel,to_rel,
1126
errors.PathsDoNotExist(paths=(str(from_rel),
1129
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1130
extra="(Use --after to update the Bazaar id)")
1131
rename_entry.only_change_inv = only_change_inv
1132
return rename_entries
1134
def _move(self, rename_entries):
1135
"""Moves a list of files.
1137
Depending on the value of the flag 'only_change_inv', the
1138
file will be moved on the file system or not.
1140
inv = self.inventory
1143
for entry in rename_entries:
1145
self._move_entry(entry)
1147
self._rollback_move(moved)
1151
def _rollback_move(self, moved):
1152
"""Try to rollback a previous move in case of an filesystem error."""
1153
inv = self.inventory
1156
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1157
entry.to_tail, entry.to_parent_id, entry.from_rel,
1158
entry.from_tail, entry.from_parent_id,
1159
entry.only_change_inv))
1160
except errors.BzrMoveFailedError, e:
1161
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1162
" The working tree is in an inconsistent state."
1163
" Please consider doing a 'bzr revert'."
1164
" Error message is: %s" % e)
1166
def _move_entry(self, entry):
1167
inv = self.inventory
1168
from_rel_abs = self.abspath(entry.from_rel)
1169
to_rel_abs = self.abspath(entry.to_rel)
1170
if from_rel_abs == to_rel_abs:
1171
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1172
"Source and target are identical.")
1174
if not entry.only_change_inv:
1176
osutils.rename(from_rel_abs, to_rel_abs)
1178
raise errors.BzrMoveFailedError(entry.from_rel,
1180
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1059
1182
@needs_tree_write_lock
1060
def rename_one(self, from_rel, to_rel):
1183
def rename_one(self, from_rel, to_rel, after=False):
1061
1184
"""Rename one file.
1063
1186
This can change the directory or the filename or both.
1188
rename_one has several 'modes' to work. First, it can rename a physical
1189
file and change the file_id. That is the normal mode. Second, it can
1190
only change the file_id without touching any physical file. This is
1191
the new mode introduced in version 0.15.
1193
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1194
versioned but present in the working tree.
1196
rename_one uses the second mode if 'after == False' and 'from_rel' is
1197
versioned but no longer in the working tree, and 'to_rel' is not
1198
versioned but present in the working tree.
1200
rename_one uses the first mode if 'after == False' and 'from_rel' is
1201
versioned and present in the working tree, and 'to_rel' is not
1202
versioned and not present in the working tree.
1204
Everything else results in an error.
1065
1206
inv = self.inventory
1066
if not self.has_filename(from_rel):
1067
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1068
if self.has_filename(to_rel):
1069
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1071
file_id = inv.path2id(from_rel)
1073
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1075
entry = inv[file_id]
1076
from_parent = entry.parent_id
1077
from_name = entry.name
1079
if inv.path2id(to_rel):
1080
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1209
# create rename entries and tuples
1210
from_tail = splitpath(from_rel)[-1]
1211
from_id = inv.path2id(from_rel)
1213
raise errors.BzrRenameFailedError(from_rel,to_rel,
1214
errors.NotVersionedError(path=str(from_rel)))
1215
from_entry = inv[from_id]
1216
from_parent_id = from_entry.parent_id
1082
1217
to_dir, to_tail = os.path.split(to_rel)
1083
1218
to_dir_id = inv.path2id(to_dir)
1084
if to_dir_id is None and to_dir != '':
1085
raise BzrError("can't determine destination directory id for %r" % to_dir)
1087
mutter("rename_one:")
1088
mutter(" file_id {%s}" % file_id)
1089
mutter(" from_rel %r" % from_rel)
1090
mutter(" to_rel %r" % to_rel)
1091
mutter(" to_dir %r" % to_dir)
1092
mutter(" to_dir_id {%s}" % to_dir_id)
1094
inv.rename(file_id, to_dir_id, to_tail)
1096
from_abs = self.abspath(from_rel)
1097
to_abs = self.abspath(to_rel)
1099
rename(from_abs, to_abs)
1101
inv.rename(file_id, from_parent, from_name)
1102
raise BzrError("failed to rename %r to %r: %s"
1103
% (from_abs, to_abs, e[1]),
1104
["rename rolled back"])
1219
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1221
from_tail=from_tail,
1222
from_parent_id=from_parent_id,
1223
to_rel=to_rel, to_tail=to_tail,
1224
to_parent_id=to_dir_id)
1225
rename_entries.append(rename_entry)
1227
# determine which move mode to use. checks also for movability
1228
rename_entries = self._determine_mv_mode(rename_entries, after)
1230
# check if the target changed directory and if the target directory is
1232
if to_dir_id is None:
1233
raise errors.BzrMoveFailedError(from_rel,to_rel,
1234
errors.NotVersionedError(path=str(to_dir)))
1236
# all checks done. now we can continue with our actual work
1237
mutter('rename_one:\n'
1242
' to_dir_id {%s}\n',
1243
from_id, from_rel, to_rel, to_dir, to_dir_id)
1245
self._move(rename_entries)
1105
1246
self._write_inventory(inv)
1248
class _RenameEntry(object):
1249
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1250
to_rel, to_tail, to_parent_id, only_change_inv=False):
1251
self.from_rel = from_rel
1252
self.from_id = from_id
1253
self.from_tail = from_tail
1254
self.from_parent_id = from_parent_id
1255
self.to_rel = to_rel
1256
self.to_tail = to_tail
1257
self.to_parent_id = to_parent_id
1258
self.only_change_inv = only_change_inv
1107
1260
@needs_read_lock
1108
1261
def unknowns(self):
1109
1262
"""Return all unknown files.