/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: Jonathan Lange
  • Date: 2009-12-09 09:20:42 UTC
  • mfrom: (4881 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4907.
  • Revision ID: jml@canonical.com-20091209092042-s2zgqcf8f39yzxpj
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
from bzrlib import cache_utf8, errors, osutils
30
30
from bzrlib.dirstate import DirState
31
 
from bzrlib.osutils import pathjoin, splitpath
 
31
from bzrlib.osutils import parent_directories, pathjoin, splitpath
32
32
 
33
33
 
34
34
# This is the Windows equivalent of ENOTDIR
963
963
 
964
964
cdef class ProcessEntryC:
965
965
 
 
966
    cdef int doing_consistency_expansion
966
967
    cdef object old_dirname_to_file_id # dict
967
968
    cdef object new_dirname_to_file_id # dict
968
 
    cdef readonly object uninteresting
969
969
    cdef object last_source_parent
970
970
    cdef object last_target_parent
971
 
    cdef object include_unchanged
 
971
    cdef int include_unchanged
 
972
    cdef int partial
972
973
    cdef object use_filesystem_for_exec
973
974
    cdef object utf8_decode
974
975
    cdef readonly object searched_specific_files
 
976
    cdef readonly object searched_exact_paths
975
977
    cdef object search_specific_files
 
978
    # The parents up to the root of the paths we are searching.
 
979
    # After all normal paths are returned, these specific items are returned.
 
980
    cdef object search_specific_file_parents
976
981
    cdef object state
977
982
    # Current iteration variables:
978
983
    cdef object current_root
990
995
    cdef object current_block_list
991
996
    cdef object current_dir_info
992
997
    cdef object current_dir_list
 
998
    cdef object _pending_consistent_entries # list
993
999
    cdef int path_index
994
1000
    cdef object root_dir_info
995
1001
    cdef object bisect_left
996
1002
    cdef object pathjoin
997
1003
    cdef object fstat
 
1004
    # A set of the ids we've output when doing partial output.
 
1005
    cdef object seen_ids
998
1006
    cdef object sha_file
999
1007
 
1000
1008
    def __init__(self, include_unchanged, use_filesystem_for_exec,
1001
1009
        search_specific_files, state, source_index, target_index,
1002
1010
        want_unversioned, tree):
 
1011
        self.doing_consistency_expansion = 0
1003
1012
        self.old_dirname_to_file_id = {}
1004
1013
        self.new_dirname_to_file_id = {}
1005
 
        # Just a sentry, so that _process_entry can say that this
1006
 
        # record is handled, but isn't interesting to process (unchanged)
1007
 
        self.uninteresting = object()
 
1014
        # Are we doing a partial iter_changes?
 
1015
        self.partial = set(['']).__ne__(search_specific_files)
1008
1016
        # Using a list so that we can access the values and change them in
1009
1017
        # nested scope. Each one is [path, file_id, entry]
1010
1018
        self.last_source_parent = [None, None]
1011
1019
        self.last_target_parent = [None, None]
1012
 
        self.include_unchanged = include_unchanged
 
1020
        if include_unchanged is None:
 
1021
            self.include_unchanged = False
 
1022
        else:
 
1023
            self.include_unchanged = int(include_unchanged)
1013
1024
        self.use_filesystem_for_exec = use_filesystem_for_exec
1014
1025
        self.utf8_decode = cache_utf8._utf8_decode
1015
1026
        # for all search_indexs in each path at or under each element of
1016
 
        # search_specific_files, if the detail is relocated: add the id, and add the
1017
 
        # relocated path as one to search if its not searched already. If the
1018
 
        # detail is not relocated, add the id.
 
1027
        # search_specific_files, if the detail is relocated: add the id, and
 
1028
        # add the relocated path as one to search if its not searched already.
 
1029
        # If the detail is not relocated, add the id.
1019
1030
        self.searched_specific_files = set()
 
1031
        # When we search exact paths without expanding downwards, we record
 
1032
        # that here.
 
1033
        self.searched_exact_paths = set()
1020
1034
        self.search_specific_files = search_specific_files
 
1035
        # The parents up to the root of the paths we are searching.
 
1036
        # After all normal paths are returned, these specific items are returned.
 
1037
        self.search_specific_file_parents = set()
 
1038
        # The ids we've sent out in the delta.
 
1039
        self.seen_ids = set()
1021
1040
        self.state = state
1022
1041
        self.current_root = None
1023
1042
        self.current_root_unicode = None
1039
1058
        self.current_block_pos = -1
1040
1059
        self.current_dir_info = None
1041
1060
        self.current_dir_list = None
 
1061
        self._pending_consistent_entries = []
1042
1062
        self.path_index = 0
1043
1063
        self.root_dir_info = None
1044
1064
        self.bisect_left = bisect.bisect_left
1045
1065
        self.pathjoin = osutils.pathjoin
1046
1066
        self.fstat = os.fstat
1047
1067
        self.sha_file = osutils.sha_file
 
1068
        if target_index != 0:
 
1069
            # A lot of code in here depends on target_index == 0
 
1070
            raise errors.BzrError('unsupported target index')
1048
1071
 
1049
1072
    cdef _process_entry(self, entry, path_info):
1050
1073
        """Compare an entry and real disk to generate delta information.
1051
1074
 
1052
1075
        :param path_info: top_relpath, basename, kind, lstat, abspath for
1053
 
            the path of entry. If None, then the path is considered absent.
1054
 
            (Perhaps we should pass in a concrete entry for this ?)
 
1076
            the path of entry. If None, then the path is considered absent in 
 
1077
            the target (Perhaps we should pass in a concrete entry for this ?)
1055
1078
            Basename is returned as a utf8 string because we expect this
1056
1079
            tuple will be ignored, and don't want to take the time to
1057
1080
            decode.
1058
 
        :return: None if the these don't match
1059
 
                 A tuple of information about the change, or
1060
 
                 the object 'uninteresting' if these match, but are
1061
 
                 basically identical.
 
1081
        :return: (iter_changes_result, changed). If the entry has not been
 
1082
            handled then changed is None. Otherwise it is False if no content
 
1083
            or metadata changes have occured, and True if any content or
 
1084
            metadata change has occurred. If self.include_unchanged is True then
 
1085
            if changed is not None, iter_changes_result will always be a result
 
1086
            tuple. Otherwise, iter_changes_result is None unless changed is
 
1087
            True.
1062
1088
        """
1063
1089
        cdef char target_minikind
1064
1090
        cdef char source_minikind
1100
1126
            else:
1101
1127
                # add the source to the search path to find any children it
1102
1128
                # has.  TODO ? : only add if it is a container ?
1103
 
                if not osutils.is_inside_any(self.searched_specific_files,
1104
 
                                             source_details[1]):
 
1129
                if (not self.doing_consistency_expansion and 
 
1130
                    not osutils.is_inside_any(self.searched_specific_files,
 
1131
                                             source_details[1])):
1105
1132
                    self.search_specific_files.add(source_details[1])
 
1133
                    # expanding from a user requested path, parent expansion
 
1134
                    # for delta consistency happens later.
1106
1135
                # generate the old path; this is needed for stating later
1107
1136
                # as well.
1108
1137
                old_path = source_details[1]
1173
1202
                        content_change = 0
1174
1203
                    target_exec = False
1175
1204
                else:
1176
 
                    raise Exception, "unknown kind %s" % path_info[2]
 
1205
                    if path is None:
 
1206
                        path = self.pathjoin(old_dirname, old_basename)
 
1207
                    raise errors.BadFileKindError(path, path_info[2])
1177
1208
            if source_minikind == c'd':
1178
1209
                if path is None:
1179
1210
                    old_path = path = self.pathjoin(old_dirname, old_basename)
1181
1212
                    file_id = entry[0][2]
1182
1213
                self.old_dirname_to_file_id[old_path] = file_id
1183
1214
            # parent id is the entry for the path in the target tree
1184
 
            if old_dirname == self.last_source_parent[0]:
 
1215
            if old_basename and old_dirname == self.last_source_parent[0]:
 
1216
                # use a cached hit for non-root source entries.
1185
1217
                source_parent_id = self.last_source_parent[1]
1186
1218
            else:
1187
1219
                try:
1197
1229
                    self.last_source_parent[0] = old_dirname
1198
1230
                    self.last_source_parent[1] = source_parent_id
1199
1231
            new_dirname = entry[0][0]
1200
 
            if new_dirname == self.last_target_parent[0]:
 
1232
            if entry[0][1] and new_dirname == self.last_target_parent[0]:
 
1233
                # use a cached hit for non-root target entries.
1201
1234
                target_parent_id = self.last_target_parent[1]
1202
1235
            else:
1203
1236
                try:
1220
1253
                    self.last_target_parent[1] = target_parent_id
1221
1254
 
1222
1255
            source_exec = source_details[3]
1223
 
            if (self.include_unchanged
1224
 
                or content_change
 
1256
            changed = (content_change
1225
1257
                or source_parent_id != target_parent_id
1226
1258
                or old_basename != entry[0][1]
1227
1259
                or source_exec != target_exec
1228
 
                ):
 
1260
                )
 
1261
            if not changed and not self.include_unchanged:
 
1262
                return None, False
 
1263
            else:
1229
1264
                if old_path is None:
1230
1265
                    path = self.pathjoin(old_dirname, old_basename)
1231
1266
                    old_path = path
1245
1280
                       (source_parent_id, target_parent_id),
1246
1281
                       (self.utf8_decode(old_basename)[0], self.utf8_decode(entry[0][1])[0]),
1247
1282
                       (source_kind, target_kind),
1248
 
                       (source_exec, target_exec))
