607
640
self.branch.unlock()
642
return LogicalLockResult(self.unlock)
610
644
def lock_tree_write(self):
611
"""See MutableTree.lock_tree_write, and WorkingTree.unlock."""
645
"""See MutableTree.lock_tree_write, and WorkingTree.unlock.
647
:return: A breezy.lock.LogicalLockResult.
612
649
self.branch.lock_read()
613
self._lock_self_write()
650
return self._lock_self_write()
615
652
def lock_write(self):
616
"""See MutableTree.lock_write, and WorkingTree.unlock."""
653
"""See MutableTree.lock_write, and WorkingTree.unlock.
655
:return: A breezy.lock.LogicalLockResult.
617
657
self.branch.lock_write()
618
self._lock_self_write()
658
return self._lock_self_write()
620
@needs_tree_write_lock
621
660
def move(self, from_paths, to_dir, after=False):
622
661
"""See WorkingTree.move()."""
624
663
if not from_paths:
626
state = self.current_dirstate()
627
if isinstance(from_paths, basestring):
629
to_dir_utf8 = to_dir.encode('utf8')
630
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
631
id_index = state._get_id_index()
632
# check destination directory
633
# get the details for it
634
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
635
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
636
if not entry_present:
637
raise errors.BzrMoveFailedError('', to_dir,
638
errors.NotVersionedError(to_dir))
639
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
640
# get a handle on the block itself.
641
to_block_index = state._ensure_block(
642
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
643
to_block = state._dirblocks[to_block_index]
644
to_abs = self.abspath(to_dir)
645
if not isdir(to_abs):
646
raise errors.BzrMoveFailedError('',to_dir,
647
errors.NotADirectory(to_abs))
649
if to_entry[1][0][0] != 'd':
650
raise errors.BzrMoveFailedError('',to_dir,
651
errors.NotADirectory(to_abs))
653
if self._inventory is not None:
654
update_inventory = True
656
to_dir_id = to_entry[0][2]
657
to_dir_ie = inv[to_dir_id]
659
update_inventory = False
662
def move_one(old_entry, from_path_utf8, minikind, executable,
663
fingerprint, packed_stat, size,
664
to_block, to_key, to_path_utf8):
665
state._make_absent(old_entry)
666
from_key = old_entry[0]
668
lambda:state.update_minimal(from_key,
670
executable=executable,
671
fingerprint=fingerprint,
672
packed_stat=packed_stat,
674
path_utf8=from_path_utf8))
675
state.update_minimal(to_key,
677
executable=executable,
678
fingerprint=fingerprint,
679
packed_stat=packed_stat,
681
path_utf8=to_path_utf8)
682
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
683
new_entry = to_block[1][added_entry_index]
684
rollbacks.append(lambda:state._make_absent(new_entry))
686
for from_rel in from_paths:
687
# from_rel is 'pathinroot/foo/bar'
688
from_rel_utf8 = from_rel.encode('utf8')
689
from_dirname, from_tail = osutils.split(from_rel)
690
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
691
from_entry = self._get_entry(path=from_rel)
692
if from_entry == (None, None):
693
raise errors.BzrMoveFailedError(from_rel,to_dir,
694
errors.NotVersionedError(path=from_rel))
696
from_id = from_entry[0][2]
697
to_rel = pathjoin(to_dir, from_tail)
698
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
699
item_to_entry = self._get_entry(path=to_rel)
700
if item_to_entry != (None, None):
701
raise errors.BzrMoveFailedError(from_rel, to_rel,
702
"Target is already versioned.")
704
if from_rel == to_rel:
705
raise errors.BzrMoveFailedError(from_rel, to_rel,
706
"Source and target are identical.")
708
from_missing = not self.has_filename(from_rel)
709
to_missing = not self.has_filename(to_rel)
716
raise errors.BzrMoveFailedError(from_rel, to_rel,
717
errors.NoSuchFile(path=to_rel,
718
extra="New file has not been created yet"))
720
# neither path exists
721
raise errors.BzrRenameFailedError(from_rel, to_rel,
722
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
724
if from_missing: # implicitly just update our path mapping
665
with self.lock_tree_write():
666
state = self.current_dirstate()
667
if isinstance(from_paths, (str, bytes)):
669
to_dir_utf8 = to_dir.encode('utf8')
670
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
671
id_index = state._get_id_index()
672
# check destination directory
673
# get the details for it
674
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
675
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
676
if not entry_present:
677
raise errors.BzrMoveFailedError('', to_dir,
678
errors.NotVersionedError(to_dir))
679
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
680
# get a handle on the block itself.
681
to_block_index = state._ensure_block(
682
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
683
to_block = state._dirblocks[to_block_index]
684
to_abs = self.abspath(to_dir)
685
if not isdir(to_abs):
686
raise errors.BzrMoveFailedError('',to_dir,
687
errors.NotADirectory(to_abs))
689
if to_entry[1][0][0] != 'd':
690
raise errors.BzrMoveFailedError('',to_dir,
691
errors.NotADirectory(to_abs))
693
if self._inventory is not None:
694
update_inventory = True
695
inv = self.root_inventory
696
to_dir_id = to_entry[0][2]
697
to_dir_ie = inv[to_dir_id]
699
update_inventory = False
701
# GZ 2017-03-28: The rollbacks variable was shadowed in the loop below
702
# missing those added here, but there's also no test coverage for this.
703
rollbacks = cleanup.ObjectWithCleanups()
704
def move_one(old_entry, from_path_utf8, minikind, executable,
705
fingerprint, packed_stat, size,
706
to_block, to_key, to_path_utf8):
707
state._make_absent(old_entry)
708
from_key = old_entry[0]
709
rollbacks.add_cleanup(
710
state.update_minimal,
713
executable=executable,
714
fingerprint=fingerprint,
715
packed_stat=packed_stat,
717
path_utf8=from_path_utf8)
718
state.update_minimal(to_key,
720
executable=executable,
721
fingerprint=fingerprint,
722
packed_stat=packed_stat,
724
path_utf8=to_path_utf8)
725
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
726
new_entry = to_block[1][added_entry_index]
727
rollbacks.add_cleanup(state._make_absent, new_entry)
729
for from_rel in from_paths:
730
# from_rel is 'pathinroot/foo/bar'
731
from_rel_utf8 = from_rel.encode('utf8')
732
from_dirname, from_tail = osutils.split(from_rel)
733
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
734
from_entry = self._get_entry(path=from_rel)
735
if from_entry == (None, None):
736
raise errors.BzrMoveFailedError(from_rel,to_dir,
737
errors.NotVersionedError(path=from_rel))
739
from_id = from_entry[0][2]
740
to_rel = pathjoin(to_dir, from_tail)
741
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
742
item_to_entry = self._get_entry(path=to_rel)
743
if item_to_entry != (None, None):
744
raise errors.BzrMoveFailedError(from_rel, to_rel,
745
"Target is already versioned.")
747
if from_rel == to_rel:
748
raise errors.BzrMoveFailedError(from_rel, to_rel,
749
"Source and target are identical.")
751
from_missing = not self.has_filename(from_rel)
752
to_missing = not self.has_filename(to_rel)
725
754
move_file = False
727
raise errors.RenameFailedFilesExist(from_rel, to_rel)
759
raise errors.BzrMoveFailedError(from_rel, to_rel,
760
errors.NoSuchFile(path=to_rel,
761
extra="New file has not been created yet"))
763
# neither path exists
764
raise errors.BzrRenameFailedError(from_rel, to_rel,
765
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
767
if from_missing: # implicitly just update our path mapping
770
raise errors.RenameFailedFilesExist(from_rel, to_rel)
730
def rollback_rename():
731
"""A single rename has failed, roll it back."""
732
# roll back everything, even if we encounter trouble doing one
735
# TODO: at least log the other exceptions rather than just
736
# losing them mbp 20070307
738
for rollback in reversed(rollbacks):
772
# perform the disk move first - its the most likely failure point.
774
from_rel_abs = self.abspath(from_rel)
775
to_rel_abs = self.abspath(to_rel)
742
exc_info = sys.exc_info()
744
raise exc_info[0], exc_info[1], exc_info[2]
746
# perform the disk move first - its the most likely failure point.
748
from_rel_abs = self.abspath(from_rel)
749
to_rel_abs = self.abspath(to_rel)
777
osutils.rename(from_rel_abs, to_rel_abs)
779
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
780
rollbacks.add_cleanup(osutils.rename, to_rel_abs, from_rel_abs)
751
osutils.rename(from_rel_abs, to_rel_abs)
753
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
754
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
756
# perform the rename in the inventory next if needed: its easy
760
from_entry = inv[from_id]
761
current_parent = from_entry.parent_id
762
inv.rename(from_id, to_dir_id, from_tail)
764
lambda: inv.rename(from_id, current_parent, from_tail))
765
# finally do the rename in the dirstate, which is a little
766
# tricky to rollback, but least likely to need it.
767
old_block_index, old_entry_index, dir_present, file_present = \
768
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
769
old_block = state._dirblocks[old_block_index][1]
770
old_entry = old_block[old_entry_index]
771
from_key, old_entry_details = old_entry
772
cur_details = old_entry_details[0]
774
to_key = ((to_block[0],) + from_key[1:3])
775
minikind = cur_details[0]
776
move_one(old_entry, from_path_utf8=from_rel_utf8,
778
executable=cur_details[3],
779
fingerprint=cur_details[1],
780
packed_stat=cur_details[4],
784
to_path_utf8=to_rel_utf8)
787
def update_dirblock(from_dir, to_key, to_dir_utf8):
788
"""Recursively update all entries in this dirblock."""
790
raise AssertionError("renaming root not supported")
791
from_key = (from_dir, '')
792
from_block_idx, present = \
793
state._find_block_index_from_key(from_key)
795
# This is the old record, if it isn't present, then
796
# there is theoretically nothing to update.
797
# (Unless it isn't present because of lazy loading,
798
# but we don't do that yet)
800
from_block = state._dirblocks[from_block_idx]
801
to_block_index, to_entry_index, _, _ = \
802
state._get_block_entry_index(to_key[0], to_key[1], 0)
803
to_block_index = state._ensure_block(
804
to_block_index, to_entry_index, to_dir_utf8)
805
to_block = state._dirblocks[to_block_index]
807
# Grab a copy since move_one may update the list.
808
for entry in from_block[1][:]:
809
if not (entry[0][0] == from_dir):
810
raise AssertionError()
811
cur_details = entry[1][0]
812
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
813
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
814
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
815
minikind = cur_details[0]
817
# Deleted children of a renamed directory
818
# Do not need to be updated.
819
# Children that have been renamed out of this
820
# directory should also not be updated
822
move_one(entry, from_path_utf8=from_path_utf8,
824
executable=cur_details[3],
825
fingerprint=cur_details[1],
826
packed_stat=cur_details[4],
830
to_path_utf8=to_path_utf8)
832
# We need to move all the children of this
834
update_dirblock(from_path_utf8, to_key,
836
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
840
result.append((from_rel, to_rel))
841
state._dirblock_state = dirstate.DirState.IN_MEMORY_MODIFIED
842
self._make_dirty(reset_inventory=False)
782
# perform the rename in the inventory next if needed: its easy
786
from_entry = inv[from_id]
787
current_parent = from_entry.parent_id
788
inv.rename(from_id, to_dir_id, from_tail)
789
rollbacks.add_cleanup(
790
inv.rename, from_id, current_parent, from_tail)
791
# finally do the rename in the dirstate, which is a little
792
# tricky to rollback, but least likely to need it.
793
old_block_index, old_entry_index, dir_present, file_present = \
794
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
795
old_block = state._dirblocks[old_block_index][1]
796
old_entry = old_block[old_entry_index]
797
from_key, old_entry_details = old_entry
798
cur_details = old_entry_details[0]
800
to_key = ((to_block[0],) + from_key[1:3])
801
minikind = cur_details[0]
802
move_one(old_entry, from_path_utf8=from_rel_utf8,
804
executable=cur_details[3],
805
fingerprint=cur_details[1],
806
packed_stat=cur_details[4],
810
to_path_utf8=to_rel_utf8)
813
def update_dirblock(from_dir, to_key, to_dir_utf8):
814
"""Recursively update all entries in this dirblock."""
816
raise AssertionError("renaming root not supported")
817
from_key = (from_dir, '')
818
from_block_idx, present = \
819
state._find_block_index_from_key(from_key)
821
# This is the old record, if it isn't present, then
822
# there is theoretically nothing to update.
823
# (Unless it isn't present because of lazy loading,
824
# but we don't do that yet)
826
from_block = state._dirblocks[from_block_idx]
827
to_block_index, to_entry_index, _, _ = \
828
state._get_block_entry_index(to_key[0], to_key[1], 0)
829
to_block_index = state._ensure_block(
830
to_block_index, to_entry_index, to_dir_utf8)
831
to_block = state._dirblocks[to_block_index]
833
# Grab a copy since move_one may update the list.
834
for entry in from_block[1][:]:
835
if not (entry[0][0] == from_dir):
836
raise AssertionError()
837
cur_details = entry[1][0]
838
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
839
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
840
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
841
minikind = cur_details[0]
842
if minikind in (b'a', b'r'):
843
# Deleted children of a renamed directory
844
# Do not need to be updated.
845
# Children that have been renamed out of this
846
# directory should also not be updated
848
move_one(entry, from_path_utf8=from_path_utf8,
850
executable=cur_details[3],
851
fingerprint=cur_details[1],
852
packed_stat=cur_details[4],
856
to_path_utf8=to_path_utf8)
858
# We need to move all the children of this
860
update_dirblock(from_path_utf8, to_key,
862
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
864
rollbacks.cleanup_now()
866
result.append((from_rel, to_rel))
867
state._mark_modified()
868
self._make_dirty(reset_inventory=False)
846
872
def _must_be_locked(self):
847
873
if not self._control_files._lock_count:
1071
1103
If tree is None, then that element is treated as an unreachable
1072
1104
parent tree - i.e. a ghost.
1074
dirstate = self.current_dirstate()
1075
if len(parents_list) > 0:
1076
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1077
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1081
parent_ids = [rev_id for rev_id, tree in parents_list]
1082
graph = self.branch.repository.get_graph()
1083
heads = graph.heads(parent_ids)
1084
accepted_revisions = set()
1086
# convert absent trees to the null tree, which we convert back to
1087
# missing on access.
1088
for rev_id, tree in parents_list:
1089
if len(accepted_revisions) > 0:
1090
# we always accept the first tree
1091
if rev_id in accepted_revisions or rev_id not in heads:
1092
# We have already included either this tree, or its
1093
# descendent, so we skip it.
1095
_mod_revision.check_not_reserved_id(rev_id)
1096
if tree is not None:
1097
real_trees.append((rev_id, tree))
1099
real_trees.append((rev_id,
1100
self.branch.repository.revision_tree(
1101
_mod_revision.NULL_REVISION)))
1102
ghosts.append(rev_id)
1103
accepted_revisions.add(rev_id)
1104
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1105
self._make_dirty(reset_inventory=False)
1106
with self.lock_tree_write():
1107
dirstate = self.current_dirstate()
1108
if len(parents_list) > 0:
1109
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1110
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1114
parent_ids = [rev_id for rev_id, tree in parents_list]
1115
graph = self.branch.repository.get_graph()
1116
heads = graph.heads(parent_ids)
1117
accepted_revisions = set()
1119
# convert absent trees to the null tree, which we convert back to
1120
# missing on access.
1121
for rev_id, tree in parents_list:
1122
if len(accepted_revisions) > 0:
1123
# we always accept the first tree
1124
if rev_id in accepted_revisions or rev_id not in heads:
1125
# We have already included either this tree, or its
1126
# descendent, so we skip it.
1128
_mod_revision.check_not_reserved_id(rev_id)
1129
if tree is not None:
1130
real_trees.append((rev_id, tree))
1132
real_trees.append((rev_id,
1133
self.branch.repository.revision_tree(
1134
_mod_revision.NULL_REVISION)))
1135
ghosts.append(rev_id)
1136
accepted_revisions.add(rev_id)
1138
if (len(real_trees) == 1
1140
and self.branch.repository._format.fast_deltas
1141
and isinstance(real_trees[0][1], InventoryRevisionTree)
1142
and self.get_parent_ids()):
1143
rev_id, rev_tree = real_trees[0]
1144
basis_id = self.get_parent_ids()[0]
1145
# There are times when basis_tree won't be in
1146
# self.branch.repository, (switch, for example)
1148
basis_tree = self.branch.repository.revision_tree(basis_id)
1149
except errors.NoSuchRevision:
1150
# Fall back to the set_parent_trees(), since we can't use
1151
# _make_delta if we can't get the RevisionTree
1154
delta = rev_tree.root_inventory._make_delta(
1155
basis_tree.root_inventory)
1156
dirstate.update_basis_by_delta(delta, rev_id)
1159
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1160
self._make_dirty(reset_inventory=False)
1107
1162
def _set_root_id(self, file_id):
1108
1163
"""See WorkingTree.set_root_id."""
1109
1164
state = self.current_dirstate()
1110
state.set_path_id('', file_id)
1165
state.set_path_id(b'', file_id)
1111
1166
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1112
1167
self._make_dirty(reset_inventory=True)
1162
1215
:param file_ids: The file ids to stop versioning.
1163
1216
:raises: NoSuchId if any fileid is not currently versioned.
1167
state = self.current_dirstate()
1168
state._read_dirblocks_if_needed()
1169
ids_to_unversion = set(file_ids)
1170
paths_to_unversion = set()
1172
# check if the root is to be unversioned, if so, assert for now.
1173
# walk the state marking unversioned things as absent.
1174
# if there are any un-unversioned ids at the end, raise
1175
for key, details in state._dirblocks[0][1]:
1176
if (details[0][0] not in ('a', 'r') and # absent or relocated
1177
key[2] in ids_to_unversion):
1178
# I haven't written the code to unversion / yet - it should be
1180
raise errors.BzrError('Unversioning the / is not currently supported')
1182
while block_index < len(state._dirblocks):
1183
# process one directory at a time.
1184
block = state._dirblocks[block_index]
1185
# first check: is the path one to remove - it or its children
1186
delete_block = False
1187
for path in paths_to_unversion:
1188
if (block[0].startswith(path) and
1189
(len(block[0]) == len(path) or
1190
block[0][len(path)] == '/')):
1191
# this entire block should be deleted - its the block for a
1192
# path to unversion; or the child of one
1195
# TODO: trim paths_to_unversion as we pass by paths
1197
# this block is to be deleted: process it.
1198
# TODO: we can special case the no-parents case and
1199
# just forget the whole block.
1218
with self.lock_tree_write():
1221
state = self.current_dirstate()
1222
state._read_dirblocks_if_needed()
1223
ids_to_unversion = set(file_ids)
1224
paths_to_unversion = set()
1226
# check if the root is to be unversioned, if so, assert for now.
1227
# walk the state marking unversioned things as absent.
1228
# if there are any un-unversioned ids at the end, raise
1229
for key, details in state._dirblocks[0][1]:
1230
if (details[0][0] not in ('a', 'r') and # absent or relocated
1231
key[2] in ids_to_unversion):
1232
# I haven't written the code to unversion / yet - it should be
1234
raise errors.BzrError('Unversioning the / is not currently supported')
1236
while block_index < len(state._dirblocks):
1237
# process one directory at a time.
1238
block = state._dirblocks[block_index]
1239
# first check: is the path one to remove - it or its children
1240
delete_block = False
1241
for path in paths_to_unversion:
1242
if (block[0].startswith(path) and
1243
(len(block[0]) == len(path) or
1244
block[0][len(path)] == '/')):
1245
# this entire block should be deleted - its the block for a
1246
# path to unversion; or the child of one
1249
# TODO: trim paths_to_unversion as we pass by paths
1251
# this block is to be deleted: process it.
1252
# TODO: we can special case the no-parents case and
1253
# just forget the whole block.
1255
while entry_index < len(block[1]):
1256
entry = block[1][entry_index]
1257
if entry[1][0][0] in 'ar':
1258
# don't remove absent or renamed entries
1261
# Mark this file id as having been removed
1262
ids_to_unversion.discard(entry[0][2])
1263
if not state._make_absent(entry):
1264
# The block has not shrunk.
1266
# go to the next block. (At the moment we dont delete empty
1200
1270
entry_index = 0
1201
1271
while entry_index < len(block[1]):
1202
1272
entry = block[1][entry_index]
1203
if entry[1][0][0] in 'ar':
1204
# don't remove absent or renamed entries
1207
# Mark this file id as having been removed
1208
ids_to_unversion.discard(entry[0][2])
1209
if not state._make_absent(entry):
1210
# The block has not shrunk.
1212
# go to the next block. (At the moment we dont delete empty
1273
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1274
# ^ some parent row.
1275
entry[0][2] not in ids_to_unversion):
1276
# ^ not an id to unversion
1279
if entry[1][0][0] == 'd':
1280
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1281
if not state._make_absent(entry):
1283
# we have unversioned this id
1284
ids_to_unversion.remove(entry[0][2])
1214
1285
block_index += 1
1217
while entry_index < len(block[1]):
1218
entry = block[1][entry_index]
1219
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1220
# ^ some parent row.
1221
entry[0][2] not in ids_to_unversion):
1222
# ^ not an id to unversion
1225
if entry[1][0][0] == 'd':
1226
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1227
if not state._make_absent(entry):
1229
# we have unversioned this id
1230
ids_to_unversion.remove(entry[0][2])
1232
if ids_to_unversion:
1233
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1234
self._make_dirty(reset_inventory=False)
1235
# have to change the legacy inventory too.
1236
if self._inventory is not None:
1237
for file_id in file_ids:
1238
self._inventory.remove_recursive_id(file_id)
1286
if ids_to_unversion:
1287
raise errors.NoSuchId(self, next(iter(ids_to_unversion)))
1288
self._make_dirty(reset_inventory=False)
1289
# have to change the legacy inventory too.
1290
if self._inventory is not None:
1291
for file_id in file_ids:
1292
if self._inventory.has_id(file_id):
1293
self._inventory.remove_recursive_id(file_id)
1240
@needs_tree_write_lock
1241
1295
def rename_one(self, from_rel, to_rel, after=False):
1242
1296
"""See WorkingTree.rename_one"""
1244
WorkingTree.rename_one(self, from_rel, to_rel, after)
1297
with self.lock_tree_write():
1299
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1246
@needs_tree_write_lock
1247
1301
def apply_inventory_delta(self, changes):
1248
1302
"""See MutableTree.apply_inventory_delta"""
1249
state = self.current_dirstate()
1250
state.update_by_delta(changes)
1251
self._make_dirty(reset_inventory=True)
1303
with self.lock_tree_write():
1304
state = self.current_dirstate()
1305
state.update_by_delta(changes)
1306
self._make_dirty(reset_inventory=True)
1253
1308
def update_basis_by_delta(self, new_revid, delta):
1254
1309
"""See MutableTree.update_basis_by_delta."""