/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/_dirstate_helpers_pyx.pyx

  • Committer: Martin
  • Date: 2017-06-11 14:36:07 UTC
  • mto: This revision was merged to the branch mainline in revision 6688.
  • Revision ID: gzlist@googlemail.com-20170611143607-iuusrtbtuvgzrcho
Make test__rio pass on Python 3

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2008, 2010 Canonical Ltd
 
1
# Copyright (C) 2007-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
This is the python implementation for DirState functions.
20
20
"""
21
21
 
 
22
from __future__ import absolute_import
 
23
 
22
24
import binascii
23
25
import bisect
24
26
import errno
26
28
import stat
27
29
import sys
28
30
 
29
 
from bzrlib import cache_utf8, errors, osutils
30
 
from bzrlib.dirstate import DirState
31
 
from bzrlib.osutils import parent_directories, pathjoin, splitpath
 
31
from . import cache_utf8, errors, osutils
 
32
from .dirstate import DirState
 
33
from .osutils import parent_directories, pathjoin, splitpath
32
34
 
33
35
 
34
36
# This is the Windows equivalent of ENOTDIR
40
42
cdef int ERROR_DIRECTORY
41
43
ERROR_DIRECTORY = 267
42
44
 
43
 
#python2.4 support, and other platform-dependent includes
44
45
cdef extern from "python-compat.h":
45
46
    unsigned long htonl(unsigned long)
46
47
 
97
98
    object PyTuple_GetItem_void_object "PyTuple_GET_ITEM" (void* tpl, int index)
98
99
    object PyTuple_GET_ITEM(object tpl, Py_ssize_t index)
99
100
 
 
101
    unsigned long PyInt_AsUnsignedLongMask(object number) except? -1
100
102
 
101
103
    char *PyString_AsString(object p)
102
104
    char *PyString_AsString_obj "PyString_AsString" (PyObject *string)
118
120
    # ??? memrchr is a GNU extension :(
119
121
    # void *memrchr(void *s, int c, size_t len)
120
122
 
 
123
# cimport all of the definitions we will need to access
 
124
from ._static_tuple_c cimport import_static_tuple_c, StaticTuple, \
 
125
    StaticTuple_New, StaticTuple_SET_ITEM
 
126
 
 
127
import_static_tuple_c()
121
128
 
122
129
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
123
130
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
237
244
    return 0
238
245
 
239
246
 
240
 
def cmp_by_dirs(path1, path2):
 
247
def lt_by_dirs(path1, path2):
241
248
    """Compare two paths directory by directory.
242
249
 
243
250
    This is equivalent to doing::
244
251
 
245
 
       cmp(path1.split('/'), path2.split('/'))
 
252
       operator.lt(path1.split('/'), path2.split('/'))
246
253
 
247
254
    The idea is that you should compare path components separately. This
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.
 
255
    differs from plain ``path1 < path2`` for paths like ``'a-b'`` and ``a/b``.
 
256
    "a-b" comes after "a" but would come before "a/b" lexically.
250
257
 
251
258
    :param path1: first path
252
259
    :param path2: second path
253
 
    :return: negative number if ``path1`` comes first,
254
 
        0 if paths are equal,
255
 
        and positive number if ``path2`` sorts first
 
260
    :return: True if path1 comes first, otherwise False
256
261
    """
257
262
    if not PyString_CheckExact(path1):
258
263
        raise TypeError("'path1' must be a plain string, not %s: %r"
260
265
    if not PyString_CheckExact(path2):
261
266
        raise TypeError("'path2' must be a plain string, not %s: %r"
262
267
                        % (type(path2), path2))
263
 
    return _cmp_by_dirs(PyString_AsString(path1),
264
 
                        PyString_Size(path1),
265
 
                        PyString_AsString(path2),
266
 
                        PyString_Size(path2))
267
 
 
268
 
 
269
 
def _cmp_path_by_dirblock(path1, path2):
 
268
    return -1 == _cmp_by_dirs(PyString_AsString(path1),
 
269
                              PyString_Size(path1),
 
270
                              PyString_AsString(path2),
 
271
                              PyString_Size(path2))
 
272
 
 
273
 
 
274
def _lt_path_by_dirblock(path1, path2):
270
275
    """Compare two paths based on what directory they are in.