1249
 
            else:
1250
 
                return self.uninteresting
 
1283
                       (source_exec, target_exec)), changed
1251
1284
        elif source_minikind == c'a' and _versioned_minikind(target_minikind):
1252
1285
            # looks like a new file
1253
1286
            path = self.pathjoin(entry[0][0], entry[0][1])
1280
1313
                       (None, parent_id),
1281
1314
                       (None, self.utf8_decode(entry[0][1])[0]),
1282
1315
                       (None, path_info[2]),
1283
 
                       (None, target_exec))
 
1316
                       (None, target_exec)), True
1284
1317
            else:
1285
1318
                # Its a missing file, report it as such.
1286
1319
                return (entry[0][2],
1290
1323
                       (None, parent_id),
1291
1324
                       (None, self.utf8_decode(entry[0][1])[0]),
1292
1325
                       (None, None),
1293
 
                       (None, False))
 
1326
                       (None, False)), True
1294
1327
        elif _versioned_minikind(source_minikind) and target_minikind == c'a':
1295
1328
            # unversioned, possibly, or possibly not deleted: we dont care.
1296
1329
            # if its still on disk, *and* theres no other entry at this
1308
1341
                   (parent_id, None),
1309
1342
                   (self.utf8_decode(entry[0][1])[0], None),
