1692
1692
if (self._header_state == DirState.IN_MEMORY_MODIFIED or
1693
1693
self._dirblock_state == DirState.IN_MEMORY_MODIFIED):
1695
grabbed_write_lock = False
1696
if self._lock_state != 'w':
1697
grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
1698
# Switch over to the new lock, as the old one may be closed.
1699
# TODO: jam 20070315 We should validate the disk file has
1700
# not changed contents. Since temporary_write_lock may
1701
# not be an atomic operation.
1702
self._lock_token = new_lock
1703
self._state_file = new_lock.f
1704
if not grabbed_write_lock:
1705
# We couldn't grab a write lock, so we switch back to a read one
1695
if self._lock_state == 'w':
1696
out_file = self._state_file
1699
# Try to grab a write lock so that we can update the file.
1701
wlock = lock.WriteLock(self._filename)
1702
except (errors.LockError, errors.LockContention), e:
1703
# We couldn't grab the lock, so just leave things dirty in
1707
# This may be a read-only tree, or someone else may have a
1708
# ReadLock. so handle the case when we cannot grab a write
1710
if e.errno in (errno.ENOENT, errno.EPERM, errno.EACCES,
1712
# Ignore these errors and just don't save anything
1708
self._state_file.seek(0)
1709
self._state_file.writelines(self.get_lines())
1710
self._state_file.truncate()
1711
self._state_file.flush()
1718
out_file.writelines(self.get_lines())
1712
1721
self._header_state = DirState.IN_MEMORY_UNMODIFIED
1713
1722
self._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
1715
if grabbed_write_lock:
1716
self._lock_token = self._lock_token.restore_read_lock()
1717
self._state_file = self._lock_token.f
1718
# TODO: jam 20070315 We should validate the disk file has
1719
# not changed contents. Since restore_read_lock may
1720
# not be an atomic operation.
1724
if wlock is not None:
1722
1727
def _set_data(self, parent_ids, dirblocks):
1723
1728
"""Set the full dirstate data in memory.
2168
2173
This can be useful in debugging; it shouldn't be necessary in
2171
This must be called with a lock held.
2173
# NOTE: This must always raise AssertionError not just assert,
2174
# otherwise it may not behave properly under python -O
2176
# TODO: All entries must have some content that's not 'a' or 'r',
2177
# otherwise it could just be removed.
2179
# TODO: All relocations must point directly to a real entry.
2181
# TODO: No repeated keys.
2184
2176
from pprint import pformat
2185
self._read_dirblocks_if_needed()
2186
2177
if len(self._dirblocks) > 0:
2187
if not self._dirblocks[0][0] == '':
2188
raise AssertionError(
2178
assert self._dirblocks[0][0] == '', \
2189
2179
"dirblocks don't start with root block:\n" + \
2191
2181
if len(self._dirblocks) > 1:
2192
if not self._dirblocks[1][0] == '':
2193
raise AssertionError(
2182
assert self._dirblocks[1][0] == '', \
2194
2183
"dirblocks missing root directory:\n" + \
2196
2185
# the dirblocks are sorted by their path components, name, and dir id
2197
2186
dir_names = [d[0].split('/')
2198
2187
for d in self._dirblocks[1:]]
2205
2194
for dirblock in self._dirblocks:
2206
2195
# within each dirblock, the entries are sorted by filename and
2208
for entry in dirblock[1]:
2209
if dirblock[0] != entry[0][0]:
2210
raise AssertionError(
2212
"doesn't match directory name in\n%r" %
2213
(entry, pformat(dirblock)))
2214
if dirblock[1] != sorted(dirblock[1]):
2215
raise AssertionError(
2216
"dirblock for %r is not sorted:\n%s" % \
2217
(dirblock[0], pformat(dirblock)))
2219
# For each file id, for each tree: either
2220
# the file id is not present at all; all rows with that id in the
2221
# key have it marked as 'absent'
2222
# OR the file id is present under exactly one name; any other entries
2223
# that mention that id point to the correct name.
2225
# We check this with a dict per tree pointing either to the present
2226
# name, or None if absent.
2227
tree_count = self._num_present_parents() + 1
2228
id_path_maps = [dict() for i in range(tree_count)]
2229
# Make sure that all renamed entries point to the correct location.
2230
for entry in self._iter_entries():
2231
file_id = entry[0][2]
2232
this_path = osutils.pathjoin(entry[0][0], entry[0][1])
2233
if len(entry[1]) != tree_count:
2234
raise AssertionError(
2235
"wrong number of entry details for row\n%s" \
2236
",\nexpected %d" % \
2237
(pformat(entry), tree_count))
2238
for tree_index, tree_state in enumerate(entry[1]):
2239
this_tree_map = id_path_maps[tree_index]
2240
minikind = tree_state[0]
2241
# have we seen this id before in this column?
2242
if file_id in this_tree_map:
2243
previous_path = this_tree_map[file_id]
2244
# any later mention of this file must be consistent with
2245
# what was said before
2247
if previous_path is not None:
2248
raise AssertionError(
2249
"file %s is absent in row %r but also present " \
2251
(file_id, entry, previous_path))
2252
elif minikind == 'r':
2253
target_location = tree_state[1]
2254
if previous_path != target_location:
2255
raise AssertionError(
2256
"file %s relocation in row %r but also at %r" \
2257
% (file_id, entry, previous_path))
2259
# a file, directory, etc - may have been previously
2260
# pointed to by a relocation, which must point here
2261
if previous_path != this_path:
2262
raise AssertionError(
2263
"entry %r inconsistent with previous path %r" % \
2264
(entry, previous_path))
2267
# absent; should not occur anywhere else
2268
this_tree_map[file_id] = None
2269
elif minikind == 'r':
2270
# relocation, must occur at expected location
2271
this_tree_map[file_id] = tree_state[1]
2273
this_tree_map[file_id] = this_path
2197
assert dirblock[1] == sorted(dirblock[1]), \
2198
"dirblock for %r is not sorted:\n%s" % \
2199
(dirblock[0], pformat(dirblock))
2275
2201
def _wipe_state(self):
2276
2202
"""Forget all state information about the dirstate."""
2371
2297
# well within the noise margin
2373
2299
# base64.encode always adds a final newline, so strip it off
2374
return _encode(_pack('>LLLLLL'
2300
return _encode(_pack('>llllll'
2375
2301
, st.st_size, int(st.st_mtime), int(st.st_ctime)
2376
, st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
2302
, st.st_dev, st.st_ino, st.st_mode))[:-1]