651
652
def lock_write(self):
652
653
"""See MutableTree.lock_write, and WorkingTree.unlock.
654
:return: A breezy.lock.LogicalLockResult.
655
:return: A bzrlib.lock.LogicalLockResult.
656
657
self.branch.lock_write()
657
658
return self._lock_self_write()
660
@needs_tree_write_lock
659
661
def move(self, from_paths, to_dir, after=False):
660
662
"""See WorkingTree.move()."""
662
664
if not from_paths:
664
with self.lock_tree_write():
665
state = self.current_dirstate()
666
if isinstance(from_paths, (str, bytes)):
668
to_dir_utf8 = to_dir.encode('utf8')
669
to_entry_dirname, to_basename = os.path.split(to_dir_utf8)
670
id_index = state._get_id_index()
671
# check destination directory
672
# get the details for it
673
to_entry_block_index, to_entry_entry_index, dir_present, entry_present = \
674
state._get_block_entry_index(to_entry_dirname, to_basename, 0)
675
if not entry_present:
676
raise errors.BzrMoveFailedError('', to_dir,
677
errors.NotVersionedError(to_dir))
678
to_entry = state._dirblocks[to_entry_block_index][1][to_entry_entry_index]
679
# get a handle on the block itself.
680
to_block_index = state._ensure_block(
681
to_entry_block_index, to_entry_entry_index, to_dir_utf8)
682
to_block = state._dirblocks[to_block_index]
683
to_abs = self.abspath(to_dir)
684
if not isdir(to_abs):
685
raise errors.BzrMoveFailedError('', to_dir,
686
errors.NotADirectory(to_abs))
688
if to_entry[1][0][0] != 'd':
689
raise errors.BzrMoveFailedError('', to_dir,
690
errors.NotADirectory(to_abs))
692
if self._inventory is not None:
693
update_inventory = True
694
inv = self.root_inventory
695
to_dir_id = to_entry[0][2]
696
to_dir_ie = inv.get_entry(to_dir_id)
698
update_inventory = False
700
# GZ 2017-03-28: The rollbacks variable was shadowed in the loop below
701
# missing those added here, but there's also no test coverage for this.
702
rollbacks = cleanup.ObjectWithCleanups()
703
def move_one(old_entry, from_path_utf8, minikind, executable,
704
fingerprint, packed_stat, size,
705
to_block, to_key, to_path_utf8):
706
state._make_absent(old_entry)
707
from_key = old_entry[0]
708
rollbacks.add_cleanup(
709
state.update_minimal,
712
executable=executable,
713
fingerprint=fingerprint,
714
packed_stat=packed_stat,
716
path_utf8=from_path_utf8)
717
state.update_minimal(to_key,
719
executable=executable,
720
fingerprint=fingerprint,
721
packed_stat=packed_stat,
723
path_utf8=to_path_utf8)
724
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
725
new_entry = to_block[1][added_entry_index]
726
rollbacks.add_cleanup(state._make_absent, new_entry)
728
for from_rel in from_paths:
729
# from_rel is 'pathinroot/foo/bar'
730
from_rel_utf8 = from_rel.encode('utf8')
731
from_dirname, from_tail = osutils.split(from_rel)
732
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
733
from_entry = self._get_entry(path=from_rel)
734
if from_entry == (None, None):
735
raise errors.BzrMoveFailedError(from_rel, to_dir,
736
errors.NotVersionedError(path=from_rel))
738
from_id = from_entry[0][2]
739
to_rel = pathjoin(to_dir, from_tail)
740
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
741
item_to_entry = self._get_entry(path=to_rel)
742
if item_to_entry != (None, None):
743
raise errors.BzrMoveFailedError(from_rel, to_rel,
744
"Target is already versioned.")
746
if from_rel == to_rel:
747
raise errors.BzrMoveFailedError(from_rel, to_rel,
748
"Source and target are identical.")
750
from_missing = not self.has_filename(from_rel)
751
to_missing = not self.has_filename(to_rel)
666
state = self.current_dirstate()
667
if isinstance(from_paths, basestring):
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
702
def move_one(old_entry, from_path_utf8, minikind, executable,
703
fingerprint, packed_stat, size,
704
to_block, to_key, to_path_utf8):
705
state._make_absent(old_entry)
706
from_key = old_entry[0]
708
lambda:state.update_minimal(from_key,
710
executable=executable,
711
fingerprint=fingerprint,
712
packed_stat=packed_stat,
714
path_utf8=from_path_utf8))
715
state.update_minimal(to_key,
717
executable=executable,
718
fingerprint=fingerprint,
719
packed_stat=packed_stat,
721
path_utf8=to_path_utf8)
722
added_entry_index, _ = state._find_entry_index(to_key, to_block[1])
723
new_entry = to_block[1][added_entry_index]
724
rollbacks.append(lambda:state._make_absent(new_entry))
726
for from_rel in from_paths:
727
# from_rel is 'pathinroot/foo/bar'
728
from_rel_utf8 = from_rel.encode('utf8')
729
from_dirname, from_tail = osutils.split(from_rel)
730
from_dirname, from_tail_utf8 = osutils.split(from_rel_utf8)
731
from_entry = self._get_entry(path=from_rel)
732
if from_entry == (None, None):
733
raise errors.BzrMoveFailedError(from_rel,to_dir,
734
errors.NotVersionedError(path=from_rel))
736
from_id = from_entry[0][2]
737
to_rel = pathjoin(to_dir, from_tail)
738
to_rel_utf8 = pathjoin(to_dir_utf8, from_tail_utf8)
739
item_to_entry = self._get_entry(path=to_rel)
740
if item_to_entry != (None, None):
741
raise errors.BzrMoveFailedError(from_rel, to_rel,
742
"Target is already versioned.")
744
if from_rel == to_rel:
745
raise errors.BzrMoveFailedError(from_rel, to_rel,
746
"Source and target are identical.")
748
from_missing = not self.has_filename(from_rel)
749
to_missing = not self.has_filename(to_rel)
756
raise errors.BzrMoveFailedError(from_rel, to_rel,
757
errors.NoSuchFile(path=to_rel,
758
extra="New file has not been created yet"))
760
# neither path exists
761
raise errors.BzrRenameFailedError(from_rel, to_rel,
762
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
764
if from_missing: # implicitly just update our path mapping
753
765
move_file = False
758
raise errors.BzrMoveFailedError(from_rel, to_rel,
759
errors.NoSuchFile(path=to_rel,
760
extra="New file has not been created yet"))
762
# neither path exists
763
raise errors.BzrRenameFailedError(from_rel, to_rel,
764
errors.PathsDoNotExist(paths=(from_rel, to_rel)))
766
if from_missing: # implicitly just update our path mapping
769
raise errors.RenameFailedFilesExist(from_rel, to_rel)
767
raise errors.RenameFailedFilesExist(from_rel, to_rel)
771
# perform the disk move first - its the most likely failure point.
773
from_rel_abs = self.abspath(from_rel)
774
to_rel_abs = self.abspath(to_rel)
770
def rollback_rename():
771
"""A single rename has failed, roll it back."""
772
# roll back everything, even if we encounter trouble doing one
775
# TODO: at least log the other exceptions rather than just
776
# losing them mbp 20070307
778
for rollback in reversed(rollbacks):
776
osutils.rename(from_rel_abs, to_rel_abs)
778
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
779
rollbacks.add_cleanup(osutils.rename, to_rel_abs, from_rel_abs)
782
exc_info = sys.exc_info()
784
raise exc_info[0], exc_info[1], exc_info[2]
786
# perform the disk move first - its the most likely failure point.
788
from_rel_abs = self.abspath(from_rel)
789
to_rel_abs = self.abspath(to_rel)
781
# perform the rename in the inventory next if needed: its easy
785
from_entry = inv.get_entry(from_id)
786
current_parent = from_entry.parent_id
787
inv.rename(from_id, to_dir_id, from_tail)
788
rollbacks.add_cleanup(
789
inv.rename, from_id, current_parent, from_tail)
790
# finally do the rename in the dirstate, which is a little
791
# tricky to rollback, but least likely to need it.
792
old_block_index, old_entry_index, dir_present, file_present = \
793
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
794
old_block = state._dirblocks[old_block_index][1]
795
old_entry = old_block[old_entry_index]
796
from_key, old_entry_details = old_entry
797
cur_details = old_entry_details[0]
799
to_key = ((to_block[0],) + from_key[1:3])
800
minikind = cur_details[0]
801
move_one(old_entry, from_path_utf8=from_rel_utf8,
803
executable=cur_details[3],
804
fingerprint=cur_details[1],
805
packed_stat=cur_details[4],
809
to_path_utf8=to_rel_utf8)
812
def update_dirblock(from_dir, to_key, to_dir_utf8):
813
"""Recursively update all entries in this dirblock."""
815
raise AssertionError("renaming root not supported")
816
from_key = (from_dir, '')
817
from_block_idx, present = \
818
state._find_block_index_from_key(from_key)
820
# This is the old record, if it isn't present, then
821
# there is theoretically nothing to update.
822
# (Unless it isn't present because of lazy loading,
823
# but we don't do that yet)
825
from_block = state._dirblocks[from_block_idx]
826
to_block_index, to_entry_index, _, _ = \
827
state._get_block_entry_index(to_key[0], to_key[1], 0)
828
to_block_index = state._ensure_block(
829
to_block_index, to_entry_index, to_dir_utf8)
830
to_block = state._dirblocks[to_block_index]
832
# Grab a copy since move_one may update the list.
833
for entry in from_block[1][:]:
834
if not (entry[0][0] == from_dir):
835
raise AssertionError()
836
cur_details = entry[1][0]
837
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
838
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
839
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
840
minikind = cur_details[0]
841
if minikind in (b'a', b'r'):
842
# Deleted children of a renamed directory
843
# Do not need to be updated.
844
# Children that have been renamed out of this
845
# directory should also not be updated
847
move_one(entry, from_path_utf8=from_path_utf8,
849
executable=cur_details[3],
850
fingerprint=cur_details[1],
851
packed_stat=cur_details[4],
855
to_path_utf8=to_path_utf8)
857
# We need to move all the children of this
859
update_dirblock(from_path_utf8, to_key,
861
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
863
rollbacks.cleanup_now()
865
result.append((from_rel, to_rel))
866
state._mark_modified()
867
self._make_dirty(reset_inventory=False)
791
osutils.rename(from_rel_abs, to_rel_abs)
793
raise errors.BzrMoveFailedError(from_rel, to_rel, e[1])
794
rollbacks.append(lambda: osutils.rename(to_rel_abs, from_rel_abs))
796
# perform the rename in the inventory next if needed: its easy
800
from_entry = inv[from_id]
801
current_parent = from_entry.parent_id
802
inv.rename(from_id, to_dir_id, from_tail)
804
lambda: inv.rename(from_id, current_parent, from_tail))
805
# finally do the rename in the dirstate, which is a little
806
# tricky to rollback, but least likely to need it.
807
old_block_index, old_entry_index, dir_present, file_present = \
808
state._get_block_entry_index(from_dirname, from_tail_utf8, 0)
809
old_block = state._dirblocks[old_block_index][1]
810
old_entry = old_block[old_entry_index]
811
from_key, old_entry_details = old_entry
812
cur_details = old_entry_details[0]
814
to_key = ((to_block[0],) + from_key[1:3])
815
minikind = cur_details[0]
816
move_one(old_entry, from_path_utf8=from_rel_utf8,
818
executable=cur_details[3],
819
fingerprint=cur_details[1],
820
packed_stat=cur_details[4],
824
to_path_utf8=to_rel_utf8)
827
def update_dirblock(from_dir, to_key, to_dir_utf8):
828
"""Recursively update all entries in this dirblock."""
830
raise AssertionError("renaming root not supported")
831
from_key = (from_dir, '')
832
from_block_idx, present = \
833
state._find_block_index_from_key(from_key)
835
# This is the old record, if it isn't present, then
836
# there is theoretically nothing to update.
837
# (Unless it isn't present because of lazy loading,
838
# but we don't do that yet)
840
from_block = state._dirblocks[from_block_idx]
841
to_block_index, to_entry_index, _, _ = \
842
state._get_block_entry_index(to_key[0], to_key[1], 0)
843
to_block_index = state._ensure_block(
844
to_block_index, to_entry_index, to_dir_utf8)
845
to_block = state._dirblocks[to_block_index]
847
# Grab a copy since move_one may update the list.
848
for entry in from_block[1][:]:
849
if not (entry[0][0] == from_dir):
850
raise AssertionError()
851
cur_details = entry[1][0]
852
to_key = (to_dir_utf8, entry[0][1], entry[0][2])
853
from_path_utf8 = osutils.pathjoin(entry[0][0], entry[0][1])
854
to_path_utf8 = osutils.pathjoin(to_dir_utf8, entry[0][1])
855
minikind = cur_details[0]
857
# Deleted children of a renamed directory
858
# Do not need to be updated.
859
# Children that have been renamed out of this
860
# directory should also not be updated
862
move_one(entry, from_path_utf8=from_path_utf8,
864
executable=cur_details[3],
865
fingerprint=cur_details[1],
866
packed_stat=cur_details[4],
870
to_path_utf8=to_path_utf8)
872
# We need to move all the children of this
874
update_dirblock(from_path_utf8, to_key,
876
update_dirblock(from_rel_utf8, to_key, to_rel_utf8)
880
result.append((from_rel, to_rel))
881
state._mark_modified()
882
self._make_dirty(reset_inventory=False)
871
886
def _must_be_locked(self):
872
887
if not self._control_files._lock_count:
1102
1117
If tree is None, then that element is treated as an unreachable
1103
1118
parent tree - i.e. a ghost.
1105
with self.lock_tree_write():
1106
dirstate = self.current_dirstate()
1107
if len(parents_list) > 0:
1108
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1109
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1113
parent_ids = [rev_id for rev_id, tree in parents_list]
1114
graph = self.branch.repository.get_graph()
1115
heads = graph.heads(parent_ids)
1116
accepted_revisions = set()
1118
# convert absent trees to the null tree, which we convert back to
1119
# missing on access.
1120
for rev_id, tree in parents_list:
1121
if len(accepted_revisions) > 0:
1122
# we always accept the first tree
1123
if rev_id in accepted_revisions or rev_id not in heads:
1124
# We have already included either this tree, or its
1125
# descendent, so we skip it.
1127
_mod_revision.check_not_reserved_id(rev_id)
1128
if tree is not None:
1129
real_trees.append((rev_id, tree))
1131
real_trees.append((rev_id,
1132
self.branch.repository.revision_tree(
1133
_mod_revision.NULL_REVISION)))
1134
ghosts.append(rev_id)
1135
accepted_revisions.add(rev_id)
1137
if (len(real_trees) == 1
1139
and self.branch.repository._format.fast_deltas
1140
and isinstance(real_trees[0][1], InventoryRevisionTree)
1141
and self.get_parent_ids()):
1142
rev_id, rev_tree = real_trees[0]
1143
basis_id = self.get_parent_ids()[0]
1144
# There are times when basis_tree won't be in
1145
# self.branch.repository, (switch, for example)
1147
basis_tree = self.branch.repository.revision_tree(basis_id)
1148
except errors.NoSuchRevision:
1149
# Fall back to the set_parent_trees(), since we can't use
1150
# _make_delta if we can't get the RevisionTree
1153
delta = rev_tree.root_inventory._make_delta(
1154
basis_tree.root_inventory)
1155
dirstate.update_basis_by_delta(delta, rev_id)
1158
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1159
self._make_dirty(reset_inventory=False)
1120
dirstate = self.current_dirstate()
1121
if len(parents_list) > 0:
1122
if not allow_leftmost_as_ghost and parents_list[0][1] is None:
1123
raise errors.GhostRevisionUnusableHere(parents_list[0][0])
1127
parent_ids = [rev_id for rev_id, tree in parents_list]
1128
graph = self.branch.repository.get_graph()
1129
heads = graph.heads(parent_ids)
1130
accepted_revisions = set()
1132
# convert absent trees to the null tree, which we convert back to
1133
# missing on access.
1134
for rev_id, tree in parents_list:
1135
if len(accepted_revisions) > 0:
1136
# we always accept the first tree
1137
if rev_id in accepted_revisions or rev_id not in heads:
1138
# We have already included either this tree, or its
1139
# descendent, so we skip it.
1141
_mod_revision.check_not_reserved_id(rev_id)
1142
if tree is not None:
1143
real_trees.append((rev_id, tree))
1145
real_trees.append((rev_id,
1146
self.branch.repository.revision_tree(
1147
_mod_revision.NULL_REVISION)))
1148
ghosts.append(rev_id)
1149
accepted_revisions.add(rev_id)
1151
if (len(real_trees) == 1
1153
and self.branch.repository._format.fast_deltas
1154
and isinstance(real_trees[0][1],
1155
revisiontree.InventoryRevisionTree)
1156
and self.get_parent_ids()):
1157
rev_id, rev_tree = real_trees[0]
1158
basis_id = self.get_parent_ids()[0]
1159
# There are times when basis_tree won't be in
1160
# self.branch.repository, (switch, for example)
1162
basis_tree = self.branch.repository.revision_tree(basis_id)
1163
except errors.NoSuchRevision:
1164
# Fall back to the set_parent_trees(), since we can't use
1165
# _make_delta if we can't get the RevisionTree
1168
delta = rev_tree.root_inventory._make_delta(
1169
basis_tree.root_inventory)
1170
dirstate.update_basis_by_delta(delta, rev_id)
1173
dirstate.set_parent_trees(real_trees, ghosts=ghosts)
1174
self._make_dirty(reset_inventory=False)
1161
1176
def _set_root_id(self, file_id):
1162
1177
"""See WorkingTree.set_root_id."""
1163
1178
state = self.current_dirstate()
1164
state.set_path_id(b'', file_id)
1179
state.set_path_id('', file_id)
1165
1180
if state._dirblock_state == dirstate.DirState.IN_MEMORY_MODIFIED:
1166
1181
self._make_dirty(reset_inventory=True)
1206
1222
self.branch.unlock()
1208
def unversion(self, paths, file_ids=None):
1209
"""Remove the file ids in paths from the current versioned set.
1224
@needs_tree_write_lock
1225
def unversion(self, file_ids):
1226
"""Remove the file ids in file_ids from the current versioned set.
1211
1228
When a file_id is unversioned, all of its children are automatically
1214
:param paths: The file ids to stop versioning.
1231
:param file_ids: The file ids to stop versioning.
1215
1232
:raises: NoSuchId if any fileid is not currently versioned.
1217
with self.lock_tree_write():
1220
state = self.current_dirstate()
1221
state._read_dirblocks_if_needed()
1222
if file_ids is None:
1225
file_id = self.path2id(path)
1227
raise errors.NoSuchFile(self, path)
1228
file_ids.add(file_id)
1229
ids_to_unversion = set(file_ids)
1230
paths_to_unversion = set()
1232
# check if the root is to be unversioned, if so, assert for now.
1233
# walk the state marking unversioned things as absent.
1234
# if there are any un-unversioned ids at the end, raise
1235
for key, details in state._dirblocks[0][1]:
1236
if (details[0][0] not in ('a', 'r') and # absent or relocated
1237
key[2] in ids_to_unversion):
1238
# I haven't written the code to unversion / yet - it should be
1240
raise errors.BzrError(
1241
'Unversioning the / is not currently supported')
1243
while block_index < len(state._dirblocks):
1244
# process one directory at a time.
1245
block = state._dirblocks[block_index]
1246
# first check: is the path one to remove - it or its children
1247
delete_block = False
1248
for path in paths_to_unversion:
1249
if (block[0].startswith(path) and
1250
(len(block[0]) == len(path) or
1251
block[0][len(path)] == '/')):
1252
# this entire block should be deleted - its the block for a
1253
# path to unversion; or the child of one
1256
# TODO: trim paths_to_unversion as we pass by paths
1258
# this block is to be deleted: process it.
1259
# TODO: we can special case the no-parents case and
1260
# just forget the whole block.
1262
while entry_index < len(block[1]):
1263
entry = block[1][entry_index]
1264
if entry[1][0][0] in 'ar':
1265
# don't remove absent or renamed entries
1268
# Mark this file id as having been removed
1269
ids_to_unversion.discard(entry[0][2])
1270
if not state._make_absent(entry):
1271
# The block has not shrunk.
1273
# go to the next block. (At the moment we dont delete empty
1236
state = self.current_dirstate()
1237
state._read_dirblocks_if_needed()
1238
ids_to_unversion = set(file_ids)
1239
paths_to_unversion = set()
1241
# check if the root is to be unversioned, if so, assert for now.
1242
# walk the state marking unversioned things as absent.
1243
# if there are any un-unversioned ids at the end, raise
1244
for key, details in state._dirblocks[0][1]:
1245
if (details[0][0] not in ('a', 'r') and # absent or relocated
1246
key[2] in ids_to_unversion):
1247
# I haven't written the code to unversion / yet - it should be
1249
raise errors.BzrError('Unversioning the / is not currently supported')
1251
while block_index < len(state._dirblocks):
1252
# process one directory at a time.
1253
block = state._dirblocks[block_index]
1254
# first check: is the path one to remove - it or its children
1255
delete_block = False
1256
for path in paths_to_unversion:
1257
if (block[0].startswith(path) and
1258
(len(block[0]) == len(path) or
1259
block[0][len(path)] == '/')):
1260
# this entire block should be deleted - its the block for a
1261
# path to unversion; or the child of one
1264
# TODO: trim paths_to_unversion as we pass by paths
1266
# this block is to be deleted: process it.
1267
# TODO: we can special case the no-parents case and
1268
# just forget the whole block.
1277
1269
entry_index = 0
1278
1270
while entry_index < len(block[1]):
1279
1271
entry = block[1][entry_index]
1280
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1281
# ^ some parent row.
1282
entry[0][2] not in ids_to_unversion):
1283
# ^ not an id to unversion
1286
if entry[1][0][0] == 'd':
1287
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1288
if not state._make_absent(entry):
1290
# we have unversioned this id
1291
ids_to_unversion.remove(entry[0][2])
1272
if entry[1][0][0] in 'ar':
1273
# don't remove absent or renamed entries
1276
# Mark this file id as having been removed
1277
ids_to_unversion.discard(entry[0][2])
1278
if not state._make_absent(entry):
1279
# The block has not shrunk.
1281
# go to the next block. (At the moment we dont delete empty
1292
1283
block_index += 1
1293
if ids_to_unversion:
1294
raise errors.NoSuchId(self, next(iter(ids_to_unversion)))
1295
self._make_dirty(reset_inventory=False)
1296
# have to change the legacy inventory too.
1297
if self._inventory is not None:
1298
for file_id in file_ids:
1299
if self._inventory.has_id(file_id):
1300
self._inventory.remove_recursive_id(file_id)
1286
while entry_index < len(block[1]):
1287
entry = block[1][entry_index]
1288
if (entry[1][0][0] in ('a', 'r') or # absent, relocated
1289
# ^ some parent row.
1290
entry[0][2] not in ids_to_unversion):
1291
# ^ not an id to unversion
1294
if entry[1][0][0] == 'd':
1295
paths_to_unversion.add(pathjoin(entry[0][0], entry[0][1]))
1296
if not state._make_absent(entry):
1298
# we have unversioned this id
1299
ids_to_unversion.remove(entry[0][2])
1301
if ids_to_unversion:
1302
raise errors.NoSuchId(self, iter(ids_to_unversion).next())
1303
self._make_dirty(reset_inventory=False)
1304
# have to change the legacy inventory too.
1305
if self._inventory is not None:
1306
for file_id in file_ids:
1307
if self._inventory.has_id(file_id):
1308
self._inventory.remove_recursive_id(file_id)
1310
@needs_tree_write_lock
1302
1311
def rename_one(self, from_rel, to_rel, after=False):
1303
1312
"""See WorkingTree.rename_one"""
1304
with self.lock_tree_write():
1306
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1314
super(DirStateWorkingTree, self).rename_one(from_rel, to_rel, after)
1316
@needs_tree_write_lock
1308
1317
def apply_inventory_delta(self, changes):
1309
1318
"""See MutableTree.apply_inventory_delta"""
1310
with self.lock_tree_write():
1311
state = self.current_dirstate()
1312
state.update_by_delta(changes)
1313
self._make_dirty(reset_inventory=True)
1319
state = self.current_dirstate()
1320
state.update_by_delta(changes)
1321
self._make_dirty(reset_inventory=True)
1315
1323
def update_basis_by_delta(self, new_revid, delta):
1316
1324
"""See MutableTree.update_basis_by_delta."""