1310
1343
                   (_minikind_to_kind(source_minikind), None),
1311
 
                   (source_details[3], None))
 
1344
                   (source_details[3], None)), True
1312
1345
        elif _versioned_minikind(source_minikind) and target_minikind == c'r':
1313
1346
            # a rename; could be a true rename, or a rename inherited from
1314
1347
            # a renamed parent. TODO: handle this efficiently. Its not
1315
1348
            # common case to rename dirs though, so a correct but slow
1316
1349
            # implementation will do.
1317
 
            if not osutils.is_inside_any(self.searched_specific_files, target_details[1]):
 
1350
            if (not self.doing_consistency_expansion and 
 
1351
                not osutils.is_inside_any(self.searched_specific_files,
 
1352
                    target_details[1])):
1318
1353
                self.search_specific_files.add(target_details[1])
 
1354
                # We don't expand the specific files parents list here as
 
1355
                # the path is absent in target and won't create a delta with
 
1356
                # missing parent.
1319
1357
        elif ((source_minikind == c'r' or source_minikind == c'a') and
1320
1358
              (target_minikind == c'r' or target_minikind == c'a')):
1321
1359
            # neither of the selected trees contain this path,
1327
1365
                "source_minikind=%r, target_minikind=%r"
1328
1366
                % (source_minikind, target_minikind))
