224
223
"""This just keeps track of information as we are bisecting."""
227
def pack_stat(st, _encode=binascii.b2a_base64, _pack=struct.pack):
228
"""Convert stat values into a packed representation."""
229
# jam 20060614 it isn't really worth removing more entries if we
230
# are going to leave it in packed form.
231
# With only st_mtime and st_mode filesize is 5.5M and read time is 275ms
232
# With all entries filesize is 5.9M and read time is mabye 280ms
233
# well within the noise margin
235
# base64 encoding always adds a final newline, so strip it off
236
# The current version
237
return _encode(_pack('>LLLLLL'
238
, st.st_size, int(st.st_mtime), int(st.st_ctime)
239
, st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
240
# This is 0.060s / 1.520s faster by not encoding as much information
241
# return _encode(_pack('>LL', int(st.st_mtime), st.st_mode))[:-1]
242
# This is not strictly faster than _encode(_pack())[:-1]
243
# return '%X.%X.%X.%X.%X.%X' % (
244
# st.st_size, int(st.st_mtime), int(st.st_ctime),
245
# st.st_dev, st.st_ino, st.st_mode)
246
# Similar to the _encode(_pack('>LL'))
247
# return '%X.%X' % (int(st.st_mtime), st.st_mode)
250
226
class DirState(object):
251
227
"""Record directory and metadata state for fast access.
1091
def update_entry(self, entry, abspath, stat_value,
1092
_stat_to_minikind=_stat_to_minikind,
1093
_pack_stat=pack_stat):
1062
def update_entry(self, entry, abspath, stat_value=None):
1094
1063
"""Update the entry based on what is actually on disk.
1096
1065
:param entry: This is the dirblock entry for the file in question.
1100
1069
:return: The sha1 hexdigest of the file (40 bytes) or link target of a
1072
# This code assumes that the entry passed in is directly held in one of
1073
# the internal _dirblocks. So the dirblock state must have already been
1075
assert self._dirblock_state != DirState.NOT_IN_MEMORY
1076
if stat_value is None:
1078
# We could inline os.lstat but the common case is that
1079
# stat_value will be passed in, not read here.
1080
stat_value = self._lstat(abspath, entry)
1081
except (OSError, IOError), e:
1082
if e.errno in (errno.ENOENT, errno.EACCES,
1084
# The entry is missing, consider it gone
1088
kind = osutils.file_kind_from_stat_mode(stat_value.st_mode)
1104
minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
1090
minikind = DirState._kind_to_minikind[kind]
1091
except KeyError: # Unknown kind
1108
packed_stat = _pack_stat(stat_value)
1093
packed_stat = pack_stat(stat_value)
1109
1094
(saved_minikind, saved_link_or_sha1, saved_file_size,
1110
1095
saved_executable, saved_packed_stat) = entry[1][0]
1112
1097
if (minikind == saved_minikind
1113
and packed_stat == saved_packed_stat):
1114
# The stat hasn't changed since we saved, so we can re-use the
1098
and packed_stat == saved_packed_stat
1099
# size should also be in packed_stat
1100
and saved_file_size == stat_value.st_size):
1101
# The stat hasn't changed since we saved, so we can potentially
1102
# re-use the saved sha hash.
1116
1103
if minikind == 'd':
1119
# size should also be in packed_stat
1120
if saved_file_size == stat_value.st_size:
1106
if self._cutoff_time is None:
1107
self._sha_cutoff_time()
1109
if (stat_value.st_mtime < self._cutoff_time
1110
and stat_value.st_ctime < self._cutoff_time):
1111
# Return the existing fingerprint
1121
1112
return saved_link_or_sha1
1123
1114
# If we have gotten this far, that means that we need to actually
1127
1118
link_or_sha1 = self._sha1_file(abspath, entry)
1128
1119
executable = self._is_executable(stat_value.st_mode,
1129
1120
saved_executable)
1130
if self._cutoff_time is None:
1131
self._sha_cutoff_time()
1132
if (stat_value.st_mtime < self._cutoff_time
1133
and stat_value.st_ctime < self._cutoff_time):
1134
entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
1135
executable, packed_stat)
1137
entry[1][0] = ('f', '', stat_value.st_size,
1138
executable, DirState.NULLSTAT)
1121
entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
1122
executable, packed_stat)
1139
1123
elif minikind == 'd':
1140
1124
link_or_sha1 = None
1141
1125
entry[1][0] = ('d', '', 0, False, packed_stat)
1149
1133
osutils.pathjoin(entry[0][0], entry[0][1]))
1150
1134
elif minikind == 'l':
1151
1135
link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
1152
if self._cutoff_time is None:
1153
self._sha_cutoff_time()
1154
if (stat_value.st_mtime < self._cutoff_time
1155
and stat_value.st_ctime < self._cutoff_time):
1156
entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
1159
entry[1][0] = ('l', '', stat_value.st_size,
1160
False, DirState.NULLSTAT)
1136
entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
1161
1138
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1162
1139
return link_or_sha1
1430
1407
The new dirstate will be an empty tree - that is it has no parents,
1431
1408
and only a root node - which has id ROOT_ID.
1410
The object will be write locked when returned to the caller,
1411
unless there was an exception in the writing, in which case it
1433
1414
:param path: The name of the file for the dirstate.
1434
:return: A write-locked DirState object.
1415
:return: A DirState object.
1436
1417
# This constructs a new DirState object on a path, sets the _state_file
1437
1418
# to a new empty file for that path. It then calls _set_data() with our
1922
1904
self._header_state = DirState.IN_MEMORY_MODIFIED
1923
1905
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1924
1906
self._id_index = id_index
1926
1909
def _sort_entries(self, entry_list):
1927
1910
"""Given a list of entries, sort them into the right order.
2007
1990
# both sides are dealt with, move on
2008
1991
current_old = advance(old_iterator)
2009
1992
current_new = advance(new_iterator)
2010
elif (new_entry_key[0].split('/') < current_old[0][0].split('/')
2011
and new_entry_key[1:] < current_old[0][1:]):
1993
elif new_entry_key < current_old[0]:
2012
1994
# new comes before:
2013
1995
# add a entry for this and advance new
2014
1996
self.update_minimal(new_entry_key, current_new_minikind,
2234
2216
"dirblock for %r is not sorted:\n%s" % \
2235
2217
(dirblock[0], pformat(dirblock)))
2238
def check_valid_parent():
2239
"""Check that the current entry has a valid parent.
2241
This makes sure that the parent has a record,
2242
and that the parent isn't marked as "absent" in the
2243
current tree. (It is invalid to have a non-absent file in an absent
2246
if entry[0][0:2] == ('', ''):
2247
# There should be no parent for the root row
2249
parent_entry = self._get_entry(tree_index, path_utf8=entry[0][0])
2250
if parent_entry == (None, None):
2251
raise AssertionError(
2252
"no parent entry for: %s in tree %s"
2253
% (this_path, tree_index))
2254
if parent_entry[1][tree_index][0] != 'd':
2255
raise AssertionError(
2256
"Parent entry for %s is not marked as a valid"
2257
" directory. %s" % (this_path, parent_entry,))
2259
2219
# For each file id, for each tree: either
2260
2220
# the file id is not present at all; all rows with that id in the
2261
2221
# key have it marked as 'absent'
2401
2359
if cur_split < dirname_split: lo = mid+1
2365
def pack_stat(st, _encode=base64.encodestring, _pack=struct.pack):
2366
"""Convert stat values into a packed representation."""
2367
# jam 20060614 it isn't really worth removing more entries if we
2368
# are going to leave it in packed form.
2369
# With only st_mtime and st_mode filesize is 5.5M and read time is 275ms
2370
# With all entries filesize is 5.9M and read time is mabye 280ms
2371
# well within the noise margin
2373
# base64.encode always adds a final newline, so strip it off
2374
return _encode(_pack('>LLLLLL'
2375
, st.st_size, int(st.st_mtime), int(st.st_ctime)
2376
, st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]