31
from .. import cache_utf8, errors, osutils
32
from .dirstate import DirState, DirstateCorrupt
33
from ..osutils import parent_directories, pathjoin, splitpath, is_inside_any, is_inside
34
from ..tree import TreeChange
29
from bzrlib import cache_utf8, errors, osutils
30
from bzrlib.dirstate import DirState
31
from bzrlib.osutils import parent_directories, pathjoin, splitpath
37
34
# This is the Windows equivalent of ENOTDIR
98
97
object PyTuple_GetItem_void_object "PyTuple_GET_ITEM" (void* tpl, int index)
99
98
object PyTuple_GET_ITEM(object tpl, Py_ssize_t index)
101
unsigned long PyInt_AsUnsignedLongMask(object number) except? -1
103
char *PyBytes_AsString(object p)
104
char *PyBytes_AsString_obj "PyBytes_AsString" (PyObject *string)
105
char *PyBytes_AS_STRING_void "PyBytes_AS_STRING" (void *p)
106
int PyBytes_AsStringAndSize(object str, char **buffer, Py_ssize_t *length) except -1
107
object PyBytes_FromString(char *)
108
object PyBytes_FromStringAndSize(char *, Py_ssize_t)
109
int PyBytes_Size(object p)
110
int PyBytes_GET_SIZE_void "PyBytes_GET_SIZE" (void *p)
111
int PyBytes_CheckExact(object p)
112
int PyFloat_Check(object p)
113
double PyFloat_AsDouble(object p)
114
int PyLong_Check(object p)
101
char *PyString_AsString(object p)
102
char *PyString_AsString_obj "PyString_AsString" (PyObject *string)
103
char *PyString_AS_STRING_void "PyString_AS_STRING" (void *p)
104
int PyString_AsStringAndSize(object str, char **buffer, Py_ssize_t *length) except -1
105
object PyString_FromString(char *)
106
object PyString_FromStringAndSize(char *, Py_ssize_t)
107
int PyString_Size(object p)
108
int PyString_GET_SIZE_void "PyString_GET_SIZE" (void *p)
109
int PyString_CheckExact(object p)
115
110
void Py_INCREF(object o)
116
111
void Py_DECREF(object o)
120
115
int strncmp(char *s1, char *s2, int len)
121
116
void *memchr(void *s, int c, size_t len)
122
117
int memcmp(void *b1, void *b2, size_t len)
124
from ._str_helpers cimport (
126
safe_string_from_size,
129
from .._static_tuple_c cimport (
130
import_static_tuple_c,
136
import_static_tuple_c()
118
# ??? memrchr is a GNU extension :(
119
# void *memrchr(void *s, int c, size_t len)
122
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
123
# memrchr seems to be a GNU extension, so we have to implement it ourselves
139
136
def _py_memrchr(s, c):
151
assert PyBytes_Size(c) == 1, 'Expected single character string not %r' % c
152
_c = PyBytes_AsString(c)
153
_s = PyBytes_AsString(s)
154
length = PyBytes_Size(s)
148
_s = PyString_AsString(s)
149
length = PyString_Size(s)
151
_c = PyString_AsString(c)
152
assert PyString_Size(c) == 1,\
153
'Must be a single character string, not %s' % (c,)
156
154
found = _my_memrchr(_s, _c[0], length)
157
155
if found == NULL:
159
157
return <char*>found - <char*>_s
160
cdef object safe_string_from_size(char *s, Py_ssize_t size):
162
raise AssertionError(
163
'tried to create a string with an invalid size: %d'
165
return PyString_FromStringAndSize(s, size)
162
168
cdef int _is_aligned(void *ptr): # cannot_raise
163
169
"""Is this pointer aligned to an integer size offset?
234
def lt_by_dirs(path1, path2):
240
def cmp_by_dirs(path1, path2):
235
241
"""Compare two paths directory by directory.
237
243
This is equivalent to doing::
239
operator.lt(path1.split('/'), path2.split('/'))
245
cmp(path1.split('/'), path2.split('/'))
241
247
The idea is that you should compare path components separately. This
242
differs from plain ``path1 < path2`` for paths like ``'a-b'`` and ``a/b``.
243
"a-b" comes after "a" but would come before "a/b" lexically.
248
differs from plain ``cmp(path1, path2)`` for paths like ``'a-b'`` and
249
``a/b``. "a-b" comes after "a" but would come before "a/b" lexically.
245
251
:param path1: first path
246
252
:param path2: second path
247
:return: True if path1 comes first, otherwise False
253
:return: negative number if ``path1`` comes first,
254
0 if paths are equal,
255
and positive number if ``path2`` sorts first
249
if not PyBytes_CheckExact(path1):
250
raise TypeError("'path1' must be a bytes string, not %s: %r"
257
if not PyString_CheckExact(path1):
258
raise TypeError("'path1' must be a plain string, not %s: %r"
251
259
% (type(path1), path1))
252
if not PyBytes_CheckExact(path2):
253
raise TypeError("'path2' must be a bytes string, not %s: %r"
260
if not PyString_CheckExact(path2):
261
raise TypeError("'path2' must be a plain string, not %s: %r"
254
262
% (type(path2), path2))
255
return -1 == _cmp_by_dirs(PyBytes_AsString(path1),
257
PyBytes_AsString(path2),
261
def _lt_path_by_dirblock(path1, path2):
263
return _cmp_by_dirs(PyString_AsString(path1),
264
PyString_Size(path1),
265
PyString_AsString(path2),
266
PyString_Size(path2))
269
def _cmp_path_by_dirblock(path1, path2):
262
270
"""Compare two paths based on what directory they are in.
264
272
This generates a sort order, such that all children of a directory are
266
274
children appear. But all grandchildren come after all children.
268
276
In other words, all entries in a directory are sorted together, and
269
directories are sorted in cmp_by_dirs order.
277
directorys are sorted in cmp_by_dirs order.
271
279
:param path1: first path
272
280
:param path2: the second path
273
:return: True if path1 comes first, otherwise False.
281
:return: negative number if ``path1`` comes first,
283
and a positive number if ``path2`` sorts first
275
if not PyBytes_CheckExact(path1):
285
if not PyString_CheckExact(path1):
276
286
raise TypeError("'path1' must be a plain string, not %s: %r"
277
287
% (type(path1), path1))
278
if not PyBytes_CheckExact(path2):
288
if not PyString_CheckExact(path2):
279
289
raise TypeError("'path2' must be a plain string, not %s: %r"
280
290
% (type(path2), path2))
281
# GZ 2017-06-09: This internal function really only needs lt as well.
282
return (_cmp_path_by_dirblock_intern(PyBytes_AsString(path1),
284
PyBytes_AsString(path2),
285
PyBytes_Size(path2)) < 0)
291
return _cmp_path_by_dirblock_intern(PyString_AsString(path1),
292
PyString_Size(path1),
293
PyString_AsString(path2),
294
PyString_Size(path2))
288
297
cdef int _cmp_path_by_dirblock_intern(char *path1, int path1_len,
327
336
basename1 = basename1 + 1
328
337
basename1_len = path1_len - dirname1_len - 1
330
basename2 = <char*>_my_memrchr(path2, b'/', path2_len)
339
basename2 = <char*>_my_memrchr(path2, c'/', path2_len)
332
341
if basename2 == NULL:
333
342
basename2 = path2
334
343
basename2_len = path2_len
389
398
if not PyList_CheckExact(paths):
390
399
raise TypeError("you must pass a python list for 'paths' not: %s %r"
391
400
% (type(paths), paths))
392
if not PyBytes_CheckExact(path):
401
if not PyString_CheckExact(path):
393
402
raise TypeError("you must pass a string for 'path' not: %s %r"
394
403
% (type(path), path))
399
path_cstr = PyBytes_AsString(path)
400
path_size = PyBytes_Size(path)
408
path_cstr = PyString_AsString(path)
409
path_size = PyString_Size(path)
403
_mid = (_lo + _hi) // 2
412
_mid = (_lo + _hi) / 2
404
413
cur = PyList_GetItem_object_void(paths, _mid)
405
cur_cstr = PyBytes_AS_STRING_void(cur)
406
cur_size = PyBytes_GET_SIZE_void(cur)
414
cur_cstr = PyString_AS_STRING_void(cur)
415
cur_size = PyString_GET_SIZE_void(cur)
407
416
if _cmp_path_by_dirblock_intern(cur_cstr, cur_size,
408
417
path_cstr, path_size) < 0:
442
451
if not PyList_CheckExact(paths):
443
452
raise TypeError("you must pass a python list for 'paths' not: %s %r"
444
453
% (type(paths), paths))
445
if not PyBytes_CheckExact(path):
454
if not PyString_CheckExact(path):
446
455
raise TypeError("you must pass a string for 'path' not: %s %r"
447
456
% (type(path), path))
452
path_cstr = PyBytes_AsString(path)
453
path_size = PyBytes_Size(path)
461
path_cstr = PyString_AsString(path)
462
path_size = PyString_Size(path)
456
_mid = (_lo + _hi) // 2
465
_mid = (_lo + _hi) / 2
457
466
cur = PyList_GetItem_object_void(paths, _mid)
458
cur_cstr = PyBytes_AS_STRING_void(cur)
459
cur_size = PyBytes_GET_SIZE_void(cur)
467
cur_cstr = PyString_AS_STRING_void(cur)
468
cur_size = PyString_GET_SIZE_void(cur)
460
469
if _cmp_path_by_dirblock_intern(path_cstr, path_size,
461
470
cur_cstr, cur_size) < 0:
487
496
if not PyList_CheckExact(dirblocks):
488
497
raise TypeError("you must pass a python list for 'dirblocks' not: %s %r"
489
498
% (type(dirblocks), dirblocks))
490
if not PyBytes_CheckExact(dirname):
499
if not PyString_CheckExact(dirname):
491
500
raise TypeError("you must pass a string for dirname not: %s %r"
492
501
% (type(dirname), dirname))
499
dirname_cstr = PyBytes_AsString(dirname)
500
dirname_size = PyBytes_Size(dirname)
508
dirname_cstr = PyString_AsString(dirname)
509
dirname_size = PyString_Size(dirname)
503
_mid = (_lo + _hi) // 2
512
_mid = (_lo + _hi) / 2
504
513
# Grab the dirname for the current dirblock
505
514
# cur = dirblocks[_mid][0]
506
515
cur = PyTuple_GetItem_void_void(
507
516
PyList_GetItem_object_void(dirblocks, _mid), 0)
508
cur_cstr = PyBytes_AS_STRING_void(cur)
509
cur_size = PyBytes_GET_SIZE_void(cur)
517
cur_cstr = PyString_AS_STRING_void(cur)
518
cur_size = PyString_GET_SIZE_void(cur)
510
519
if _cmp_by_dirs(cur_cstr, cur_size, dirname_cstr, dirname_size) < 0:
529
538
def __init__(self, text, state):
530
539
self.state = state
532
self.text_cstr = PyBytes_AsString(text)
533
self.text_size = PyBytes_Size(text)
541
self.text_cstr = PyString_AsString(text)
542
self.text_size = PyString_Size(text)
534
543
self.end_cstr = self.text_cstr + self.text_size
535
544
self.cur_cstr = self.text_cstr
545
554
raise AssertionError('get_next() called when there are no chars'
547
556
next = self.cur_cstr
548
self.cur_cstr = <char*>memchr(next, b'\0', self.end_cstr - next)
557
self.cur_cstr = <char*>memchr(next, c'\0', self.end_cstr - next)
549
558
if self.cur_cstr == NULL:
550
559
extra_len = self.end_cstr - next
551
raise DirstateCorrupt(self.state,
560
raise errors.DirstateCorrupt(self.state,
552
561
'failed to find trailing NULL (\\0).'
553
562
' Trailing garbage: %r'
554
563
% safe_string_from_size(next, extra_len))
591
600
columns, then "current" columns, and then "parent" columns.
593
602
:param num_trees: How many parent trees need to be parsed
594
:param p_current_dirname: A pointer to the current PyBytes
603
:param p_current_dirname: A pointer to the current PyString
595
604
representing the directory name.
596
605
We pass this in as a void * so that pyrex doesn't have to
597
606
increment/decrement the PyObject reference counter for each
601
610
:param new_block: This is to let the caller know that it needs to
602
611
create a new directory block to store the next entry.
604
cdef StaticTuple path_name_file_id_key
613
cdef object path_name_file_id_key
606
614
cdef char *entry_size_cstr
607
615
cdef unsigned long int entry_size
608
616
cdef char* executable_cstr
618
626
# Read the 'key' information (dirname, name, file_id)
619
627
dirname_cstr = self.get_next(&cur_size)
620
628
# Check to see if we have started a new directory block.
621
# If so, then we need to create a new dirname PyBytes, so that it can
629
# If so, then we need to create a new dirname PyString, so that it can
622
630
# be used in all of the tuples. This saves time and memory, by re-using
623
631
# the same object repeatedly.
625
633
# Do the cheap 'length of string' check first. If the string is a
626
634
# different length, then we *have* to be a different directory.
627
if (cur_size != PyBytes_GET_SIZE_void(p_current_dirname[0])
635
if (cur_size != PyString_GET_SIZE_void(p_current_dirname[0])
628
636
or strncmp(dirname_cstr,
629
637
# Extract the char* from our current dirname string. We
630
# know it is a PyBytes, so we can use
631
# PyBytes_AS_STRING, we use the _void version because
638
# know it is a PyString, so we can use
639
# PyString_AS_STRING, we use the _void version because
632
640
# we are tricking Pyrex by using a void* rather than an
634
PyBytes_AS_STRING_void(p_current_dirname[0]),
642
PyString_AS_STRING_void(p_current_dirname[0]),
635
643
cur_size+1) != 0):
636
644
dirname = safe_string_from_size(dirname_cstr, cur_size)
637
645
p_current_dirname[0] = <void*>dirname
642
650
# Build up the key that will be used.
643
651
# By using <object>(void *) Pyrex will automatically handle the
644
652
# Py_INCREF that we need.
645
cur_dirname = <object>p_current_dirname[0]
646
# Use StaticTuple_New to pre-allocate, rather than creating a regular
647
# tuple and passing it to the StaticTuple constructor.
648
# path_name_file_id_key = StaticTuple(<object>p_current_dirname[0],
649
# self.get_next_str(),
650
# self.get_next_str(),
652
tmp = StaticTuple_New(3)
653
Py_INCREF(cur_dirname); StaticTuple_SET_ITEM(tmp, 0, cur_dirname)
654
cur_basename = self.get_next_str()
655
cur_file_id = self.get_next_str()
656
Py_INCREF(cur_basename); StaticTuple_SET_ITEM(tmp, 1, cur_basename)
657
Py_INCREF(cur_file_id); StaticTuple_SET_ITEM(tmp, 2, cur_file_id)
658
path_name_file_id_key = tmp
653
path_name_file_id_key = (<object>p_current_dirname[0],
660
658
# Parse all of the per-tree information. current has the information in
661
659
# the same location as parent trees. The only difference is that 'info'
677
675
entry_size_cstr = self.get_next(&cur_size)
678
676
entry_size = strtoul(entry_size_cstr, NULL, 10)
679
677
executable_cstr = self.get_next(&cur_size)
680
is_executable = (executable_cstr[0] == b'y')
678
is_executable = (executable_cstr[0] == c'y')
681
679
info = self.get_next_str()
682
# TODO: If we want to use StaticTuple_New here we need to be pretty
683
# careful. We are relying on a bit of Pyrex
684
# automatic-conversion from 'int' to PyInt, and that doesn't
685
# play well with the StaticTuple_SET_ITEM macro.
686
# Timing doesn't (yet) show a worthwile improvement in speed
687
# versus complexity and maintainability.
688
# tmp = StaticTuple_New(5)
689
# Py_INCREF(minikind); StaticTuple_SET_ITEM(tmp, 0, minikind)
690
# Py_INCREF(fingerprint); StaticTuple_SET_ITEM(tmp, 1, fingerprint)
691
# Py_INCREF(entry_size); StaticTuple_SET_ITEM(tmp, 2, entry_size)
692
# Py_INCREF(is_executable); StaticTuple_SET_ITEM(tmp, 3, is_executable)
693
# Py_INCREF(info); StaticTuple_SET_ITEM(tmp, 4, info)
694
# PyList_Append(trees, tmp)
695
PyList_Append(trees, StaticTuple(
680
PyList_Append(trees, (
696
681
minikind, # minikind
697
682
fingerprint, # fingerprint
698
683
entry_size, # size
706
691
# ensures that we always finish parsing a line on an end-of-entry
708
693
trailing = self.get_next(&cur_size)
709
if cur_size != 1 or not trailing.startswith(b'\n'):
710
raise DirstateCorrupt(self.state,
694
if cur_size != 1 or trailing[0] != c'\n':
695
raise errors.DirstateCorrupt(self.state,
711
696
'Bad parse, we expected to end on \\n, not: %d %s: %s'
712
697
% (cur_size, safe_string_from_size(trailing, cur_size),
732
717
current_block = []
733
dirblocks = [(b'', current_block), (b'', [])]
718
dirblocks = [('', current_block), ('', [])]
734
719
self.state._dirblocks = dirblocks
736
721
current_dirname = <void*>obj
754
739
PyList_Append(current_block, entry)
755
740
entry_count = entry_count + 1
756
741
if entry_count != expected_entry_count:
757
raise DirstateCorrupt(self.state,
742
raise errors.DirstateCorrupt(self.state,
758
743
'We read the wrong number of entries.'
759
744
' We expected to read %s, but read %s'
760
745
% (expected_entry_count, entry_count))
812
792
cdef char result[6*4] # 6 long ints
813
793
cdef int *aliased
814
794
aliased = <int *>result
815
aliased[0] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_size))
816
# mtime and ctime will often be floats but get converted to PyInt within
817
aliased[1] = htonl(_time_to_unsigned(stat_value.st_mtime))
818
aliased[2] = htonl(_time_to_unsigned(stat_value.st_ctime))
819
aliased[3] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_dev))
820
aliased[4] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_ino))
821
aliased[5] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_mode))
822
packed = PyBytes_FromStringAndSize(result, 6*4)
795
aliased[0] = htonl(stat_value.st_size)
796
aliased[1] = htonl(int(stat_value.st_mtime))
797
aliased[2] = htonl(int(stat_value.st_ctime))
798
aliased[3] = htonl(stat_value.st_dev)
799
aliased[4] = htonl(stat_value.st_ino & 0xFFFFFFFF)
800
aliased[5] = htonl(stat_value.st_mode)
801
packed = PyString_FromStringAndSize(result, 6*4)
823
802
return _encode(packed)[:-1]
826
def pack_stat(stat_value):
827
"""Convert stat value into a packed representation quickly with pyrex"""
828
return _pack_stat(stat_value)
831
cpdef update_entry(self, entry, abspath, stat_value):
805
def update_entry(self, entry, abspath, stat_value):
806
"""Update the entry based on what is actually on disk.
808
This function only calculates the sha if it needs to - if the entry is
809
uncachable, or clearly different to the first parent's entry, no sha
810
is calculated, and None is returned.
812
:param entry: This is the dirblock entry for the file in question.
813
:param abspath: The path on disk for this file.
814
:param stat_value: (optional) if we already have done a stat on the
816
:return: None, or The sha1 hexdigest of the file (40 bytes) or link
819
return _update_entry(self, entry, abspath, stat_value)
822
cdef _update_entry(self, entry, abspath, stat_value):
832
823
"""Update the entry based on what is actually on disk.
834
825
This function only calculates the sha if it needs to - if the entry is
846
837
# _st mode of the compiled stat objects.
847
838
cdef int minikind, saved_minikind
848
839
cdef void * details
849
cdef int worth_saving
850
840
minikind = minikind_from_mode(stat_value.st_mode)
851
841
if 0 == minikind:
853
843
packed_stat = _pack_stat(stat_value)
854
844
details = PyList_GetItem_void_void(PyTuple_GetItem_void_void(<void *>entry, 1), 0)
855
saved_minikind = PyBytes_AsString_obj(<PyObject *>PyTuple_GetItem_void_void(details, 0))[0]
856
if minikind == b'd' and saved_minikind == b't':
845
saved_minikind = PyString_AsString_obj(<PyObject *>PyTuple_GetItem_void_void(details, 0))[0]
846
if minikind == c'd' and saved_minikind == c't':
858
848
saved_link_or_sha1 = PyTuple_GetItem_void_object(details, 1)
859
849
saved_file_size = PyTuple_GetItem_void_object(details, 2)
860
850
saved_executable = PyTuple_GetItem_void_object(details, 3)
890
879
if (stat_value.st_mtime < self._cutoff_time
891
880
and stat_value.st_ctime < self._cutoff_time
892
881
and len(entry[1]) > 1
893
and entry[1][1][0] != b'a'):
882
and entry[1][1][0] != 'a'):
894
883
# Could check for size changes for further optimised
895
884
# avoidance of sha1's. However the most prominent case of
896
885
# over-shaing is during initial add, which this catches.
897
886
link_or_sha1 = self._sha1_file(abspath)
898
entry[1][0] = (b'f', link_or_sha1, stat_value.st_size,
887
entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
899
888
executable, packed_stat)
901
# This file is not worth caching the sha1. Either it is too new, or
902
# it is newly added. Regardless, the only things we are changing
903
# are derived from the stat, and so are not worth caching. So we do
904
# *not* set the IN_MEMORY_MODIFIED flag. (But we'll save the
905
# updated values if there is *other* data worth saving.)
906
entry[1][0] = (b'f', b'', stat_value.st_size, executable,
909
elif minikind == b'd':
910
entry[1][0] = (b'd', b'', 0, False, packed_stat)
911
if saved_minikind != b'd':
890
entry[1][0] = ('f', '', stat_value.st_size,
891
executable, DirState.NULLSTAT)
892
elif minikind == c'd':
894
entry[1][0] = ('d', '', 0, False, packed_stat)
895
if saved_minikind != c'd':
912
896
# This changed from something into a directory. Make sure we
913
897
# have a directory block for it. This doesn't happen very
914
898
# often, so this doesn't have to be super fast.
916
900
self._get_block_entry_index(entry[0][0], entry[0][1], 0)
917
901
self._ensure_block(block_index, entry_index,
918
902
pathjoin(entry[0][0], entry[0][1]))
920
# Any changes are derived trivially from the stat object, not worth
921
# re-writing a dirstate for just this
923
elif minikind == b'l':
924
if saved_minikind == b'l':
925
# If the object hasn't changed kind, it isn't worth saving the
926
# dirstate just for a symlink. The default is 'fast symlinks' which
927
# save the target in the inode entry, rather than separately. So to
928
# stat, we've already read everything off disk.
903
elif minikind == c'l':
930
904
link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
931
905
if self._cutoff_time is None:
932
906
self._sha_cutoff_time()
933
907
if (stat_value.st_mtime < self._cutoff_time
934
908
and stat_value.st_ctime < self._cutoff_time):
935
entry[1][0] = (b'l', link_or_sha1, stat_value.st_size,
909
entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
936
910
False, packed_stat)
938
entry[1][0] = (b'l', b'', stat_value.st_size,
912
entry[1][0] = ('l', '', stat_value.st_size,
939
913
False, DirState.NULLSTAT)
941
# Note, even though _mark_modified will only set
942
# IN_MEMORY_HASH_MODIFIED, it still isn't worth
943
self._mark_modified([entry])
914
self._dirblock_state = DirState.IN_MEMORY_MODIFIED
944
915
return link_or_sha1
947
918
# TODO: Do we want to worry about exceptions here?
948
919
cdef char _minikind_from_string(object string) except? -1:
949
920
"""Convert a python string to a char."""
950
return PyBytes_AsString(string)[0]
921
return PyString_AsString(string)[0]
953
924
cdef object _kind_absent
967
938
cdef object _minikind_to_kind(char minikind):
968
939
"""Create a string kind for minikind."""
969
940
cdef char _minikind[1]
971
942
return _kind_file
972
elif minikind == b'd':
943
elif minikind == c'd':
973
944
return _kind_directory
974
elif minikind == b'a':
945
elif minikind == c'a':
975
946
return _kind_absent
976
elif minikind == b'r':
947
elif minikind == c'r':
977
948
return _kind_relocated
978
elif minikind == b'l':
949
elif minikind == c'l':
979
950
return _kind_symlink
980
elif minikind == b't':
951
elif minikind == c't':
981
952
return _kind_tree_reference
982
953
_minikind[0] = minikind
983
raise KeyError(PyBytes_FromStringAndSize(_minikind, 1))
954
raise KeyError(PyString_FromStringAndSize(_minikind, 1))
986
957
cdef int _versioned_minikind(char minikind): # cannot_raise
987
958
"""Return non-zero if minikind is in fltd"""
988
return (minikind == b'f' or
959
return (minikind == c'f' or
994
965
cdef class ProcessEntryC:
1133
1104
if self.target_index != 0:
1134
1105
raise AssertionError("Unsupported target index %d" %
1135
1106
self.target_index)
1136
link_or_sha1 = update_entry(self.state, entry, path_info[4], path_info[3])
1107
link_or_sha1 = _update_entry(self.state, entry, path_info[4], path_info[3])
1137
1108
# The entry may have been modified by update_entry
1138
1109
target_details = details_list[self.target_index]
1139
1110
target_minikind = _minikind_from_string(target_details[0])
1142
1113
# the rest of this function is 0.3 seconds on 50K paths, or
1143
1114
# 0.000006 seconds per call.
1144
1115
source_minikind = _minikind_from_string(source_details[0])
1145
if ((_versioned_minikind(source_minikind) or source_minikind == b'r')
1116
if ((_versioned_minikind(source_minikind) or source_minikind == c'r')
1146
1117
and _versioned_minikind(target_minikind)):
1147
1118
# claimed content in both: diff
1148
1119
# r | fdlt | | add source to search, add id path move and perform
1149
1120
# | | | diff check on source-target
1150
1121
# r | fdlt | a | dangling file that was present in the basis.
1152
if source_minikind != b'r':
1123
if source_minikind != c'r':
1153
1124
old_dirname = entry[0][0]
1154
1125
old_basename = entry[0][1]
1155
1126
old_path = path = None
1157
1128
# add the source to the search path to find any children it
1158
1129
# has. TODO ? : only add if it is a container ?
1159
1130
if (not self.doing_consistency_expansion and
1160
not is_inside_any(self.searched_specific_files,
1131
not osutils.is_inside_any(self.searched_specific_files,
1161
1132
source_details[1])):
1162
1133
self.search_specific_files.add(source_details[1])
1163
1134
# expanding from a user requested path, parent expansion
1172
1143
# update the source details variable to be the real
1174
1145
if old_entry == (None, None):
1175
raise DirstateCorrupt(self.state._filename,
1146
raise errors.CorruptDirstate(self.state._filename,
1176
1147
"entry '%s/%s' is considered renamed from %r"
1177
1148
" but source does not exist\n"
1178
1149
"entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
1191
1162
old_path = path = self.pathjoin(old_dirname, old_basename)
1192
1163
file_id = entry[0][2]
1193
1164
self.new_dirname_to_file_id[path] = file_id
1194
if source_minikind != b'd':
1165
if source_minikind != c'd':
1195
1166
content_change = 1
1197
1168
# directories have no fingerprint
1198
1169
content_change = 0
1199
1170
target_exec = False
1200
1171
elif target_kind == 'file':
1201
if source_minikind != b'f':
1172
if source_minikind != c'f':
1202
1173
content_change = 1
1204
1175
# Check the sha. We can't just rely on the size as
1221
1192
target_exec = target_details[3]
1222
1193
elif target_kind == 'symlink':
1223
if source_minikind != b'l':
1194
if source_minikind != c'l':
1224
1195
content_change = 1
1226
1197
content_change = (link_or_sha1 != source_details[1])
1227
1198
target_exec = False
1228
1199
elif target_kind == 'tree-reference':
1229
if source_minikind != b't':
1200
if source_minikind != c't':
1230
1201
content_change = 1
1232
1203
content_change = 0
1235
1206
if path is None:
1236
1207
path = self.pathjoin(old_dirname, old_basename)
1237
1208
raise errors.BadFileKindError(path, path_info[2])
1238
if source_minikind == b'd':
1209
if source_minikind == c'd':
1239
1210
if path is None:
1240
1211
old_path = path = self.pathjoin(old_dirname, old_basename)
1241
1212
if file_id is None:
1311
1282
(self.utf8_decode(old_basename)[0], self.utf8_decode(entry[0][1])[0]),
1312
1283
(source_kind, target_kind),
1313
1284
(source_exec, target_exec)), changed
1314
elif source_minikind == b'a' and _versioned_minikind(target_minikind):
1285
elif source_minikind == c'a' and _versioned_minikind(target_minikind):
1315
1286
# looks like a new file
1316
1287
path = self.pathjoin(entry[0][0], entry[0][1])
1317
1288
# parent id is the entry for the path in the target tree
1319
1290
parent_entry = self.state._get_entry(self.target_index,
1320
1291
path_utf8=entry[0][0])
1321
1292
if parent_entry is None:
1322
raise DirstateCorrupt(self.state,
1293
raise errors.DirstateCorrupt(self.state,
1323
1294
"We could not find the parent entry in index %d"
1324
1295
" for the entry: %s"
1325
1296
% (self.target_index, entry[0]))
1346
1317
(None, target_exec)), True
1348
1319
# Its a missing file, report it as such.
1349
return TreeChange(entry[0][2],
1320
return (entry[0][2],
1350
1321
(None, self.utf8_decode(path)[0]),
1354
1325
(None, self.utf8_decode(entry[0][1])[0]),
1356
1327
(None, False)), True
1357
elif _versioned_minikind(source_minikind) and target_minikind == b'a':
1328
elif _versioned_minikind(source_minikind) and target_minikind == c'a':
1358
1329
# unversioned, possibly, or possibly not deleted: we dont care.
1359
1330
# if its still on disk, *and* theres no other entry at this
1360
1331
# path [we dont know this in this routine at the moment -
1373
1343
(self.utf8_decode(entry[0][1])[0], None),
1374
1344
(_minikind_to_kind(source_minikind), None),
1375
1345
(source_details[3], None)), True
1376
elif _versioned_minikind(source_minikind) and target_minikind == b'r':
1346
elif _versioned_minikind(source_minikind) and target_minikind == c'r':
1377
1347
# a rename; could be a true rename, or a rename inherited from
1378
1348
# a renamed parent. TODO: handle this efficiently. Its not
1379
1349
# common case to rename dirs though, so a correct but slow
1380
1350
# implementation will do.
1381
1351
if (not self.doing_consistency_expansion and
1382
not is_inside_any(self.searched_specific_files,
1352
not osutils.is_inside_any(self.searched_specific_files,
1383
1353
target_details[1])):
1384
1354
self.search_specific_files.add(target_details[1])
1385
1355
# We don't expand the specific files parents list here as
1386
1356
# the path is absent in target and won't create a delta with
1387
1357
# missing parent.
1388
elif ((source_minikind == b'r' or source_minikind == b'a') and
1389
(target_minikind == b'r' or target_minikind == b'a')):
1358
elif ((source_minikind == c'r' or source_minikind == c'a') and
1359
(target_minikind == c'r' or target_minikind == c'a')):
1390
1360
# neither of the selected trees contain this path,
1391
1361
# so skip over it. This is not currently directly tested, but
1392
1362
# is indirectly via test_too_much.TestCommands.test_conflicts.
1412
1382
:param result: A result tuple.
1414
if not self.partial or not result.file_id:
1384
if not self.partial or not result[0]:
1416
self.seen_ids.add(result.file_id)
1417
new_path = result.path[1]
1386
self.seen_ids.add(result[0])
1387
new_path = result[1][1]
1419
1389
# Not the root and not a delete: queue up the parents of the path.
1420
1390
self.search_specific_file_parents.update(
1421
1391
osutils.parent_directories(new_path.encode('utf8')))
1422
1392
# Add the root directory which parent_directories does not
1424
self.search_specific_file_parents.add(b'')
1394
self.search_specific_file_parents.add('')
1427
1397
cdef int _update_current_block(self) except -1:
1428
1398
if (self.block_index < len(self.state._dirblocks) and
1429
is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1399
osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1430
1400
self.current_block = self.state._dirblocks[self.block_index]
1431
1401
self.current_block_list = self.current_block[1]
1432
1402
self.current_block_pos = 0
1488
1458
# TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
1489
1459
# keeping a cache of directories that we have seen.
1490
1460
cdef object current_dirname, current_blockname
1491
cdef char * current_dirname_c
1492
cdef char * current_blockname_c
1461
cdef char * current_dirname_c, * current_blockname_c
1493
1462
cdef int advance_entry, advance_path
1494
1463
cdef int path_handled
1495
1464
searched_specific_files = self.searched_specific_files
1528
1497
# some other random error: hand it up.
1531
self.root_dir_info = (b'', self.current_root,
1500
self.root_dir_info = ('', self.current_root,
1532
1501
osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
1533
1502
self.root_abspath)
1534
1503
if self.root_dir_info[2] == 'directory':
1613
if self.current_dir_info[0][0] == b'':
1581
if self.current_dir_info[0][0] == '':
1614
1582
# remove .bzr from iteration
1615
bzr_index = self.bisect_left(self.current_dir_list, (b'.bzr',))
1616
if self.current_dir_list[bzr_index][0] != b'.bzr':
1583
bzr_index = self.bisect_left(self.current_dir_list, ('.bzr',))
1584
if self.current_dir_list[bzr_index][0] != '.bzr':
1617
1585
raise AssertionError()
1618
1586
del self.current_dir_list[bzr_index]
1619
initial_key = (self.current_root, b'', b'')
1587
initial_key = (self.current_root, '', '')
1620
1588
self.block_index, _ = self.state._find_block_index_from_key(initial_key)
1621
1589
if self.block_index == 0:
1622
1590
# we have processed the total root already, but because the
1633
1601
# Work around pyrex broken heuristic - current_dirname has
1634
1602
# the same scope as current_dirname_c
1635
1603
current_dirname = self.current_dir_info[0][0]
1636
current_dirname_c = PyBytes_AS_STRING_void(
1604
current_dirname_c = PyString_AS_STRING_void(
1637
1605
<void *>current_dirname)
1638
1606
current_blockname = self.current_block[0]
1639
current_blockname_c = PyBytes_AS_STRING_void(
1607
current_blockname_c = PyString_AS_STRING_void(
1640
1608
<void *>current_blockname)
1641
1609
# In the python generator we evaluate this if block once per
1642
1610
# dir+block; because we reenter in the pyrex version its being
1643
1611
# evaluated once per path: we could cache the result before
1644
1612
# doing the while loop and probably save time.
1645
1613
if _cmp_by_dirs(current_dirname_c,
1646
PyBytes_Size(current_dirname),
1614
PyString_Size(current_dirname),
1647
1615
current_blockname_c,
1648
PyBytes_Size(current_blockname)) < 0:
1616
PyString_Size(current_blockname)) < 0:
1649
1617
# filesystem data refers to paths not covered by the
1650
1618
# dirblock. this has two possibilities:
1651
1619
# A) it is versioned but empty, so there is no block for it
1687
1654
self.path_index = 0
1688
1655
self.current_dir_list = None
1690
self.current_dir_info = next(self.dir_iterator)
1657
self.current_dir_info = self.dir_iterator.next()
1691
1658
self.current_dir_list = self.current_dir_info[1]
1692
except StopIteration, _:
1659
except StopIteration:
1693
1660
self.current_dir_info = None
1694
1661
else: #(dircmp > 0)
1695
1662
# We have a dirblock entry for this location, but there
1750
1717
cdef int cmp_result
1751
1718
# cdef char * temp_str
1752
1719
# cdef Py_ssize_t temp_str_length
1753
# PyBytes_AsStringAndSize(disk_kind, &temp_str, &temp_str_length)
1720
# PyString_AsStringAndSize(disk_kind, &temp_str, &temp_str_length)
1754
1721
# if not strncmp(temp_str, "directory", temp_str_length):
1755
1722
if (self.current_block is not None and
1756
1723
self.current_block_pos < PyList_GET_SIZE(self.current_block_list)):
1791
1757
minikind = _minikind_from_string(
1792
1758
current_entry[1][self.target_index][0])
1793
cmp_result = ((current_path_info[1] > current_entry[0][1]) -
1794
(current_path_info[1] < current_entry[0][1]))
1795
if (cmp_result or minikind == b'a' or minikind == b'r'):
1759
cmp_result = cmp(current_path_info[1], current_entry[0][1])
1760
if (cmp_result or minikind == c'a' or minikind == c'r'):
1796
1761
# The current path on disk doesn't match the dirblock
1797
1762
# record. Either the dirblock record is marked as
1798
1763
# absent/renamed, or the file on disk is not present at all
1838
1803
and stat.S_IEXEC & current_path_info[3].st_mode)
1840
1805
relpath_unicode = self.utf8_decode(current_path_info[0])[0]
1841
except UnicodeDecodeError, _:
1806
except UnicodeDecodeError:
1842
1807
raise errors.BadFilenameEncoding(
1843
1808
current_path_info[0], osutils._fs_enc)
1844
1809
if changed is not None:
1845
1810
raise AssertionError(
1846
1811
"result is not None: %r" % result)
1847
result = TreeChange(
1849
1813
(None, relpath_unicode),
1851
1815
(False, False),
1885
1849
self.path_index = 0
1886
1850
self.current_dir_list = None
1888
self.current_dir_info = next(self.dir_iterator)
1852
self.current_dir_info = self.dir_iterator.next()
1889
1853
self.current_dir_list = self.current_dir_info[1]
1890
except StopIteration, _:
1854
except StopIteration:
1891
1855
self.current_dir_info = None
1893
1857
cdef object _next_consistent_entries(self):
1894
1858
"""Grabs the next specific file parent case to consider.
1896
1860
:return: A list of the results, each of which is as for _process_entry.
1904
1868
if path_utf8 in self.searched_exact_paths:
1905
1869
# We've examined this path.
1907
if is_inside_any(self.searched_specific_files, path_utf8):
1871
if osutils.is_inside_any(self.searched_specific_files, path_utf8):
1908
1872
# We've examined this path.
1910
1874
path_entries = self.state._entries_for_path(path_utf8)
1916
1880
found_item = False
1917
1881
for candidate_entry in path_entries:
1918
1882
# Find entries present in target at this path:
1919
if candidate_entry[1][self.target_index][0] not in (b'a', b'r'):
1883
if candidate_entry[1][self.target_index][0] not in 'ar':
1920
1884
found_item = True
1921
1885
selected_entries.append(candidate_entry)
1922
1886
# Find entries present in source at this path:
1923
1887
elif (self.source_index is not None and
1924
candidate_entry[1][self.source_index][0] not in (b'a', b'r')):
1888
candidate_entry[1][self.source_index][0] not in 'ar'):
1925
1889
found_item = True
1926
if candidate_entry[1][self.target_index][0] == b'a':
1890
if candidate_entry[1][self.target_index][0] == 'a':
1927
1891
# Deleted, emit it here.
1928
1892
selected_entries.append(candidate_entry)
1951
1915
self._gather_result_for_consistency(result)
1952
if (result.kind[0] == 'directory' and
1953
result.kind[1] != 'directory'):
1916
if (result[6][0] == 'directory' and
1917
result[6][1] != 'directory'):
1954
1918
# This stopped being a directory, the old children have
1955
1919
# to be included.
1956
if entry[1][self.source_index][0] == b'r':
1920
if entry[1][self.source_index][0] == 'r':
1957
1921
# renamed, take the source path
1958
1922
entry_path_utf8 = entry[1][self.source_index][1]
1960
1924
entry_path_utf8 = path_utf8
1961
initial_key = (entry_path_utf8, b'', b'')
1925
initial_key = (entry_path_utf8, '', '')
1962
1926
block_index, _ = self.state._find_block_index_from_key(
1964
1928
if block_index == 0:
1967
1931
current_block = None
1968
1932
if block_index < len(self.state._dirblocks):
1969
1933
current_block = self.state._dirblocks[block_index]
1934
if not osutils.is_inside(
1971
1935
entry_path_utf8, current_block[0]):
1972
1936
# No entries for this directory at all.
1973
1937
current_block = None
1974
1938
if current_block is not None:
1975
1939
for entry in current_block[1]:
1976
if entry[1][self.source_index][0] in (b'a', b'r'):
1940
if entry[1][self.source_index][0] in 'ar':
1977
1941
# Not in the source tree, so doesn't have to be
2002
utf8_basename = utf8_path.rsplit(b'/', 1)[-1]
1966
utf8_basename = utf8_path.rsplit('/', 1)[-1]
2003
1967
dir_info = (utf8_path, utf8_basename,
2004
1968
osutils.file_kind_from_stat_mode(stat.st_mode), stat,