1329
1367
            ## import pdb;pdb.set_trace()
1330
 
        return None
 
1368
        return None, None
1331
1369
 
1332
1370
    def __iter__(self):
1333
1371
        return self
1335
1373
    def iter_changes(self):
1336
1374
        return self
1337
1375
 
 
1376
    cdef void _gather_result_for_consistency(self, result):
 
1377
        """Check a result we will yield to make sure we are consistent later.
 
1378
        
 
1379
        This gathers result's parents into a set to output later.
 
1380
 
 
1381
        :param result: A result tuple.
 
1382
        """
 
1383
        if not self.partial or not result[0]:
 
1384
            return
 
1385
        self.seen_ids.add(result[0])
 
1386
        new_path = result[1][1]
 
1387
        if new_path:
 
1388
            # Not the root and not a delete: queue up the parents of the path.
 
1389
            self.search_specific_file_parents.update(
 
1390
                osutils.parent_directories(new_path.encode('utf8')))
 
1391
            # Add the root directory which parent_directories does not
 
1392
            # provide.
 
1393
            self.search_specific_file_parents.add('')
 
1394
 
1338
1395
    cdef void _update_current_block(self):
1339
1396
        if (self.block_index < len(self.state._dirblocks) and
1340
1397
            osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1401
1458
        cdef char * current_dirname_c, * current_blockname_c
1402
1459
        cdef int advance_entry, advance_path
1403
1460
        cdef int path_handled
1404
 
        uninteresting = self.uninteresting
1405
1461
        searched_specific_files = self.searched_specific_files
1406
1462
        # Are we walking a root?
1407
1463
        while self.root_entries_pos < self.root_entries_len:
1408
1464
            entry = self.root_entries[self.root_entries_pos]
1409
1465
            self.root_entries_pos = self.root_entries_pos + 1
1410
 
            result = self._process_entry(entry, self.root_dir_info)
1411
 
            if result is not None and result is not self.uninteresting:
1412
 
                return result
 
1466
            result, changed = self._process_entry(entry, self.root_dir_info)
 
1467
            if changed is not None:
 
1468
                if changed:
 
1469
                    self._gather_result_for_consistency(result)
 
1470
                if changed or self.include_unchanged:
 
1471
                    return result
1413
1472
        # Have we finished the prior root, or never started one ?
1414
1473
        if self.current_root is None:
1415
1474
            # TODO: the pending list should be lexically sorted?  the
1418
1477
                self.current_root = self.search_specific_files.pop()
1419
1478
            except KeyError:
1420
1479
                raise StopIteration()
1421
 
            self.current_root_unicode = self.current_root.decode('utf8')
1422
1480
            self.searched_specific_files.add(self.current_root)
1423
1481
            # process the entries for this containing directory: the rest will be
1424
1482
            # found by their parents recursively.
1425
1483
            self.root_entries = self.state._entries_for_path(self.current_root)
1426
1484
            self.root_entries_len = len(self.root_entries)
 
1485
            self.current_root_unicode = self.current_root.decode('utf8')
1427
1486
            self.root_abspath = self.tree.abspath(self.current_root_unicode)
1428
1487
            try:
1429
1488
                root_stat = os.lstat(self.root_abspath)
1457
1516
            while self.root_entries_pos < self.root_entries_len:
1458
1517
                entry = self.root_entries[self.root_entries_pos]
1459
1518
                self.root_entries_pos = self.root_entries_pos + 1
1460
 
                result = self._process_entry(entry, self.root_dir_info)
1461
 
                if result is not None:
 
1519
                result, changed = self._process_entry(entry, self.root_dir_info)
 
1520
                if changed is not None:
1462
1521
                    path_handled = -1
1463
 
                    if result is not self.uninteresting:
 
1522
                    if changed:
 
1523
                        self._gather_result_for_consistency(result)
 
1524
                    if changed or self.include_unchanged:
1464
1525
                        return result
1465
1526
            # handle unversioned specified paths:
1466
1527
            if self.want_unversioned and not path_handled and self.root_dir_info:
1478
1539
                      )