271
276
 
272
277
    This generates a sort order, such that all children of a directory are
278
283
 
279
284
    :param path1: first path
280
285
    :param path2: the second path
281
 
    :return: negative number if ``path1`` comes first,
282
 
        0 if paths are equal
283
 
        and a positive number if ``path2`` sorts first
 
286
    :return: True if path1 comes first, otherwise False.
284
287
    """
285
288
    if not PyString_CheckExact(path1):
286
289
        raise TypeError("'path1' must be a plain string, not %s: %r"
288
291
    if not PyString_CheckExact(path2):
289
292
        raise TypeError("'path2' must be a plain string, not %s: %r"
290
293
                        % (type(path2), path2))
291
 
    return _cmp_path_by_dirblock_intern(PyString_AsString(path1),
292
 
                                        PyString_Size(path1),
293
 
                                        PyString_AsString(path2),
294
 
                                        PyString_Size(path2))
 
294
    # GZ 2017-06-09: This internal function really only needs lt as well.
 
295
    return (_cmp_path_by_dirblock_intern(PyString_AsString(path1),
 
296
                                         PyString_Size(path1),
 
297
                                         PyString_AsString(path2),
 
298
                                         PyString_Size(path2)) < 0)
295
299
 
296
300
 
297
301
cdef int _cmp_path_by_dirblock_intern(char *path1, int path1_len,
610
614
        :param new_block: This is to let the caller know that it needs to
611
615
            create a new directory block to store the next entry.
612
616
        """
613
 
        cdef object path_name_file_id_key
 
617
        cdef StaticTuple path_name_file_id_key
 
618
        cdef StaticTuple tmp
614
619
        cdef char *entry_size_cstr
615
620
        cdef unsigned long int entry_size
616
621
        cdef char* executable_cstr
650
655
        # Build up the key that will be used.
651
656
        # By using <object>(void *) Pyrex will automatically handle the
652
657
        # Py_INCREF that we need.
653
 
        path_name_file_id_key = (<object>p_current_dirname[0],
654
 
                                 self.get_next_str(),
655
 
                                 self.get_next_str(),
656
 
                                )
 
658
        cur_dirname = <object>p_current_dirname[0]
 
659
        # Use StaticTuple_New to pre-allocate, rather than creating a regular
 
660
        # tuple and passing it to the StaticTuple constructor.
 
661
        # path_name_file_id_key = StaticTuple(<object>p_current_dirname[0],
 
662
        #                          self.get_next_str(),
 
663
        #                          self.get_next_str(),
 
664
        #                         )
 
665
        tmp = StaticTuple_New(3)
 
666
        Py_INCREF(cur_dirname); StaticTuple_SET_ITEM(tmp, 0, cur_dirname)
 
667
        cur_basename = self.get_next_str()
 
668
        cur_file_id = self.get_next_str()
 
669
        Py_INCREF(cur_basename); StaticTuple_SET_ITEM(tmp, 1, cur_basename)
 
670
        Py_INCREF(cur_file_id); StaticTuple_SET_ITEM(tmp, 2, cur_file_id)
 
671
        path_name_file_id_key = tmp
657
672
 
658
673
        # Parse all of the per-tree information. current has the information in
659
674
        # the same location as parent trees. The only difference is that 'info'
677
692
            executable_cstr = self.get_next(&cur_size)
678
693
            is_executable = (executable_cstr[0] == c'y')
679
694
            info = self.get_next_str()
