2171
2171
This must be called with a lock held.
2173
# TODO: All entries must have some content that's not 'a' or 'r',
2174
# otherwise it could just be removed.
2176
# TODO: All relocations must point directly to a real entry.
2178
# TODO: No repeated keys.
2173
2181
from pprint import pformat
2174
# TODO: All entries should have the same length?
2182
self._read_dirblocks_if_needed()
2175
2183
if len(self._dirblocks) > 0:
2176
2184
assert self._dirblocks[0][0] == '', \
2177
2185
"dirblocks don't start with root block:\n" + \
2210
2218
# We check this with a dict per tree pointing either to the present
2211
2219
# name, or None if absent.
2213
all_file_ids = set()
2220
tree_count = self._num_present_parents() + 1
2221
id_path_maps = [dict() for i in range(tree_count)]
2214
2222
# Make sure that all renamed entries point to the correct location.
2215
2223
for entry in self._iter_entries():
2216
2224
file_id = entry[0][2]
2217
all_file_ids.add(file_id)
2225
this_path = osutils.pathjoin(entry[0][0], entry[0][1])
2226
assert len(entry[1]) == tree_count, \
2227
"wrong number of entry details for row\n%s" \
2228
",\nexpected %d" % \
2229
(pformat(entry), tree_count)
2218
2230
for tree_index, tree_state in enumerate(entry[1]):
2219
if tree_state[0] == 'r': # Renamed entry
2220
target_location = tree_state[1]
2221
other_entry = self._get_entry(tree_index,
2222
path_utf8=target_location)
2223
this_path = osutils.pathjoin(entry[0][0], entry[0][1])
2224
other_path = osutils.pathjoin(other_entry[0][0],
2226
assert entry[0][2] == other_entry[0][2], \
2227
('A rename entry points to a record with a different'
2228
' file id. %s => %s'
2229
% (pformat(entry), pformat(other_entry)))
2230
# there must be 'rename' pointers between all occurrences
2231
# of this file_id in all trees.
2233
# TODO: If there's only a single tree (no basis
2234
# revisions), there must not be any rename markers.
2236
# TODO: If there's more than 2 trees, we should still be
2237
# able to check that all the renames line up but it'll be
2240
# these are disabled because i think the assertions
2241
# they encode are wrong: there is no necessary link
2242
# between the rename pointers in one tree and in another.
2245
## if len(entry[1]) == 2: # Check the rename is symmetric
2246
## if tree_index == 0:
2250
## assert other_entry[1][other_index][0] == 'r', \
2251
## ('a rename points to a record which '
2252
## 'does not have a reverse rename pointer '
2254
## % (pformat(entry), pformat(other_entry)))
2255
## assert other_entry[1][other_index][1] == this_path, \
2256
## ('a rename points to a record which points to a'
2257
## ' different location.\n'
2260
## % (pformat(entry), pformat(other_entry),
2231
this_tree_map = id_path_maps[tree_index]
2232
minikind = tree_state[0]
2233
# have we seen this id before in this column?
2234
if file_id in this_tree_map:
2235
previous_path = this_tree_map[file_id]
2236
# any later mention of this file must be consistent with
2237
# what was said before
2239
assert previous_path is None, \
2240
"file %s is absent in row %r but also present " \
2242
(file_id, entry, previous_path)
2243
elif minikind == 'r':
2244
target_location = tree_state[1]
2245
assert previous_path == target_location, \
2246
"file %s relocation in row %r but also at %r" \
2247
% (file_id, entry, previous_path)
2249
# a file, directory, etc - may have been previously
2250
# pointed to by a relocation, which must point here
2251
assert previous_path == this_path, \
2252
"entry %r inconsistent with previous path %r" % \
2253
(entry, previous_path)
2256
# absent; should not occur anywhere else
2257
this_tree_map[file_id] = None
2258
elif minikind == 'r':
2259
# relocation, must occur at expected location
2260
this_tree_map[file_id] = tree_state[1]
2262
this_tree_map[file_id] = this_path
2263
2264
def _wipe_state(self):
2264
2265
"""Forget all state information about the dirstate."""