1479
1540
            # If we reach here, the outer flow continues, which enters into the
1480
1541
            # per-root setup logic.
1481
 
        if self.current_dir_info is None and self.current_block is None:
 
1542
        if (self.current_dir_info is None and self.current_block is None and not
 
1543
            self.doing_consistency_expansion):
1482
1544
            # setup iteration of this root:
1483
1545
            self.current_dir_list = None
1484
1546
            if self.root_dir_info and self.root_dir_info[2] == 'tree-reference':
1606
1668
                        self.current_block_pos = self.current_block_pos + 1
1607
1669
                        # entry referring to file not present on disk.
1608
1670
                        # advance the entry only, after processing.
1609
 
                        result = self._process_entry(current_entry, None)
1610
 
                        if result is not None:
1611
 
                            if result is not self.uninteresting:
 
1671
                        result, changed = self._process_entry(current_entry, None)
 
1672
                        if changed is not None:
 
1673
                            if changed:
 
1674
                                self._gather_result_for_consistency(result)
 
1675
                            if changed or self.include_unchanged:
1612
1676
                                return result
1613
1677
                    self.block_index = self.block_index + 1
1614
1678
                    self._update_current_block()
1620
1684
            # More supplied paths to process
1621
1685
            self.current_root = None
1622
1686
            return self._iter_next()
 
1687
        # Start expanding more conservatively, adding paths the user may not
 
1688
        # have intended but required for consistent deltas.
 
1689
        self.doing_consistency_expansion = 1
 
1690
        if not self._pending_consistent_entries:
 
1691
            self._pending_consistent_entries = self._next_consistent_entries()
 
1692
        while self._pending_consistent_entries:
 
1693
            result, changed = self._pending_consistent_entries.pop()
 
1694
            if changed is not None:
 
1695
                return result
1623
1696
        raise StopIteration()
1624
1697
 
1625
1698
    cdef object _maybe_tree_ref(self, current_path_info):
1675
1748
                    pass
1676
1749
                elif current_path_info is None:
1677
1750
                    # no path is fine: the per entry code will handle it.
1678
 
                    result = self._process_entry(current_entry, current_path_info)
1679
 
                    if result is not None:
1680
 
                        if result is self.uninteresting:
1681
 
                            result = None
 
1751
                    result, changed = self._process_entry(current_entry,
 
1752
                        current_path_info)
1682
1753
                else:
1683
1754
                    minikind = _minikind_from_string(
1684
1755
                        current_entry[1][self.target_index][0])
1699
1770
                        else:
1700
1771
                            # entry referring to file not present on disk.
1701
1772
                            # advance the entry only, after processing.
1702
 
                            result = self._process_entry(current_entry, None)
1703
 
                            if result is not None:
1704
 
                                if result is self.uninteresting:
1705
 
                                    result = None
 
1773
                            result, changed = self._process_entry(current_entry,
 
1774
                                None)
1706
1775
                            advance_path = 0
1707
1776
                    else:
1708
1777
                        # paths are the same,and the dirstate entry is not
1709
1778
                        # absent or renamed.
1710
 
                        result = self._process_entry(current_entry, current_path_info)
1711
 
                        if result is not None:
 
1779
                        result, changed = self._process_entry(current_entry,
 
1780
                            current_path_info)
 
