17
17
"""Python implementations of Dirstate Helper functions."""
19
from __future__ import absolute_import
21
25
# We cannot import the dirstate module, because it loads this module
22
26
# All we really need is the IN_MEMORY_MODIFIED constant
23
from bzrlib import errors
24
from bzrlib.dirstate import DirState
27
from breezy import errors
28
from .dirstate import DirState
34
def pack_stat(st, _b64=binascii.b2a_base64, _pack=struct.Struct('>6L').pack):
35
"""Convert stat values into a packed representation
37
Not all of the fields from the stat included are strictly needed, and by
38
just encoding the mtime and mode a slight speed increase could be gained.
39
However, using the pyrex version instead is a bigger win.
41
# base64 encoding always adds a final newline, so strip it off
42
return _b64(_pack(st.st_size & 0xFFFFFFFF, int(st.st_mtime) & 0xFFFFFFFF,
43
int(st.st_ctime) & 0xFFFFFFFF, st.st_dev & 0xFFFFFFFF,
44
st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
47
def _unpack_stat(packed_stat):
48
"""Turn a packed_stat back into the stat fields.
50
This is meant as a debugging tool, should not be used in real code.
52
(st_size, st_mtime, st_ctime, st_dev, st_ino,
53
st_mode) = struct.unpack('>6L', binascii.a2b_base64(packed_stat))
54
return dict(st_size=st_size, st_mtime=st_mtime, st_ctime=st_ctime,
55
st_dev=st_dev, st_ino=st_ino, st_mode=st_mode)
27
58
def _bisect_path_left(paths, path):
126
157
cur_split = cache[cur]
128
cur_split = cur.split('/')
159
cur_split = cur.split(b'/')
129
160
cache[cur] = cur_split
130
161
if cur_split < dirname_split: lo = mid + 1
135
def cmp_by_dirs(path1, path2):
166
def lt_by_dirs(path1, path2):
136
167
"""Compare two paths directory by directory.
138
169
This is equivalent to doing::
140
cmp(path1.split('/'), path2.split('/'))
171
operator.lt(path1.split('/'), path2.split('/'))
142
173
The idea is that you should compare path components separately. This
143
differs from plain ``cmp(path1, path2)`` for paths like ``'a-b'`` and
144
``a/b``. "a-b" comes after "a" but would come before "a/b" lexically.
174
differs from plain ``path1 < path2`` for paths like ``'a-b'`` and ``a/b``.
175
"a-b" comes after "a" but would come before "a/b" lexically.
146
177
:param path1: first path
147
178
:param path2: second path
148
:return: negative number if ``path1`` comes first,
149
0 if paths are equal,
150
and positive number if ``path2`` sorts first
179
:return: True if path1 comes first, otherwise False
152
if not isinstance(path1, str):
181
if not isinstance(path1, bytes):
153
182
raise TypeError("'path1' must be a plain string, not %s: %r"
154
183
% (type(path1), path1))
155
if not isinstance(path2, str):
184
if not isinstance(path2, bytes):
156
185
raise TypeError("'path2' must be a plain string, not %s: %r"
157
186
% (type(path2), path2))
158
return cmp(path1.split('/'), path2.split('/'))
161
def _cmp_path_by_dirblock(path1, path2):
187
return path1.split('/') < path2.split('/')
190
def _lt_path_by_dirblock(path1, path2):
162
191
"""Compare two paths based on what directory they are in.
164
193
This generates a sort order, such that all children of a directory are
168
197
:param path1: first path
169
198
:param path2: the second path
170
:return: negative number if ``path1`` comes first,
172
and a positive number if ``path2`` sorts first
199
:return: True if path1 comes first, otherwise False
174
if not isinstance(path1, str):
201
if not isinstance(path1, bytes):
175
202
raise TypeError("'path1' must be a plain string, not %s: %r"
176
203
% (type(path1), path1))
177
if not isinstance(path2, str):
204
if not isinstance(path2, bytes):
178
205
raise TypeError("'path2' must be a plain string, not %s: %r"
179
206
% (type(path2), path2))
180
207
dirname1, basename1 = os.path.split(path1)
181
key1 = (dirname1.split('/'), basename1)
208
key1 = (dirname1.split(b'/'), basename1)
182
209
dirname2, basename2 = os.path.split(path2)
183
210
key2 = (dirname2.split('/'), basename2)
184
return cmp(key1, key2)
187
214
def _read_dirblocks(state):
198
225
text = state._state_file.read()
199
226
# TODO: check the crc checksums. crc_measured = zlib.crc32(text)
201
fields = text.split('\0')
228
fields = text.split(b'\0')
202
229
# Remove the last blank entry
203
230
trailing = fields.pop()
205
232
raise errors.DirstateCorrupt(state,
206
233
'trailing garbage: %r' % (trailing,))
207
234
# consider turning fields into a tuple.
234
261
# them. Grab an straight iterator over the fields. (We use an
235
262
# iterator because we don't want to do a lot of additions, nor
236
263
# do we want to do a lot of slicing)
237
next = iter(fields).next
265
# Get a local reference to the compatible next method
266
next = getattr(_iter, '__next__', None)
238
269
# Move the iterator to the current position
239
for x in xrange(cur):
241
272
# The two blocks here are deliberate: the root block and the
242
273
# contents-of-root block.
261
292
next(), # minikind
262
293
next(), # fingerprint
263
294
_int(next()), # size
264
next() == 'y', # executable
295
next() == b'y', # executable
265
296
next(), # packed_stat or revision_id
268
299
next(), # minikind
269
300
next(), # fingerprint
270
301
_int(next()), # size
271
next() == 'y', # executable
302
next() == b'y', # executable
272
303
next(), # packed_stat or revision_id
282
313
fields_to_entry = state._get_fields_to_entry()
283
314
entries = [fields_to_entry(fields[pos:pos+entry_size])
284
for pos in xrange(cur, field_count, entry_size)]
315
for pos in range(cur, field_count, entry_size)]
285
316
state._entries_to_current_state(entries)
286
317
# To convert from format 2 => format 3
287
318
# state._dirblocks = sorted(state._dirblocks,