17
17
"""Python implementations of Dirstate Helper functions."""
19
from __future__ import absolute_import
23
25
# We cannot import the dirstate module, because it loads this module
24
26
# All we really need is the IN_MEMORY_MODIFIED constant
25
from .dirstate import DirState, DirstateCorrupt
27
from brzlib import errors
28
from brzlib.dirstate import DirState
28
31
def pack_stat(st, _b64=binascii.b2a_base64, _pack=struct.Struct('>6L').pack):
35
38
# base64 encoding always adds a final newline, so strip it off
36
39
return _b64(_pack(st.st_size & 0xFFFFFFFF, int(st.st_mtime) & 0xFFFFFFFF,
37
int(st.st_ctime) & 0xFFFFFFFF, st.st_dev & 0xFFFFFFFF,
38
st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
40
int(st.st_ctime) & 0xFFFFFFFF, st.st_dev & 0xFFFFFFFF,
41
st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
41
44
def _unpack_stat(packed_stat):
151
154
cur_split = cache[cur]
153
cur_split = cur.split(b'/')
156
cur_split = cur.split('/')
154
157
cache[cur] = cur_split
155
if cur_split < dirname_split:
158
if cur_split < dirname_split: lo = mid + 1
162
def lt_by_dirs(path1, path2):
163
def cmp_by_dirs(path1, path2):
163
164
"""Compare two paths directory by directory.
165
166
This is equivalent to doing::
167
operator.lt(path1.split('/'), path2.split('/'))
168
cmp(path1.split('/'), path2.split('/'))
169
170
The idea is that you should compare path components separately. This
170
differs from plain ``path1 < path2`` for paths like ``'a-b'`` and ``a/b``.
171
"a-b" comes after "a" but would come before "a/b" lexically.
171
differs from plain ``cmp(path1, path2)`` for paths like ``'a-b'`` and
172
``a/b``. "a-b" comes after "a" but would come before "a/b" lexically.
173
174
:param path1: first path
174
175
:param path2: second path
175
:return: True if path1 comes first, otherwise False
176
:return: negative number if ``path1`` comes first,
177
0 if paths are equal,
178
and positive number if ``path2`` sorts first
177
if not isinstance(path1, bytes):
178
raise TypeError("'path1' must be a byte string, not %s: %r"
180
if not isinstance(path1, str):
181
raise TypeError("'path1' must be a plain string, not %s: %r"
179
182
% (type(path1), path1))
180
if not isinstance(path2, bytes):
181
raise TypeError("'path2' must be a byte string, not %s: %r"
183
if not isinstance(path2, str):
184
raise TypeError("'path2' must be a plain string, not %s: %r"
182
185
% (type(path2), path2))
183
return path1.split(b'/') < path2.split(b'/')
186
def _lt_path_by_dirblock(path1, path2):
186
return cmp(path1.split('/'), path2.split('/'))
189
def _cmp_path_by_dirblock(path1, path2):
187
190
"""Compare two paths based on what directory they are in.
189
192
This generates a sort order, such that all children of a directory are
193
196
:param path1: first path
194
197
:param path2: the second path
195
:return: True if path1 comes first, otherwise False
198
:return: negative number if ``path1`` comes first,
200
and a positive number if ``path2`` sorts first
197
if not isinstance(path1, bytes):
202
if not isinstance(path1, str):
198
203
raise TypeError("'path1' must be a plain string, not %s: %r"
199
204
% (type(path1), path1))
200
if not isinstance(path2, bytes):
205
if not isinstance(path2, str):
201
206
raise TypeError("'path2' must be a plain string, not %s: %r"
202
207
% (type(path2), path2))
203
208
dirname1, basename1 = os.path.split(path1)
204
key1 = (dirname1.split(b'/'), basename1)
209
key1 = (dirname1.split('/'), basename1)
205
210
dirname2, basename2 = os.path.split(path2)
206
key2 = (dirname2.split(b'/'), basename2)
211
key2 = (dirname2.split('/'), basename2)
212
return cmp(key1, key2)
210
215
def _read_dirblocks(state):
221
226
text = state._state_file.read()
222
227
# TODO: check the crc checksums. crc_measured = zlib.crc32(text)
224
fields = text.split(b'\0')
229
fields = text.split('\0')
225
230
# Remove the last blank entry
226
231
trailing = fields.pop()
228
raise DirstateCorrupt(state,
229
'trailing garbage: %r' % (trailing,))
233
raise errors.DirstateCorrupt(state,
234
'trailing garbage: %r' % (trailing,))
230
235
# consider turning fields into a tuple.
232
237
# skip the first field which is the trailing null from the header.
244
249
field_count = len(fields)
245
250
# this checks our adjustment, and also catches file too short.
246
251
if field_count - cur != expected_field_count:
247
raise DirstateCorrupt(state,
248
'field count incorrect %s != %s, entry_size=%s, '
249
'num_entries=%s fields=%r' % (
250
field_count - cur, expected_field_count, entry_size,
251
state._num_entries, fields))
252
raise errors.DirstateCorrupt(state,
253
'field count incorrect %s != %s, entry_size=%s, '\
254
'num_entries=%s fields=%r' % (
255
field_count - cur, expected_field_count, entry_size,
256
state._num_entries, fields))
253
258
if num_present_parents == 1:
254
259
# Bind external functions to local names
257
262
# them. Grab an straight iterator over the fields. (We use an
258
263
# iterator because we don't want to do a lot of additions, nor
259
264
# do we want to do a lot of slicing)
261
# Get a local reference to the compatible next method
262
next = getattr(_iter, '__next__', None)
265
next = iter(fields).next
265
266
# Move the iterator to the current position
267
for x in xrange(cur):
268
269
# The two blocks here are deliberate: the root block and the
269
270
# contents-of-root block.
270
state._dirblocks = [(b'', []), (b'', [])]
271
state._dirblocks = [('', []), ('', [])]
271
272
current_block = state._dirblocks[0][1]
272
current_dirname = b''
273
274
append_entry = current_block.append
274
for count in range(state._num_entries):
275
for count in xrange(state._num_entries):
284
285
# we know current_dirname == dirname, so re-use it to avoid
285
286
# creating new strings
286
287
entry = ((current_dirname, name, file_id),
289
next(), # fingerprint
291
next() == b'y', # executable
292
next(), # packed_stat or revision_id
296
next(), # fingerprint
298
next() == b'y', # executable
299
next(), # packed_stat or revision_id
290
next(), # fingerprint
292
next() == 'y', # executable
293
next(), # packed_stat or revision_id
297
next(), # fingerprint
299
next() == 'y', # executable
300
next(), # packed_stat or revision_id
302
303
trailing = next()
303
if trailing != b'\n':
304
305
raise ValueError("trailing garbage in dirstate: %r" % trailing)
305
306
# append the entry to the current block
306
307
append_entry(entry)
307
308
state._split_root_dirblock_into_contents()
309
310
fields_to_entry = state._get_fields_to_entry()
310
entries = [fields_to_entry(fields[pos:pos + entry_size])
311
for pos in range(cur, field_count, entry_size)]
311
entries = [fields_to_entry(fields[pos:pos+entry_size])
312
for pos in xrange(cur, field_count, entry_size)]
312
313
state._entries_to_current_state(entries)
313
314
# To convert from format 2 => format 3
314
315
# state._dirblocks = sorted(state._dirblocks,