1781
                        if changed is not None:
1712
1782
                            path_handled = -1
1713
 
                            if result is self.uninteresting:
1714
 
                                result = None
 
1783
                            if not changed and not self.include_unchanged:
 
1784
                                changed = None
1715
1785
                # >- loop control starts here:
1716
1786
                # >- entry
1717
1787
                if advance_entry and current_entry is not None:
1733
1803
                            except UnicodeDecodeError:
1734
1804
                                raise errors.BadFilenameEncoding(
1735
1805
                                    current_path_info[0], osutils._fs_enc)
1736
 
                            if result is not None:
 
1806
                            if changed is not None:
1737
1807
                                raise AssertionError(
1738
1808
                                    "result is not None: %r" % result)
1739
1809
                            result = (None,
1744
1814
                                (None, self.utf8_decode(current_path_info[1])[0]),
1745
1815
                                (None, current_path_info[2]),
1746
1816
                                (None, new_executable))
 
1817
                            changed = True
1747
1818
                        # dont descend into this unversioned path if it is
1748
1819
                        # a dir
1749
1820
                        if current_path_info[2] in ('directory'):
1762
1833
                                current_path_info)
1763
1834
                    else:
1764
1835
                        current_path_info = None
1765
 
                if result is not None:
 
1836
                if changed is not None:
1766
1837
                    # Found a result on this pass, yield it
1767
 
                    return result
 
1838
                    if changed:
 
1839
                        self._gather_result_for_consistency(result)
 
1840
                    if changed or self.include_unchanged:
 
1841
                        return result
1768
1842
            if self.current_block is not None:
1769
1843
                self.block_index = self.block_index + 1
1770
1844
                self._update_current_block()
1776
1850
                    self.current_dir_list = self.current_dir_info[1]
1777
1851
                except StopIteration:
1778
1852
                    self.current_dir_info = None
 
1853
 
 
1854
    cdef object _next_consistent_entries(self):
 
1855
        """Grabs the next specific file parent case to consider.
 
1856
        
 
1857
        :return: A list of the results, each of which is as for _process_entry.
 
1858
        """
 
1859
        results = []
 
1860
        while self.search_specific_file_parents:
 
1861
            # Process the parent directories for the paths we were iterating.
 
1862
            # Even in extremely large trees this should be modest, so currently
 
1863
            # no attempt is made to optimise.
 
1864
            path_utf8 = self.search_specific_file_parents.pop()
 
1865
            if path_utf8 in self.searched_exact_paths:
 
1866
                # We've examined this path.
 
1867
                continue
 
1868
            if osutils.is_inside_any(self.searched_specific_files, path_utf8):
 
1869
                # We've examined this path.
 
1870
                continue
 
1871
            path_entries = self.state._entries_for_path(path_utf8)
 
1872
            # We need either one or two entries. If the path in
 
1873
            # self.target_index has moved (so the entry in source_index is in
 
1874
            # 'ar') then we need to also look for the entry for this path in
 
1875
            # self.source_index, to output the appropriate delete-or-rename.
 
1876
            selected_entries = []
 
1877
            found_item = False
 
1878
            for candidate_entry in path_entries:
 
1879
                # Find entries present in target at this path:
 
1880
                if candidate_entry[1][self.target_index][0] not in 'ar':
 
1881
                    found_item = True
 
1882
                    selected_entries.append(candidate_entry)
 
1883
                # Find entries present in source at this path:
 
1884
                elif (self.source_index is not None and
 
1885
                    candidate_entry[1][self.source_index][0] not in 'ar'):
 
1886
                    found_item = True
 
1887
                    if candidate_entry[1][self.target_index][0] == 'a':
 
1888
                        # Deleted, emit it here.
 
1889
                        selected_entries.append(candidate_entry)
 
1890
                    else:
 
1891
                        # renamed, emit it when we process the directory it
 
1892
                        # ended up at.
 
