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