1097
1088
new_children.sort()
1098
1089
new_children = collections.deque(new_children)
1099
1090
stack.append((f_ie.file_id, fp, fap, new_children))
1100
# Break out of inner loop, so that we start outer loop with child
1091
# Break out of inner loop,
1092
# so that we start outer loop with child
1103
1095
# if we finished all children, pop it off the stack
1106
1098
@needs_tree_write_lock
1107
def move(self, from_paths, to_name):
1099
def move(self, from_paths, to_dir=None, after=False, **kwargs):
1108
1100
"""Rename files.
1110
to_name must exist in the inventory.
1102
to_dir must exist in the inventory.
1112
If to_name exists and is a directory, the files are moved into
1104
If to_dir exists and is a directory, the files are moved into
1113
1105
it, keeping their old names.
1115
Note that to_name is only the last component of the new name;
1107
Note that to_dir is only the last component of the new name;
1116
1108
this doesn't change the directory.
1110
For each entry in from_paths the move mode will be determined
1113
The first mode moves the file in the filesystem and updates the
1114
inventory. The second mode only updates the inventory without
1115
touching the file on the filesystem. This is the new mode introduced
1118
move uses the second mode if 'after == True' and the target is not
1119
versioned but present in the working tree.
1121
move uses the second mode if 'after == False' and the source is
1122
versioned but no longer in the working tree, and the target is not
1123
versioned but present in the working tree.
1125
move uses the first mode if 'after == False' and the source is
1126
versioned and present in the working tree, and the target is not
1127
versioned and not present in the working tree.
1129
Everything else results in an error.
1118
1131
This returns a list of (from_path, to_path) pairs for each
1119
1132
entry that is moved.
1122
## TODO: Option to move IDs only
1137
# check for deprecated use of signature
1139
to_dir = kwargs.get('to_name', None)
1141
raise TypeError('You must supply a target directory')
1143
symbol_versioning.warn('The parameter to_name was deprecated'
1144
' in version 0.13. Use to_dir instead',
1147
# check destination directory
1123
1148
assert not isinstance(from_paths, basestring)
1124
1149
inv = self.inventory
1125
to_abs = self.abspath(to_name)
1150
to_abs = self.abspath(to_dir)
1126
1151
if not isdir(to_abs):
1127
raise BzrError("destination %r is not a directory" % to_abs)
1128
if not self.has_filename(to_name):
1129
raise BzrError("destination %r not in working directory" % to_abs)
1130
to_dir_id = inv.path2id(to_name)
1131
if to_dir_id is None and to_name != '':
1132
raise BzrError("destination %r is not a versioned directory" % to_name)
1152
raise errors.BzrMoveFailedError('',to_dir,
1153
errors.NotADirectory(to_abs))
1154
if not self.has_filename(to_dir):
1155
raise errors.BzrMoveFailedError('',to_dir,
1156
errors.NotInWorkingDirectory(to_dir))
1157
to_dir_id = inv.path2id(to_dir)
1158
if to_dir_id is None:
1159
raise errors.BzrMoveFailedError('',to_dir,
1160
errors.NotVersionedError(path=str(to_dir)))
1133
1162
to_dir_ie = inv[to_dir_id]
1134
1163
if to_dir_ie.kind != 'directory':
1135
raise BzrError("destination %r is not a directory" % to_abs)
1137
to_idpath = inv.get_idpath(to_dir_id)
1139
for f in from_paths:
1140
if not self.has_filename(f):
1141
raise BzrError("%r does not exist in working tree" % f)
1142
f_id = inv.path2id(f)
1144
raise BzrError("%r is not versioned" % f)
1145
name_tail = splitpath(f)[-1]
1146
dest_path = pathjoin(to_name, name_tail)
1147
if self.has_filename(dest_path):
1148
raise BzrError("destination %r already exists" % dest_path)
1149
if f_id in to_idpath:
1150
raise BzrError("can't move %r to a subdirectory of itself" % f)
1152
# OK, so there's a race here, it's possible that someone will
1153
# create a file in this interval and then the rename might be
1154
# left half-done. But we should have caught most problems.
1155
orig_inv = deepcopy(self.inventory)
1164
raise errors.BzrMoveFailedError('',to_dir,
1165
errors.NotADirectory(to_abs))
1167
# create rename entries and tuples
1168
for from_rel in from_paths:
1169
from_tail = splitpath(from_rel)[-1]
1170
from_id = inv.path2id(from_rel)
1172
raise errors.BzrMoveFailedError(from_rel,to_dir,
1173
errors.NotVersionedError(path=str(from_rel)))
1175
from_entry = inv[from_id]
1176
from_parent_id = from_entry.parent_id
1177
to_rel = pathjoin(to_dir, from_tail)
1178
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1180
from_tail=from_tail,
1181
from_parent_id=from_parent_id,
1182
to_rel=to_rel, to_tail=from_tail,
1183
to_parent_id=to_dir_id)
1184
rename_entries.append(rename_entry)
1185
rename_tuples.append((from_rel, to_rel))
1187
# determine which move mode to use. checks also for movability
1188
rename_entries = self._determine_mv_mode(rename_entries, after)
1156
1190
original_modified = self._inventory_is_modified
1158
1192
if len(from_paths):
1159
1193
self._inventory_is_modified = True
1160
for f in from_paths:
1161
name_tail = splitpath(f)[-1]
1162
dest_path = pathjoin(to_name, name_tail)
1163
result.append((f, dest_path))
1164
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1166
osutils.rename(self.abspath(f), self.abspath(dest_path))
1168
raise BzrError("failed to rename %r to %r: %s" %
1169
(f, dest_path, e[1]))
1194
self._move(rename_entries)
1171
1196
# restore the inventory on error
1172
self._set_inventory(orig_inv, dirty=original_modified)
1197
self._inventory_is_modified = original_modified
1174
1199
self._write_inventory(inv)
1200
return rename_tuples
1202
def _determine_mv_mode(self, rename_entries, after=False):
1203
"""Determines for each from-to pair if both inventory and working tree
1204
or only the inventory has to be changed.
1206
Also does basic plausability tests.
1208
inv = self.inventory
1210
for rename_entry in rename_entries:
1211
# store to local variables for easier reference
1212
from_rel = rename_entry.from_rel
1213
from_id = rename_entry.from_id
1214
to_rel = rename_entry.to_rel
1215
to_id = inv.path2id(to_rel)
1216
only_change_inv = False
1218
# check the inventory for source and destination
1220
raise errors.BzrMoveFailedError(from_rel,to_rel,
1221
errors.NotVersionedError(path=str(from_rel)))
1222
if to_id is not None:
1223
raise errors.BzrMoveFailedError(from_rel,to_rel,
1224
errors.AlreadyVersionedError(path=str(to_rel)))
1226
# try to determine the mode for rename (only change inv or change
1227
# inv and file system)
1229
if not self.has_filename(to_rel):
1230
raise errors.BzrMoveFailedError(from_id,to_rel,
1231
errors.NoSuchFile(path=str(to_rel),
1232
extra="New file has not been created yet"))
1233
only_change_inv = True
1234
elif not self.has_filename(from_rel) and self.has_filename(to_rel):
1235
only_change_inv = True
1236
elif self.has_filename(from_rel) and not self.has_filename(to_rel):
1237
only_change_inv = False
1239
# something is wrong, so lets determine what exactly
1240
if not self.has_filename(from_rel) and \
1241
not self.has_filename(to_rel):
1242
raise errors.BzrRenameFailedError(from_rel,to_rel,
1243
errors.PathsDoNotExist(paths=(str(from_rel),
1246
raise errors.RenameFailedFilesExist(from_rel, to_rel,
1247
extra="(Use --after to update the Bazaar id)")
1248
rename_entry.only_change_inv = only_change_inv
1249
return rename_entries
1251
def _move(self, rename_entries):
1252
"""Moves a list of files.
1254
Depending on the value of the flag 'only_change_inv', the
1255
file will be moved on the file system or not.
1257
inv = self.inventory
1260
for entry in rename_entries:
1262
self._move_entry(entry)
1264
self._rollback_move(moved)
1268
def _rollback_move(self, moved):
1269
"""Try to rollback a previous move in case of an filesystem error."""
1270
inv = self.inventory
1273
self._move_entry(_RenameEntry(entry.to_rel, entry.from_id,
1274
entry.to_tail, entry.to_parent_id, entry.from_rel,
1275
entry.from_tail, entry.from_parent_id,
1276
entry.only_change_inv))
1277
except errors.BzrMoveFailedError, e:
1278
raise errors.BzrMoveFailedError( '', '', "Rollback failed."
1279
" The working tree is in an inconsistent state."
1280
" Please consider doing a 'bzr revert'."
1281
" Error message is: %s" % e)
1283
def _move_entry(self, entry):
1284
inv = self.inventory
1285
from_rel_abs = self.abspath(entry.from_rel)
1286
to_rel_abs = self.abspath(entry.to_rel)
1287
if from_rel_abs == to_rel_abs:
1288
raise errors.BzrMoveFailedError(entry.from_rel, entry.to_rel,
1289
"Source and target are identical.")
1291
if not entry.only_change_inv:
1293
osutils.rename(from_rel_abs, to_rel_abs)
1295
raise errors.BzrMoveFailedError(entry.from_rel,
1297
inv.rename(entry.from_id, entry.to_parent_id, entry.to_tail)
1177
1299
@needs_tree_write_lock
1178
def rename_one(self, from_rel, to_rel):
1300
def rename_one(self, from_rel, to_rel, after=False):
1179
1301
"""Rename one file.
1181
1303
This can change the directory or the filename or both.
1305
rename_one has several 'modes' to work. First, it can rename a physical
1306
file and change the file_id. That is the normal mode. Second, it can
1307
only change the file_id without touching any physical file. This is
1308
the new mode introduced in version 0.15.
1310
rename_one uses the second mode if 'after == True' and 'to_rel' is not
1311
versioned but present in the working tree.
1313
rename_one uses the second mode if 'after == False' and 'from_rel' is
1314
versioned but no longer in the working tree, and 'to_rel' is not
1315
versioned but present in the working tree.
1317
rename_one uses the first mode if 'after == False' and 'from_rel' is
1318
versioned and present in the working tree, and 'to_rel' is not
1319
versioned and not present in the working tree.
1321
Everything else results in an error.
1183
1323
inv = self.inventory
1184
if not self.has_filename(from_rel):
1185
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1186
if self.has_filename(to_rel):
1187
raise BzrError("can't rename: new working file %r already exists" % to_rel)
1189
file_id = inv.path2id(from_rel)
1191
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1193
entry = inv[file_id]
1194
from_parent = entry.parent_id
1195
from_name = entry.name
1197
if inv.path2id(to_rel):
1198
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1326
# create rename entries and tuples
1327
from_tail = splitpath(from_rel)[-1]
1328
from_id = inv.path2id(from_rel)
1330
raise errors.BzrRenameFailedError(from_rel,to_rel,
1331
errors.NotVersionedError(path=str(from_rel)))
1332
from_entry = inv[from_id]
1333
from_parent_id = from_entry.parent_id
1200
1334
to_dir, to_tail = os.path.split(to_rel)
1201
1335
to_dir_id = inv.path2id(to_dir)
1202
if to_dir_id is None and to_dir != '':
1203
raise BzrError("can't determine destination directory id for %r" % to_dir)
1205
mutter("rename_one:")
1206
mutter(" file_id {%s}" % file_id)
1207
mutter(" from_rel %r" % from_rel)
1208
mutter(" to_rel %r" % to_rel)
1209
mutter(" to_dir %r" % to_dir)
1210
mutter(" to_dir_id {%s}" % to_dir_id)
1212
inv.rename(file_id, to_dir_id, to_tail)
1214
from_abs = self.abspath(from_rel)
1215
to_abs = self.abspath(to_rel)
1217
osutils.rename(from_abs, to_abs)
1219
inv.rename(file_id, from_parent, from_name)
1220
raise BzrError("failed to rename %r to %r: %s"
1221
% (from_abs, to_abs, e[1]))
1336
rename_entry = WorkingTree._RenameEntry(from_rel=from_rel,
1338
from_tail=from_tail,
1339
from_parent_id=from_parent_id,
1340
to_rel=to_rel, to_tail=to_tail,
1341
to_parent_id=to_dir_id)
1342
rename_entries.append(rename_entry)
1344
# determine which move mode to use. checks also for movability
1345
rename_entries = self._determine_mv_mode(rename_entries, after)
1347
# check if the target changed directory and if the target directory is
1349
if to_dir_id is None:
1350
raise errors.BzrMoveFailedError(from_rel,to_rel,
1351
errors.NotVersionedError(path=str(to_dir)))
1353
# all checks done. now we can continue with our actual work
1354
mutter('rename_one:\n'
1359
' to_dir_id {%s}\n',
1360
from_id, from_rel, to_rel, to_dir, to_dir_id)
1362
self._move(rename_entries)
1222
1363
self._write_inventory(inv)
1365
class _RenameEntry(object):
1366
def __init__(self, from_rel, from_id, from_tail, from_parent_id,
1367
to_rel, to_tail, to_parent_id, only_change_inv=False):
1368
self.from_rel = from_rel
1369
self.from_id = from_id
1370
self.from_tail = from_tail
1371
self.from_parent_id = from_parent_id
1372
self.to_rel = to_rel
1373
self.to_tail = to_tail
1374
self.to_parent_id = to_parent_id
1375
self.only_change_inv = only_change_inv
1224
1377
@needs_read_lock
1225
1378
def unknowns(self):
1226
1379
"""Return all unknown files.