1893
                        self.search_specific_file_parents.add(
 
1894
                            candidate_entry[1][self.target_index][1])
 
1895
            if not found_item:
 
1896
                raise AssertionError(
 
1897
                    "Missing entry for specific path parent %r, %r" % (
 
1898
                    path_utf8, path_entries))
 
1899
            path_info = self._path_info(path_utf8, path_utf8.decode('utf8'))
 
1900
            for entry in selected_entries:
 
1901
                if entry[0][2] in self.seen_ids:
 
1902
                    continue
 
1903
                result, changed = self._process_entry(entry, path_info)
 
1904
                if changed is None:
 
1905
                    raise AssertionError(
 
1906
                        "Got entry<->path mismatch for specific path "
 
1907
                        "%r entry %r path_info %r " % (
 
1908
                        path_utf8, entry, path_info))
 
1909
                # Only include changes - we're outside the users requested
 
1910
                # expansion.
 
1911
                if changed:
 
1912
                    self._gather_result_for_consistency(result)
 
1913
                    if (result[6][0] == 'directory' and
 
1914
                        result[6][1] != 'directory'):
 
1915
                        # This stopped being a directory, the old children have
 
1916
                        # to be included.
 
1917
                        if entry[1][self.source_index][0] == 'r':
 
1918
                            # renamed, take the source path
 
1919
                            entry_path_utf8 = entry[1][self.source_index][1]
 
1920
                        else:
 
1921
                            entry_path_utf8 = path_utf8
 
1922
                        initial_key = (entry_path_utf8, '', '')
 
1923
                        block_index, _ = self.state._find_block_index_from_key(
 
1924
                            initial_key)
 
1925
                        if block_index == 0:
 
1926
                            # The children of the root are in block index 1.
 
1927
                            block_index = block_index + 1
 
1928
                        current_block = None
 
1929
                        if block_index < len(self.state._dirblocks):
 
1930
                            current_block = self.state._dirblocks[block_index]
 
1931
                            if not osutils.is_inside(
 
1932
                                entry_path_utf8, current_block[0]):
 
1933
                                # No entries for this directory at all.
 
1934
                                current_block = None
 
1935
                        if current_block is not None:
 
1936
                            for entry in current_block[1]:
 
1937
                                if entry[1][self.source_index][0] in 'ar':
 
1938
                                    # Not in the source tree, so doesn't have to be
 
1939
                                    # included.
 
1940
                                    continue
 
1941
                                # Path of the entry itself.
 
1942
                                self.search_specific_file_parents.add(
 
1943
                                    self.pathjoin(*entry[0][:2]))
 
1944
                if changed or self.include_unchanged:
 
1945
                    results.append((result, changed))
 
1946
            self.searched_exact_paths.add(path_utf8)
 
1947
        return results
 
1948
 
 
1949
    cdef object _path_info(self, utf8_path, unicode_path):
 
1950
        """Generate path_info for unicode_path.
 
1951
 
 
1952
        :return: None if unicode_path does not exist, or a path_info tuple.
 
1953
        """
 
1954
        abspath = self.tree.abspath(unicode_path)
 
1955
        try:
 
1956
            stat = os.lstat(abspath)
 
1957
        except OSError, e:
 
1958
            if e.errno == errno.ENOENT:
 
1959
                # the path does not exist.
 
1960
                return None
 
1961
            else:
 
1962
                raise
 
1963
        utf8_basename = utf8_path.rsplit('/', 1)[-1]
 
1964
        dir_info = (utf8_path, utf8_basename,
 
1965
            osutils.file_kind_from_stat_mode(stat.st_mode), stat,
 
1966
            abspath)
 
1967
        if dir_info[2] == 'directory':
 
1968
            if self.tree._directory_is_tree_reference(
 
1969
                unicode_path):
 
1970
                self.root_dir_info = self.root_dir_info[:2] + \
 
1971
                    ('tree-reference',) + self.root_dir_info[3:]
 
1972
        return dir_info