/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 bzrlib/_dirstate_helpers_pyx.pyx

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-08-05 18:56:37 UTC
  • mfrom: (4580.5.16 1.18-win32-buildbot)
  • Revision ID: pqm@pqm.ubuntu.com-20090805185637-3f0y10upzcdw7e0g
Updates to buildout.cfg etc to have 'make installer-all' start being
        the preferred way to build win32 installer.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007, 2008, 2010 Canonical Ltd
 
1
# Copyright (C) 2007, 2008 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
28
28
 
29
29
from bzrlib import cache_utf8, errors, osutils
30
30
from bzrlib.dirstate import DirState
31
 
from bzrlib.osutils import parent_directories, pathjoin, splitpath
 
31
from bzrlib.osutils import pathjoin, splitpath
32
32
 
33
33
 
34
34
# This is the Windows equivalent of ENOTDIR
119
119
    # void *memrchr(void *s, int c, size_t len)
120
120
 
121
121
 
122
 
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
 
122
cdef void* _my_memrchr(void *s, int c, size_t n):
123
123
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
124
124
    cdef char *pos
125
125
    cdef char *start
156
156
        return None
157
157
    return <char*>found - <char*>_s
158
158
 
159
 
 
160
159
cdef object safe_string_from_size(char *s, Py_ssize_t size):
161
160
    if size < 0:
 
161
        # XXX: On 64-bit machines the <int> cast causes a C compiler warning.
162
162
        raise AssertionError(
163
 
            'tried to create a string with an invalid size: %d'
164
 
            % (size))
 
163
            'tried to create a string with an invalid size: %d @0x%x'
 
164
            % (size, <int>s))
165
165
    return PyString_FromStringAndSize(s, size)
166
166
 
167
167
 
168
 
cdef int _is_aligned(void *ptr): # cannot_raise
 
168
cdef int _is_aligned(void *ptr):
169
169
    """Is this pointer aligned to an integer size offset?
170
170
 
171
171
    :return: 1 if this pointer is aligned, 0 otherwise.
173
173
    return ((<intptr_t>ptr) & ((sizeof(int))-1)) == 0
174
174
 
175
175
 
176
 
cdef int _cmp_by_dirs(char *path1, int size1, char *path2, int size2): # cannot_raise
 
176
cdef int _cmp_by_dirs(char *path1, int size1, char *path2, int size2):
177
177
    cdef unsigned char *cur1
178
178
    cdef unsigned char *cur2
179
179
    cdef unsigned char *end1
295
295
 
296
296
 
297
297
cdef int _cmp_path_by_dirblock_intern(char *path1, int path1_len,
298
 
                                      char *path2, int path2_len): # cannot_raise
 
298
                                      char *path2, int path2_len):
299
299
    """Compare two paths by what directory they are in.
300
300
 
301
301
    see ``_cmp_path_by_dirblock`` for details.
768
768
    state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
769
769
 
770
770
 
771
 
cdef int minikind_from_mode(int mode): # cannot_raise
 
771
cdef int minikind_from_mode(int mode):
772
772
    # in order of frequency:
773
773
    if S_ISREG(mode):
774
774
        return c"f"
915
915
    return link_or_sha1
916
916
 
917
917
 
918
 
# TODO: Do we want to worry about exceptions here?
919
 
cdef char _minikind_from_string(object string) except? -1:
 
918
cdef char _minikind_from_string(object string):
920
919
    """Convert a python string to a char."""
921
920
    return PyString_AsString(string)[0]
922
921
 
954
953
    raise KeyError(PyString_FromStringAndSize(_minikind, 1))
955
954
 
956
955
 
957
 
cdef int _versioned_minikind(char minikind): # cannot_raise
 
956
cdef int _versioned_minikind(char minikind):
958
957
    """Return non-zero if minikind is in fltd"""