680
 
            PyList_Append(trees, (
 
695
            # TODO: If we want to use StaticTuple_New here we need to be pretty
 
696
            #       careful. We are relying on a bit of Pyrex
 
697
            #       automatic-conversion from 'int' to PyInt, and that doesn't
 
698
            #       play well with the StaticTuple_SET_ITEM macro.
 
699
            #       Timing doesn't (yet) show a worthwile improvement in speed
 
700
            #       versus complexity and maintainability.
 
701
            # tmp = StaticTuple_New(5)
 
702
            # Py_INCREF(minikind); StaticTuple_SET_ITEM(tmp, 0, minikind)
 
703
            # Py_INCREF(fingerprint); StaticTuple_SET_ITEM(tmp, 1, fingerprint)
 
704
            # Py_INCREF(entry_size); StaticTuple_SET_ITEM(tmp, 2, entry_size)
 
705
            # Py_INCREF(is_executable); StaticTuple_SET_ITEM(tmp, 3, is_executable)
 
706
            # Py_INCREF(info); StaticTuple_SET_ITEM(tmp, 4, info)
 
707
            # PyList_Append(trees, tmp)
 
708
            PyList_Append(trees, StaticTuple(
681
709
                minikind,     # minikind
682
710
                fingerprint,  # fingerprint
683
711
                entry_size,   # size
782
810
_encode = binascii.b2a_base64
783
811
 
784
812
 
785
 
from struct import pack
786
813
cdef _pack_stat(stat_value):
787
814
    """return a string representing the stat value's key fields.
788
815
 
792
819
    cdef char result[6*4] # 6 long ints
793
820
    cdef int *aliased
794
821
    aliased = <int *>result
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)
 
822
    aliased[0] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_size))
 
823
    # mtime and ctime will often be floats but get converted to PyInt within
 
824
    aliased[1] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_mtime))
 
825
    aliased[2] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_ctime))
 
826
    aliased[3] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_dev))
 
827
    aliased[4] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_ino))
 
828
    aliased[5] = htonl(PyInt_AsUnsignedLongMask(stat_value.st_mode))
801
829
    packed = PyString_FromStringAndSize(result, 6*4)
802
830
    return _encode(packed)[:-1]
803
831
 
804
832
 
 
833
def pack_stat(stat_value):
 
834
    """Convert stat value into a packed representation quickly with pyrex"""
 
835
    return _pack_stat(stat_value)
 
836
 
 
837
 
805
838
def update_entry(self, entry, abspath, stat_value):
806
839
    """Update the entry based on what is actually on disk.
807
840
 
837
870
    # _st mode of the compiled stat objects.
838
871
    cdef int minikind, saved_minikind
839
872
    cdef void * details
 
873
    cdef int worth_saving
840
874
    minikind = minikind_from_mode(stat_value.st_mode)
841
875
    if 0 == minikind:
842
876
        return None
871
905
    # If we have gotten this far, that means that we need to actually
872
906
    # process this entry.
873
907
    link_or_sha1 = None
 
908
    worth_saving = 1
874
909
    if minikind == c'f':
875
910
        executable = self._is_executable(stat_value.st_mode,
876
911
                                         saved_executable)
887
922
            entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
888
923
                           executable, packed_stat)
889
924
        else:
890
 
            entry[1][0] = ('f', '', stat_value.st_size,
891
 
                           executable, DirState.NULLSTAT)
 
925
            # This file is not worth caching the sha1. Either it is too new, or
 
926
            # it is newly added. Regardless, the only things we are changing
 
927
            # are derived from the stat, and so are not worth caching. So we do
 
928
            # *not* set the IN_MEMORY_MODIFIED flag. (But we'll save the
 
929
            # updated values if there is *other* data worth saving.)
 
930
            entry[1][0] = ('f', '', stat_value.st_size, executable,
 
931
                           DirState.NULLSTAT)
 
932
            worth_saving = 0
892
933
    elif minikind == c'd':
893
 
        link_or_sha1 = None
894
934
        entry[1][0] = ('d', '', 0, False, packed_stat)
895
935
        if saved_minikind != c'd':
896
936
            # This changed from something into a directory. Make sure we
900
940
                self._get_block_entry_index(entry[0][0], entry[0][1], 0)
901
941
            self._ensure_block(block_index, entry_index,
902
942
                               pathjoin(entry[0][0], entry[0][1]))
 
943
        else:
 
944
            # Any changes are derived trivially from the stat object, not worth
 
945
            # re-writing a dirstate for just this
 
946
            worth_saving = 0
903
947
    elif minikind == c'l':
 
948
        if saved_minikind == c'l':
 
949
            # If the object hasn't changed kind, it isn't worth saving the
 
950
            # dirstate just for a symlink. The default is 'fast symlinks' which
 
951
            # save the target in the inode entry, rather than separately. So to
 
952
            # stat, we've already read everything off disk.
 
953
            worth_saving = 0
904
954
        link_or_sha1 = self._read_link(abspath, saved_link_or_sha1)
905
955
        if self._cutoff_time is None:
906
956
            self._sha_cutoff_time()
911
961
        else:
912
962
            entry[1][0] = ('l', '', stat_value.st_size,
913
963
                           False, DirState.NULLSTAT)
914
 
    self._dirblock_state = DirState.IN_MEMORY_MODIFIED
 
964
    if worth_saving:
 
965
        # Note, even though _mark_modified will only set
 
966
        # IN_MEMORY_HASH_MODIFIED, it still isn't worth 
 
967
        self._mark_modified([entry])
915
968
    return link_or_sha1
916
969
 
917
970
 
1219
1272
            else:
1220
1273
                try:
1221
1274
                    source_parent_id = self.old_dirname_to_file_id[old_dirname]
1222
 
                except KeyError:
 
1275
                except KeyError, _:
1223
1276
                    source_parent_entry = self.state._get_entry(self.source_index,
1224
1277
                                                           path_utf8=old_dirname)
1225
1278
                    source_parent_id = source_parent_entry[0][2]
1236
1289
            else:
1237
1290
                try:
1238
1291
                    target_parent_id = self.new_dirname_to_file_id[new_dirname]
1239
 
                except KeyError:
 
1292
                except KeyError, _:
1240
1293
                    # TODO: We don't always need to do the lookup, because the
1241
1294
                    #       parent entry will be the same as the source entry.
1242
1295
                    target_parent_entry = self.state._get_entry(self.target_index,
1478
1531
            # interface doesn't require it.
1479
1532
            try:
1480
1533
                self.current_root = self.search_specific_files.pop()
1481
 
            except KeyError:
 
1534
            except KeyError, _:
1482
1535
                raise StopIteration()
1483
1536
            self.searched_specific_files.add(self.current_root)
1484
1537
            # process the entries for this containing directory: the rest will be
1567
1620
                        #            and e.winerror == ERROR_DIRECTORY
1568
1621
                        try:
1569
1622
                            e_winerror = e.winerror
1570
 
                        except AttributeError:
 
1623
                        except AttributeError, _:
1571
1624
                            e_winerror = None
1572
1625
                        win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
1573
1626
                        if (e.errno in win_errors or e_winerror in win_errors):
1656
1709
                    try:
1657
1710
                        self.current_dir_info = self.dir_iterator.next()
1658
1711
                        self.current_dir_list = self.current_dir_info[1]
1659
 
                    except StopIteration:
 
1712
                    except StopIteration, _:
1660
1713
                        self.current_dir_info = None
1661
1714
                else: #(dircmp > 0)
1662
1715
                    # We have a dirblock entry for this location, but there
1744
1797
                advance_entry = -1
1745
1798
                advance_path = -1
1746
1799
                result = None
 
1800
                changed = None
1747
1801
                path_handled = 0
1748
1802
                if current_entry is None:
1749
1803
                    # unversioned -  the check for path_handled when the path
1803
1857
                                and stat.S_IEXEC & current_path_info[3].st_mode)
1804
1858
                            try:
1805
1859
                                relpath_unicode = self.utf8_decode(current_path_info[0])[0]
1806
 
                            except UnicodeDecodeError:
 
1860
                            except UnicodeDecodeError, _:
1807
1861
                                raise errors.BadFilenameEncoding(
1808
1862
                                    current_path_info[0], osutils._fs_enc)
1809
1863
                            if changed is not None:
1851
1905
                try:
1852
1906
                    self.current_dir_info = self.dir_iterator.next()
1853
1907
                    self.current_dir_list = self.current_dir_info[1]
1854
 
                except StopIteration:
 
1908
                except StopIteration, _:
1855
1909
                    self.current_dir_info = None
1856
1910
 
1857
1911
    cdef object _next_consistent_entries(self):