959
958
    return (minikind == c'f' or
960
959
            minikind == c'd' or
964
963
 
965
964
cdef class ProcessEntryC:
966
965
 
967
 
    cdef int doing_consistency_expansion
968
966
    cdef object old_dirname_to_file_id # dict
969
967
    cdef object new_dirname_to_file_id # dict
970
968
    cdef object last_source_parent
971
969
    cdef object last_target_parent
972
 
    cdef int include_unchanged
973
 
    cdef int partial
 
970
    cdef object include_unchanged
974
971
    cdef object use_filesystem_for_exec
975
972
    cdef object utf8_decode
976
973
    cdef readonly object searched_specific_files
977
 
    cdef readonly object searched_exact_paths
978
974
    cdef object search_specific_files
979
 
    # The parents up to the root of the paths we are searching.
980
 
    # After all normal paths are returned, these specific items are returned.
981
 
    cdef object search_specific_file_parents
982
975
    cdef object state
983
976
    # Current iteration variables:
984
977
    cdef object current_root
996
989
    cdef object current_block_list
997
990
    cdef object current_dir_info
998
991
    cdef object current_dir_list
999
 
    cdef object _pending_consistent_entries # list
1000
992
    cdef int path_index
1001
993
    cdef object root_dir_info
1002
994
    cdef object bisect_left
1003
995
    cdef object pathjoin
1004
996
    cdef object fstat
1005
 
    # A set of the ids we've output when doing partial output.
1006
 
    cdef object seen_ids
1007
997
    cdef object sha_file
1008
998
 
1009
999
    def __init__(self, include_unchanged, use_filesystem_for_exec,
1010
1000
        search_specific_files, state, source_index, target_index,
1011
1001
        want_unversioned, tree):
1012
 
        self.doing_consistency_expansion = 0
1013
1002
        self.old_dirname_to_file_id = {}
1014
1003
        self.new_dirname_to_file_id = {}
1015
 
        # Are we doing a partial iter_changes?
1016
 
        self.partial = set(['']).__ne__(search_specific_files)
1017
1004
        # Using a list so that we can access the values and change them in
1018
1005
        # nested scope. Each one is [path, file_id, entry]
1019
1006
        self.last_source_parent = [None, None]
1020
1007
        self.last_target_parent = [None, None]
1021
 
        if include_unchanged is None:
1022
 
            self.include_unchanged = False
1023
 
        else:
1024
 
            self.include_unchanged = int(include_unchanged)
 
1008
        self.include_unchanged = include_unchanged
1025
1009
        self.use_filesystem_for_exec = use_filesystem_for_exec
1026
1010
        self.utf8_decode = cache_utf8._utf8_decode
1027
1011
        # for all search_indexs in each path at or under each element of
1028
 
        # search_specific_files, if the detail is relocated: add the id, and
1029
 
        # add the relocated path as one to search if its not searched already.
1030
 
        # If the detail is not relocated, add the id.
 
1012
        # search_specific_files, if the detail is relocated: add the id, and add the
 
1013
        # relocated path as one to search if its not searched already. If the
 
1014
        # detail is not relocated, add the id.
1031
1015
        self.searched_specific_files = set()
1032
 
        # When we search exact paths without expanding downwards, we record
1033
 
        # that here.
1034
 
        self.searched_exact_paths = set()
1035
1016
        self.search_specific_files = search_specific_files
1036
 
        # The parents up to the root of the paths we are searching.
1037
 
        # After all normal paths are returned, these specific items are returned.
1038
 
        self.search_specific_file_parents = set()
1039
 
        # The ids we've sent out in the delta.
1040
 
        self.seen_ids = set()
1041
1017
        self.state = state
1042
1018
        self.current_root = None
1043
1019
        self.current_root_unicode = None
1059
1035
        self.current_block_pos = -1
1060
1036
        self.current_dir_info = None
1061
1037
        self.current_dir_list = None
1062
 
        self._pending_consistent_entries = []
1063
1038
        self.path_index = 0
1064
1039
        self.root_dir_info = None
1065
1040
        self.bisect_left = bisect.bisect_left
1066
1041
        self.pathjoin = osutils.pathjoin
1067
1042
        self.fstat = os.fstat
1068
1043
        self.sha_file = osutils.sha_file
1069
 
        if target_index != 0:
1070
 
            # A lot of code in here depends on target_index == 0
1071
 
            raise errors.BzrError('unsupported target index')
1072
1044
 
1073
1045
    cdef _process_entry(self, entry, path_info):
1074
1046
        """Compare an entry and real disk to generate delta information.
1075
1047
 
1076
1048
        :param path_info: top_relpath, basename, kind, lstat, abspath for
1077
 
            the path of entry. If None, then the path is considered absent in 
1078
 
            the target (Perhaps we should pass in a concrete entry for this ?)
 
1049
            the path of entry. If None, then the path is considered absent.
 
1050
            (Perhaps we should pass in a concrete entry for this ?)
1079
1051
            Basename is returned as a utf8 string because we expect this
1080
1052
            tuple will be ignored, and don't want to take the time to
1081
1053
            decode.
1082
1054
        :return: (iter_changes_result, changed). If the entry has not been
1083
1055
            handled then changed is None. Otherwise it is False if no content
1084
 
            or metadata changes have occured, and True if any content or
1085
 
            metadata change has occurred. If self.include_unchanged is True then
 
1056
            or metadata changes have occured, and None if any content or
 
1057
            metadata change has occured. If self.include_unchanged is True then
1086
1058
            if changed is not None, iter_changes_result will always be a result
1087
1059
            tuple. Otherwise, iter_changes_result is None unless changed is
1088
1060
            True.
1127
1099
            else:
1128
1100
                # add the source to the search path to find any children it
1129
1101
                # has.  TODO ? : only add if it is a container ?
1130
 
                if (not self.doing_consistency_expansion and 
1131
 
                    not osutils.is_inside_any(self.searched_specific_files,
1132
 
                                             source_details[1])):
 
1102
                if not osutils.is_inside_any(self.searched_specific_files,
 
1103
                                             source_details[1]):
1133
1104
                    self.search_specific_files.add(source_details[1])
1134
 
                    # expanding from a user requested path, parent expansion
1135
 
                    # for delta consistency happens later.
1136
1105
                # generate the old path; this is needed for stating later
1137
1106
                # as well.
1138
1107
                old_path = source_details[1]
1203
1172
                        content_change = 0
1204
1173
                    target_exec = False
1205
1174
                else:
1206
 
                    if path is None:
1207
 
                        path = self.pathjoin(old_dirname, old_basename)
1208
 
                    raise errors.BadFileKindError(path, path_info[2])
 
1175
                    raise Exception, "unknown kind %s" % path_info[2]
1209
1176
            if source_minikind == c'd':
1210
1177
                if path is None:
1211
1178
                    old_path = path = self.pathjoin(old_dirname, old_basename)
1213
1180
                    file_id = entry[0][2]
1214
1181
                self.old_dirname_to_file_id[old_path] = file_id
1215
1182
            # parent id is the entry for the path in the target tree
1216
 
            if old_basename and old_dirname == self.last_source_parent[0]:
1217
 
                # use a cached hit for non-root source entries.
 
1183
            if old_dirname == self.last_source_parent[0]:
1218
1184
                source_parent_id = self.last_source_parent[1]
1219
1185
            else:
1220
1186
                try:
1230
1196
                    self.last_source_parent[0] = old_dirname
1231
1197
                    self.last_source_parent[1] = source_parent_id
1232
1198
            new_dirname = entry[0][0]
1233
 
            if entry[0][1] and new_dirname == self.last_target_parent[0]:
1234
 
                # use a cached hit for non-root target entries.
 
1199
            if new_dirname == self.last_target_parent[0]:
1235
1200
                target_parent_id = self.last_target_parent[1]
1236
1201
            else:
1237
1202
                try:
1348
1313
            # a renamed parent. TODO: handle this efficiently. Its not
1349
1314
            # common case to rename dirs though, so a correct but slow
1350
1315
            # implementation will do.
1351
 
            if (not self.doing_consistency_expansion and 
1352
 
                not osutils.is_inside_any(self.searched_specific_files,
1353
 
                    target_details[1])):
 
1316
            if not osutils.is_inside_any(self.searched_specific_files, target_details[1]):
1354
1317
                self.search_specific_files.add(target_details[1])
1355
 
                # We don't expand the specific files parents list here as
1356
 
                # the path is absent in target and won't create a delta with
1357
 
                # missing parent.
1358
1318
        elif ((source_minikind == c'r' or source_minikind == c'a') and
1359
1319
              (target_minikind == c'r' or target_minikind == c'a')):
1360
1320
            # neither of the selected trees contain this path,
1374
1334
    def iter_changes(self):
1375
1335
        return self
1376
1336
 
1377
 
    cdef int _gather_result_for_consistency(self, result) except -1:
1378
 
        """Check a result we will yield to make sure we are consistent later.
1379
 
        
1380
 
        This gathers result's parents into a set to output later.
1381
 
 
1382
 
        :param result: A result tuple.
1383
 
        """
1384
 
        if not self.partial or not result[0]:
1385
 
            return 0
1386
 
        self.seen_ids.add(result[0])
1387
 
        new_path = result[1][1]
1388
 
        if new_path:
1389
 
            # Not the root and not a delete: queue up the parents of the path.
1390
 
            self.search_specific_file_parents.update(
1391
 
                osutils.parent_directories(new_path.encode('utf8')))
1392
 
            # Add the root directory which parent_directories does not
1393
 
            # provide.
1394
 
            self.search_specific_file_parents.add('')
1395
 
        return 0
1396
 
 
1397
 
    cdef int _update_current_block(self) except -1:
 
1337
    cdef void _update_current_block(self):
1398
1338
        if (self.block_index < len(self.state._dirblocks) and
1399
1339
            osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1400
1340
            self.current_block = self.state._dirblocks[self.block_index]
1403
1343
        else:
1404
1344
            self.current_block = None
1405
1345
            self.current_block_list = None
1406
 
        return 0
1407
1346
 
1408
1347
    def __next__(self):
1409
1348
        # Simple thunk to allow tail recursion without pyrex confusion
1467
1406
            entry = self.root_entries[self.root_entries_pos]
1468
1407
            self.root_entries_pos = self.root_entries_pos + 1
1469
1408
            result, changed = self._process_entry(entry, self.root_dir_info)
1470
 
            if changed is not None:
1471
 
                if changed:
1472
 
                    self._gather_result_for_consistency(result)
1473
 
                if changed or self.include_unchanged:
1474
 
                    return result
 
1409
            if changed is not None and changed or self.include_unchanged:
 
1410
                return result
1475
1411
        # Have we finished the prior root, or never started one ?
1476
1412
        if self.current_root is None:
1477
1413
            # TODO: the pending list should be lexically sorted?  the
1480
1416
                self.current_root = self.search_specific_files.pop()
1481
1417
            except KeyError:
1482
1418
                raise StopIteration()
 
1419
            self.current_root_unicode = self.current_root.decode('utf8')
1483
1420
            self.searched_specific_files.add(self.current_root)
1484
1421
            # process the entries for this containing directory: the rest will be
1485
1422
            # found by their parents recursively.
1486
1423
            self.root_entries = self.state._entries_for_path(self.current_root)
1487
1424
            self.root_entries_len = len(self.root_entries)
1488
 
            self.current_root_unicode = self.current_root.decode('utf8')
1489
1425
            self.root_abspath = self.tree.abspath(self.current_root_unicode)
1490
1426
            try:
1491
1427
                root_stat = os.lstat(self.root_abspath)
1522
1458
                result, changed = self._process_entry(entry, self.root_dir_info)
1523
1459
                if changed is not None:
1524
1460
                    path_handled = -1
1525
 
                    if changed:
1526
 
                        self._gather_result_for_consistency(result)
1527
1461
                    if changed or self.include_unchanged:
1528
1462
                        return result
1529
1463
            # handle unversioned specified paths:
1542
1476
                      )
1543
1477
            # If we reach here, the outer flow continues, which enters into the
1544
1478
            # per-root setup logic.
1545
 
        if (self.current_dir_info is None and self.current_block is None and not
1546
 
            self.doing_consistency_expansion):
 
1479
        if self.current_dir_info is None and self.current_block is None:
1547
1480
            # setup iteration of this root:
1548
1481
            self.current_dir_list = None
1549
1482
            if self.root_dir_info and self.root_dir_info[2] == 'tree-reference':
1673
1606
                        # advance the entry only, after processing.
1674
1607
                        result, changed = self._process_entry(current_entry, None)
1675
1608
                        if changed is not None:
1676
 
                            if changed:
1677
 
                                self._gather_result_for_consistency(result)
1678
1609
                            if changed or self.include_unchanged:
1679
1610
                                return result
1680
1611
                    self.block_index = self.block_index + 1
1687
1618
            # More supplied paths to process
1688
1619
            self.current_root = None
1689
1620
            return self._iter_next()
1690
 
        # Start expanding more conservatively, adding paths the user may not
1691
 
        # have intended but required for consistent deltas.
1692
 
        self.doing_consistency_expansion = 1
1693
 
        if not self._pending_consistent_entries:
1694
 
            self._pending_consistent_entries = self._next_consistent_entries()
1695
 
        while self._pending_consistent_entries:
1696
 
            result, changed = self._pending_consistent_entries.pop()
1697
 
            if changed is not None:
1698
 
                return result
1699
1621
        raise StopIteration()
1700
1622
 
1701
1623
    cdef object _maybe_tree_ref(self, current_path_info):
1783
1705
                            current_path_info)
1784
1706
                        if changed is not None:
1785
1707
                            path_handled = -1
1786
 
                            if not changed and not self.include_unchanged:
1787
 
                                changed = None
1788
1708
                # >- loop control starts here:
1789
1709
                # >- entry
1790
1710
                if advance_entry and current_entry is not None:
1806
1726
                            except UnicodeDecodeError:
1807
1727
                                raise errors.BadFilenameEncoding(
1808
1728
                                    current_path_info[0], osutils._fs_enc)
1809
 
                            if changed is not None:
 
1729
                            if result is not None:
1810
1730
                                raise AssertionError(
1811
1731
                                    "result is not None: %r" % result)
1812
1732
                            result = (None,
1817
1737
                                (None, self.utf8_decode(current_path_info[1])[0]),
1818
1738
                                (None, current_path_info[2]),
1819
1739
                                (None, new_executable))
1820
 
                            changed = True
1821
1740
                        # dont descend into this unversioned path if it is
1822
1741
                        # a dir
1823
1742
                        if current_path_info[2] in ('directory'):
1836
1755
                                current_path_info)
1837
1756
                    else:
1838
1757
                        current_path_info = None
1839
 
                if changed is not None:
 
1758
                if result is not None:
1840
1759
                    # Found a result on this pass, yield it
1841
 
                    if changed:
1842
 
                        self._gather_result_for_consistency(result)
1843
 
                    if changed or self.include_unchanged:
1844
 
                        return result
 
1760
                    return result
1845
1761
            if self.current_block is not None:
1846
1762
                self.block_index = self.block_index + 1
1847
1763
                self._update_current_block()
1853
1769
                    self.current_dir_list = self.current_dir_info[1]
1854
1770
                except StopIteration:
1855
1771
                    self.current_dir_info = None
1856
 
 
1857
 
    cdef object _next_consistent_entries(self):
1858
 
        """Grabs the next specific file parent case to consider.
1859
 
        
1860
 
        :return: A list of the results, each of which is as for _process_entry.
1861
 
        """
1862
 
        results = []
1863
 
        while self.search_specific_file_parents:
1864
 
            # Process the parent directories for the paths we were iterating.
1865
 
            # Even in extremely large trees this should be modest, so currently
1866
 
            # no attempt is made to optimise.
1867
 
            path_utf8 = self.search_specific_file_parents.pop()
1868
 
            if path_utf8 in self.searched_exact_paths:
1869
 
                # We've examined this path.
1870
 
                continue
1871
 
            if osutils.is_inside_any(self.searched_specific_files, path_utf8):
1872
 
                # We've examined this path.
1873
 
                continue
1874
 
            path_entries = self.state._entries_for_path(path_utf8)
1875
 
            # We need either one or two entries. If the path in
1876
 
            # self.target_index has moved (so the entry in source_index is in
1877
 
            # 'ar') then we need to also look for the entry for this path in
1878
 
            # self.source_index, to output the appropriate delete-or-rename.
1879
 
            selected_entries = []
1880
 
            found_item = False
1881
 
            for candidate_entry in path_entries:
1882
 
                # Find entries present in target at this path:
1883
 
                if candidate_entry[1][self.target_index][0] not in 'ar':
1884
 
                    found_item = True
1885
 
                    selected_entries.append(candidate_entry)
1886
 
                # Find entries present in source at this path:
1887
 
                elif (self.source_index is not None and
1888
 
                    candidate_entry[1][self.source_index][0] not in 'ar'):
1889
 
                    found_item = True
1890
 
                    if candidate_entry[1][self.target_index][0] == 'a':
1891
 
                        # Deleted, emit it here.
1892
 
                        selected_entries.append(candidate_entry)
1893
 
                    else:
1894
 
                        # renamed, emit it when we process the directory it
1895
 
                        # ended up at.
1896
 
                        self.search_specific_file_parents.add(
1897
 
                            candidate_entry[1][self.target_index][1])
1898
 
            if not found_item:
1899
 
                raise AssertionError(
1900
 
                    "Missing entry for specific path parent %r, %r" % (
1901
 
                    path_utf8, path_entries))
1902
 
            path_info = self._path_info(path_utf8, path_utf8.decode('utf8'))
1903
 
            for entry in selected_entries:
1904
 
                if entry[0][2] in self.seen_ids:
1905
 
                    continue
1906
 
                result, changed = self._process_entry(entry, path_info)
1907
 
                if changed is None:
1908
 
                    raise AssertionError(
1909
 
                        "Got entry<->path mismatch for specific path "
1910
 
                        "%r entry %r path_info %r " % (
1911
 
                        path_utf8, entry, path_info))
1912
 
                # Only include changes - we're outside the users requested
1913
 
                # expansion.
1914
 
                if changed:
1915
 
                    self._gather_result_for_consistency(result)
1916
 
                    if (result[6][0] == 'directory' and
1917
 
                        result[6][1] != 'directory'):
1918
 
                        # This stopped being a directory, the old children have
1919
 
                        # to be included.
1920
 
                        if entry[1][self.source_index][0] == 'r':
1921
 
                            # renamed, take the source path
1922
 
                            entry_path_utf8 = entry[1][self.source_index][1]
1923
 
                        else:
1924
 
                            entry_path_utf8 = path_utf8
1925
 
                        initial_key = (entry_path_utf8, '', '')
1926
 
                        block_index, _ = self.state._find_block_index_from_key(
1927
 
                            initial_key)
1928
 
                        if block_index == 0:
1929
 
                            # The children of the root are in block index 1.
1930
 
                            block_index = block_index + 1
1931
 
                        current_block = None
1932
 
                        if block_index < len(self.state._dirblocks):
1933
 
                            current_block = self.state._dirblocks[block_index]
1934
 
                            if not osutils.is_inside(
1935
 
                                entry_path_utf8, current_block[0]):
1936
 
                                # No entries for this directory at all.
1937
 
                                current_block = None
1938
 
                        if current_block is not None:
1939
 
                            for entry in current_block[1]:
1940
 
                                if entry[1][self.source_index][0] in 'ar':
1941
 
                                    # Not in the source tree, so doesn't have to be
1942
 
                                    # included.
1943
 
                                    continue
1944
 
                                # Path of the entry itself.
1945
 
                                self.search_specific_file_parents.add(
1946
 
                                    self.pathjoin(*entry[0][:2]))
1947
 
                if changed or self.include_unchanged:
1948
 
                    results.append((result, changed))
1949
 
            self.searched_exact_paths.add(path_utf8)
1950
 
        return results
1951
 
 
1952
 
    cdef object _path_info(self, utf8_path, unicode_path):
1953
 
        """Generate path_info for unicode_path.
1954
 
 
1955
 
        :return: None if unicode_path does not exist, or a path_info tuple.
1956
 
        """
1957
 
        abspath = self.tree.abspath(unicode_path)
1958
 
        try:
1959
 
            stat = os.lstat(abspath)
1960
 
        except OSError, e:
1961
 
            if e.errno == errno.ENOENT:
1962
 
                # the path does not exist.
1963
 
                return None
1964
 
            else:
1965
 
                raise
1966
 
        utf8_basename = utf8_path.rsplit('/', 1)[-1]
1967
 
        dir_info = (utf8_path, utf8_basename,
1968
 
            osutils.file_kind_from_stat_mode(stat.st_mode), stat,
1969
 
            abspath)
1970
 
        if dir_info[2] == 'directory':
1971
 
            if self.tree._directory_is_tree_reference(
1972
 
                unicode_path):
1973
 
                self.root_dir_info = self.root_dir_info[:2] + \
1974
 
                    ('tree-reference',) + self.root_dir_info[3:]
1975
 
        return dir_info