~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/_dirstate_helpers_pyx.pyx

  • Committer: Martin Pool
  • Date: 2009-07-10 06:46:10 UTC
  • mto: (4525.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4526.
  • Revision ID: mbp@sourcefrog.net-20090710064610-sqviksbqp5i34sw2
Rename to per_interrepository

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-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
118
118
    # ??? memrchr is a GNU extension :(
119
119
    # void *memrchr(void *s, int c, size_t len)
120
120
 
121
 
# cimport all of the definitions we will need to access
122
 
from _static_tuple_c cimport import_static_tuple_c, StaticTuple, \
123
 
    StaticTuple_New, StaticTuple_SET_ITEM
124
 
 
125
 
import_static_tuple_c()
126
 
 
127
 
cdef void* _my_memrchr(void *s, int c, size_t n): # cannot_raise
 
121
 
 
122
cdef void* _my_memrchr(void *s, int c, size_t n):
128
123
    # memrchr seems to be a GNU extension, so we have to implement it ourselves
129
124
    cdef char *pos
130
125
    cdef char *start
161
156
        return None
162
157
    return <char*>found - <char*>_s
163
158
 
164
 
 
165
159
cdef object safe_string_from_size(char *s, Py_ssize_t size):
166
160
    if size < 0:
 
161
        # XXX: On 64-bit machines the <int> cast causes a C compiler warning.
167
162
        raise AssertionError(
168
 
            'tried to create a string with an invalid size: %d'
169
 
            % (size))
 
163
            'tried to create a string with an invalid size: %d @0x%x'
 
164
            % (size, <int>s))
170
165
    return PyString_FromStringAndSize(s, size)
171
166
 
172
167
 
173
 
cdef int _is_aligned(void *ptr): # cannot_raise
 
168
cdef int _is_aligned(void *ptr):
174
169
    """Is this pointer aligned to an integer size offset?
175
170
 
176
171
    :return: 1 if this pointer is aligned, 0 otherwise.
178
173
    return ((<intptr_t>ptr) & ((sizeof(int))-1)) == 0
179
174
 
180
175
 
181
 
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):
182
177
    cdef unsigned char *cur1
183
178
    cdef unsigned char *cur2
184
179
    cdef unsigned char *end1
300
295
 
301
296
 
302
297
cdef int _cmp_path_by_dirblock_intern(char *path1, int path1_len,
303
 
                                      char *path2, int path2_len): # cannot_raise
 
298
                                      char *path2, int path2_len):
304
299
    """Compare two paths by what directory they are in.
305
300
 
306
301
    see ``_cmp_path_by_dirblock`` for details.
615
610
        :param new_block: This is to let the caller know that it needs to
616
611
            create a new directory block to store the next entry.
617
612
        """
618
 
        cdef StaticTuple path_name_file_id_key
619
 
        cdef StaticTuple tmp
 
613
        cdef object path_name_file_id_key
620
614
        cdef char *entry_size_cstr
621
615
        cdef unsigned long int entry_size
622
616
        cdef char* executable_cstr
656
650
        # Build up the key that will be used.
657
651
        # By using <object>(void *) Pyrex will automatically handle the
658
652
        # Py_INCREF that we need.
659
 
        cur_dirname = <object>p_current_dirname[0]
660
 
        # Use StaticTuple_New to pre-allocate, rather than creating a regular
661
 
        # tuple and passing it to the StaticTuple constructor.
662
 
        # path_name_file_id_key = StaticTuple(<object>p_current_dirname[0],
663
 
        #                          self.get_next_str(),
664
 
        #                          self.get_next_str(),
665
 
        #                         )
666
 
        tmp = StaticTuple_New(3)
667
 
        Py_INCREF(cur_dirname); StaticTuple_SET_ITEM(tmp, 0, cur_dirname)
668
 
        cur_basename = self.get_next_str()
669
 
        cur_file_id = self.get_next_str()
670
 
        Py_INCREF(cur_basename); StaticTuple_SET_ITEM(tmp, 1, cur_basename)
671
 
        Py_INCREF(cur_file_id); StaticTuple_SET_ITEM(tmp, 2, cur_file_id)
672
 
        path_name_file_id_key = tmp
 
653
        path_name_file_id_key = (<object>p_current_dirname[0],
 
654
                                 self.get_next_str(),
 
655
                                 self.get_next_str(),
 
656
                                )
673
657
 
674
658
        # Parse all of the per-tree information. current has the information in
675
659
        # the same location as parent trees. The only difference is that 'info'
693
677
            executable_cstr = self.get_next(&cur_size)
694
678
            is_executable = (executable_cstr[0] == c'y')
695
679
            info = self.get_next_str()
696
 
            # TODO: If we want to use StaticTuple_New here we need to be pretty
697
 
            #       careful. We are relying on a bit of Pyrex
698
 
            #       automatic-conversion from 'int' to PyInt, and that doesn't
699
 
            #       play well with the StaticTuple_SET_ITEM macro.
700
 
            #       Timing doesn't (yet) show a worthwile improvement in speed
701
 
            #       versus complexity and maintainability.
702
 
            # tmp = StaticTuple_New(5)
703
 
            # Py_INCREF(minikind); StaticTuple_SET_ITEM(tmp, 0, minikind)
704
 
            # Py_INCREF(fingerprint); StaticTuple_SET_ITEM(tmp, 1, fingerprint)
705
 
            # Py_INCREF(entry_size); StaticTuple_SET_ITEM(tmp, 2, entry_size)
706
 
            # Py_INCREF(is_executable); StaticTuple_SET_ITEM(tmp, 3, is_executable)
707
 
            # Py_INCREF(info); StaticTuple_SET_ITEM(tmp, 4, info)
708
 
            # PyList_Append(trees, tmp)
709
 
            PyList_Append(trees, StaticTuple(
 
680
            PyList_Append(trees, (
710
681
                minikind,     # minikind
711
682
                fingerprint,  # fingerprint
712
683
                entry_size,   # size
797
768
    state._dirblock_state = DirState.IN_MEMORY_UNMODIFIED
798
769
 
799
770
 
800
 
cdef int minikind_from_mode(int mode): # cannot_raise
 
771
cdef int minikind_from_mode(int mode):
801
772
    # in order of frequency:
802
773
    if S_ISREG(mode):
803
774
        return c"f"
944
915
    return link_or_sha1
945
916
 
946
917
 
947
 
# TODO: Do we want to worry about exceptions here?
948
 
cdef char _minikind_from_string(object string) except? -1:
 
918
cdef char _minikind_from_string(object string):
949
919
    """Convert a python string to a char."""
950
920
    return PyString_AsString(string)[0]
951
921
 
983
953
    raise KeyError(PyString_FromStringAndSize(_minikind, 1))
984
954
 
985
955
 
986
 
cdef int _versioned_minikind(char minikind): # cannot_raise
 
956
cdef int _versioned_minikind(char minikind):
987
957
    """Return non-zero if minikind is in fltd"""
988
958
    return (minikind == c'f' or
989
959
            minikind == c'd' or
993
963
 
994
964
cdef class ProcessEntryC:
995
965
 
996
 
    cdef int doing_consistency_expansion
997
966
    cdef object old_dirname_to_file_id # dict
998
967
    cdef object new_dirname_to_file_id # dict
 
968
    cdef readonly object uninteresting
999
969
    cdef object last_source_parent
1000
970
    cdef object last_target_parent
1001
 
    cdef int include_unchanged
1002
 
    cdef int partial
 
971
    cdef object include_unchanged
1003
972
    cdef object use_filesystem_for_exec
1004
973
    cdef object utf8_decode
1005
974
    cdef readonly object searched_specific_files
1006
 
    cdef readonly object searched_exact_paths
1007
975
    cdef object search_specific_files
1008
 
    # The parents up to the root of the paths we are searching.
1009
 
    # After all normal paths are returned, these specific items are returned.
1010
 
    cdef object search_specific_file_parents
1011
976
    cdef object state
1012
977
    # Current iteration variables:
1013
978
    cdef object current_root
1025
990
    cdef object current_block_list
1026
991
    cdef object current_dir_info
1027
992
    cdef object current_dir_list
1028
 
    cdef object _pending_consistent_entries # list
1029
993
    cdef int path_index
1030
994
    cdef object root_dir_info
1031
995
    cdef object bisect_left
1032
996
    cdef object pathjoin
1033
997
    cdef object fstat
1034
 
    # A set of the ids we've output when doing partial output.
1035
 
    cdef object seen_ids
1036
998
    cdef object sha_file
1037
999
 
1038
1000
    def __init__(self, include_unchanged, use_filesystem_for_exec,
1039
1001
        search_specific_files, state, source_index, target_index,
1040
1002
        want_unversioned, tree):
1041
 
        self.doing_consistency_expansion = 0
1042
1003
        self.old_dirname_to_file_id = {}
1043
1004
        self.new_dirname_to_file_id = {}
1044
 
        # Are we doing a partial iter_changes?
1045
 
        self.partial = set(['']).__ne__(search_specific_files)
 
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()
1046
1008
        # Using a list so that we can access the values and change them in
1047
1009
        # nested scope. Each one is [path, file_id, entry]
1048
1010
        self.last_source_parent = [None, None]
1049
1011
        self.last_target_parent = [None, None]
1050
 
        if include_unchanged is None:
1051
 
            self.include_unchanged = False
1052
 
        else:
1053
 
            self.include_unchanged = int(include_unchanged)
 
1012
        self.include_unchanged = include_unchanged
1054
1013
        self.use_filesystem_for_exec = use_filesystem_for_exec
1055
1014
        self.utf8_decode = cache_utf8._utf8_decode
1056
1015
        # for all search_indexs in each path at or under each element of
1057
 
        # search_specific_files, if the detail is relocated: add the id, and
1058
 
        # add the relocated path as one to search if its not searched already.
1059
 
        # If the detail is not relocated, add the id.
 
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.
1060
1019
        self.searched_specific_files = set()
1061
 
        # When we search exact paths without expanding downwards, we record
1062
 
        # that here.
1063
 
        self.searched_exact_paths = set()
1064
1020
        self.search_specific_files = search_specific_files
1065
 
        # The parents up to the root of the paths we are searching.
1066
 
        # After all normal paths are returned, these specific items are returned.
1067
 
        self.search_specific_file_parents = set()
1068
 
        # The ids we've sent out in the delta.
1069
 
        self.seen_ids = set()
1070
1021
        self.state = state
1071
1022
        self.current_root = None
1072
1023
        self.current_root_unicode = None
1088
1039
        self.current_block_pos = -1
1089
1040
        self.current_dir_info = None
1090
1041
        self.current_dir_list = None
1091
 
        self._pending_consistent_entries = []
1092
1042
        self.path_index = 0
1093
1043
        self.root_dir_info = None
1094
1044
        self.bisect_left = bisect.bisect_left
1095
1045
        self.pathjoin = osutils.pathjoin
1096
1046
        self.fstat = os.fstat
1097
1047
        self.sha_file = osutils.sha_file
1098
 
        if target_index != 0:
1099
 
            # A lot of code in here depends on target_index == 0
1100
 
            raise errors.BzrError('unsupported target index')
1101
1048
 
1102
1049
    cdef _process_entry(self, entry, path_info):
1103
1050
        """Compare an entry and real disk to generate delta information.
1104
1051
 
1105
1052
        :param path_info: top_relpath, basename, kind, lstat, abspath for
1106
 
            the path of entry. If None, then the path is considered absent in 
1107
 
            the target (Perhaps we should pass in a concrete entry for this ?)
 
1053
            the path of entry. If None, then the path is considered absent.
 
1054
            (Perhaps we should pass in a concrete entry for this ?)
1108
1055
            Basename is returned as a utf8 string because we expect this
1109
1056
            tuple will be ignored, and don't want to take the time to
1110
1057
            decode.
1111
 
        :return: (iter_changes_result, changed). If the entry has not been
1112
 
            handled then changed is None. Otherwise it is False if no content
1113
 
            or metadata changes have occured, and True if any content or
1114
 
            metadata change has occurred. If self.include_unchanged is True then
1115
 
            if changed is not None, iter_changes_result will always be a result
1116
 
            tuple. Otherwise, iter_changes_result is None unless changed is
1117
 
            True.
 
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.
1118
1062
        """
1119
1063
        cdef char target_minikind
1120
1064
        cdef char source_minikind
1156
1100
            else:
1157
1101
                # add the source to the search path to find any children it
1158
1102
                # has.  TODO ? : only add if it is a container ?
1159
 
                if (not self.doing_consistency_expansion and 
1160
 
                    not osutils.is_inside_any(self.searched_specific_files,
1161
 
                                             source_details[1])):
 
1103
                if not osutils.is_inside_any(self.searched_specific_files,
 
1104
                                             source_details[1]):
1162
1105
                    self.search_specific_files.add(source_details[1])
1163
 
                    # expanding from a user requested path, parent expansion
1164
 
                    # for delta consistency happens later.
1165
1106
                # generate the old path; this is needed for stating later
1166
1107
                # as well.
1167
1108
                old_path = source_details[1]
1232
1173
                        content_change = 0
1233
1174
                    target_exec = False
1234
1175
                else:
1235
 
                    if path is None:
1236
 
                        path = self.pathjoin(old_dirname, old_basename)
1237
 
                    raise errors.BadFileKindError(path, path_info[2])
 
1176
                    raise Exception, "unknown kind %s" % path_info[2]
1238
1177
            if source_minikind == c'd':
1239
1178
                if path is None:
1240
1179
                    old_path = path = self.pathjoin(old_dirname, old_basename)
1242
1181
                    file_id = entry[0][2]
1243
1182
                self.old_dirname_to_file_id[old_path] = file_id
1244
1183
            # parent id is the entry for the path in the target tree
1245
 
            if old_basename and old_dirname == self.last_source_parent[0]:
1246
 
                # use a cached hit for non-root source entries.
 
1184
            if old_dirname == self.last_source_parent[0]:
1247
1185
                source_parent_id = self.last_source_parent[1]
1248
1186
            else:
1249
1187
                try:
1250
1188
                    source_parent_id = self.old_dirname_to_file_id[old_dirname]
1251
 
                except KeyError, _:
 
1189
                except KeyError:
1252
1190
                    source_parent_entry = self.state._get_entry(self.source_index,
1253
1191
                                                           path_utf8=old_dirname)
1254
1192
                    source_parent_id = source_parent_entry[0][2]
1259
1197
                    self.last_source_parent[0] = old_dirname
1260
1198
                    self.last_source_parent[1] = source_parent_id
1261
1199
            new_dirname = entry[0][0]
1262
 
            if entry[0][1] and new_dirname == self.last_target_parent[0]:
1263
 
                # use a cached hit for non-root target entries.
 
1200
            if new_dirname == self.last_target_parent[0]:
1264
1201
                target_parent_id = self.last_target_parent[1]
1265
1202
            else:
1266
1203
                try:
1267
1204
                    target_parent_id = self.new_dirname_to_file_id[new_dirname]
1268
 
                except KeyError, _:
 
1205
                except KeyError:
1269
1206
                    # TODO: We don't always need to do the lookup, because the
1270
1207
                    #       parent entry will be the same as the source entry.
1271
1208
                    target_parent_entry = self.state._get_entry(self.target_index,
1283
1220
                    self.last_target_parent[1] = target_parent_id
1284
1221
 
1285
1222
            source_exec = source_details[3]
1286
 
            changed = (content_change
 
1223
            if (self.include_unchanged
 
1224
                or content_change
1287
1225
                or source_parent_id != target_parent_id
1288
1226
                or old_basename != entry[0][1]
1289
1227
                or source_exec != target_exec
1290
 
                )
1291
 
            if not changed and not self.include_unchanged:
1292
 
                return None, False
1293
 
            else:
 
1228
                ):
1294
1229
                if old_path is None:
1295
1230
                    path = self.pathjoin(old_dirname, old_basename)
1296
1231
                    old_path = path
1310
1245
                       (source_parent_id, target_parent_id),
1311
1246
                       (self.utf8_decode(old_basename)[0], self.utf8_decode(entry[0][1])[0]),
1312
1247
                       (source_kind, target_kind),
1313
 
                       (source_exec, target_exec)), changed
 
1248
                       (source_exec, target_exec))
 
1249
            else:
 
1250
                return self.uninteresting
1314
1251
        elif source_minikind == c'a' and _versioned_minikind(target_minikind):
1315
1252
            # looks like a new file
1316
1253
            path = self.pathjoin(entry[0][0], entry[0][1])
1343
1280
                       (None, parent_id),
1344
1281
                       (None, self.utf8_decode(entry[0][1])[0]),
1345
1282
                       (None, path_info[2]),
1346
 
                       (None, target_exec)), True
 
1283
                       (None, target_exec))
1347
1284
            else:
1348
1285
                # Its a missing file, report it as such.
1349
1286
                return (entry[0][2],
1353
1290
                       (None, parent_id),
1354
1291
                       (None, self.utf8_decode(entry[0][1])[0]),
1355
1292
                       (None, None),
1356
 
                       (None, False)), True
 
1293
                       (None, False))
1357
1294
        elif _versioned_minikind(source_minikind) and target_minikind == c'a':
1358
1295
            # unversioned, possibly, or possibly not deleted: we dont care.
1359
1296
            # if its still on disk, *and* theres no other entry at this
1371
1308
                   (parent_id, None),
1372
1309
                   (self.utf8_decode(entry[0][1])[0], None),
1373
1310
                   (_minikind_to_kind(source_minikind), None),
1374
 
                   (source_details[3], None)), True
 
1311
                   (source_details[3], None))
1375
1312
        elif _versioned_minikind(source_minikind) and target_minikind == c'r':
1376
1313
            # a rename; could be a true rename, or a rename inherited from
1377
1314
            # a renamed parent. TODO: handle this efficiently. Its not
1378
1315
            # common case to rename dirs though, so a correct but slow
1379
1316
            # implementation will do.
1380
 
            if (not self.doing_consistency_expansion and 
1381
 
                not osutils.is_inside_any(self.searched_specific_files,
1382
 
                    target_details[1])):
 
1317
            if not osutils.is_inside_any(self.searched_specific_files, target_details[1]):
1383
1318
                self.search_specific_files.add(target_details[1])
1384
 
                # We don't expand the specific files parents list here as
1385
 
                # the path is absent in target and won't create a delta with
1386
 
                # missing parent.
1387
1319
        elif ((source_minikind == c'r' or source_minikind == c'a') and
1388
1320
              (target_minikind == c'r' or target_minikind == c'a')):
1389
1321
            # neither of the selected trees contain this path,
1395
1327
                "source_minikind=%r, target_minikind=%r"
1396
1328
                % (source_minikind, target_minikind))
1397
1329
            ## import pdb;pdb.set_trace()
1398
 
        return None, None
 
1330
        return None
1399
1331
 
1400
1332
    def __iter__(self):
1401
1333
        return self
1403
1335
    def iter_changes(self):
1404
1336
        return self
1405
1337
 
1406
 
    cdef int _gather_result_for_consistency(self, result) except -1:
1407
 
        """Check a result we will yield to make sure we are consistent later.
1408
 
        
1409
 
        This gathers result's parents into a set to output later.
1410
 
 
1411
 
        :param result: A result tuple.
1412
 
        """
1413
 
        if not self.partial or not result[0]:
1414
 
            return 0
1415
 
        self.seen_ids.add(result[0])
1416
 
        new_path = result[1][1]
1417
 
        if new_path:
1418
 
            # Not the root and not a delete: queue up the parents of the path.
1419
 
            self.search_specific_file_parents.update(
1420
 
                osutils.parent_directories(new_path.encode('utf8')))
1421
 
            # Add the root directory which parent_directories does not
1422
 
            # provide.
1423
 
            self.search_specific_file_parents.add('')
1424
 
        return 0
1425
 
 
1426
 
    cdef int _update_current_block(self) except -1:
 
1338
    cdef void _update_current_block(self):
1427
1339
        if (self.block_index < len(self.state._dirblocks) and
1428
1340
            osutils.is_inside(self.current_root, self.state._dirblocks[self.block_index][0])):
1429
1341
            self.current_block = self.state._dirblocks[self.block_index]
1432
1344
        else:
1433
1345
            self.current_block = None
1434
1346
            self.current_block_list = None
1435
 
        return 0
1436
1347
 
1437
1348
    def __next__(self):
1438
1349
        # Simple thunk to allow tail recursion without pyrex confusion
1490
1401
        cdef char * current_dirname_c, * current_blockname_c
1491
1402
        cdef int advance_entry, advance_path
1492
1403
        cdef int path_handled
 
1404
        uninteresting = self.uninteresting
1493
1405
        searched_specific_files = self.searched_specific_files
1494
1406
        # Are we walking a root?
1495
1407
        while self.root_entries_pos < self.root_entries_len:
1496
1408
            entry = self.root_entries[self.root_entries_pos]
1497
1409
            self.root_entries_pos = self.root_entries_pos + 1
1498
 
            result, changed = self._process_entry(entry, self.root_dir_info)
1499
 
            if changed is not None:
1500
 
                if changed:
1501
 
                    self._gather_result_for_consistency(result)
1502
 
                if changed or self.include_unchanged:
1503
 
                    return result
 
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
1504
1413
        # Have we finished the prior root, or never started one ?
1505
1414
        if self.current_root is None:
1506
1415
            # TODO: the pending list should be lexically sorted?  the
1507
1416
            # interface doesn't require it.
1508
1417
            try:
1509
1418
                self.current_root = self.search_specific_files.pop()
1510
 
            except KeyError, _:
 
1419
            except KeyError:
1511
1420
                raise StopIteration()
 
1421
            self.current_root_unicode = self.current_root.decode('utf8')
1512
1422
            self.searched_specific_files.add(self.current_root)
1513
1423
            # process the entries for this containing directory: the rest will be
1514
1424
            # found by their parents recursively.
1515
1425
            self.root_entries = self.state._entries_for_path(self.current_root)
1516
1426
            self.root_entries_len = len(self.root_entries)
1517
 
            self.current_root_unicode = self.current_root.decode('utf8')
1518
1427
            self.root_abspath = self.tree.abspath(self.current_root_unicode)
1519
1428
            try:
1520
1429
                root_stat = os.lstat(self.root_abspath)
1548
1457
            while self.root_entries_pos < self.root_entries_len:
1549
1458
                entry = self.root_entries[self.root_entries_pos]
1550
1459
                self.root_entries_pos = self.root_entries_pos + 1
1551
 
                result, changed = self._process_entry(entry, self.root_dir_info)
1552
 
                if changed is not None:
 
1460
                result = self._process_entry(entry, self.root_dir_info)
 
1461
                if result is not None:
1553
1462
                    path_handled = -1
1554
 
                    if changed:
1555
 
                        self._gather_result_for_consistency(result)
1556
 
                    if changed or self.include_unchanged:
 
1463
                    if result is not self.uninteresting:
1557
1464
                        return result
1558
1465
            # handle unversioned specified paths:
1559
1466
            if self.want_unversioned and not path_handled and self.root_dir_info:
1571
1478
                      )
1572
1479
            # If we reach here, the outer flow continues, which enters into the
1573
1480
            # per-root setup logic.
1574
 
        if (self.current_dir_info is None and self.current_block is None and not
1575
 
            self.doing_consistency_expansion):
 
1481
        if self.current_dir_info is None and self.current_block is None:
1576
1482
            # setup iteration of this root:
1577
1483
            self.current_dir_list = None
1578
1484
            if self.root_dir_info and self.root_dir_info[2] == 'tree-reference':
1596
1502
                        #            and e.winerror == ERROR_DIRECTORY
1597
1503
                        try:
1598
1504
                            e_winerror = e.winerror
1599
 
                        except AttributeError, _:
 
1505
                        except AttributeError:
1600
1506
                            e_winerror = None
1601
1507
                        win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
1602
1508
                        if (e.errno in win_errors or e_winerror in win_errors):
1685
1591
                    try:
1686
1592
                        self.current_dir_info = self.dir_iterator.next()
1687
1593
                        self.current_dir_list = self.current_dir_info[1]
1688
 
                    except StopIteration, _:
 
1594
                    except StopIteration:
1689
1595
                        self.current_dir_info = None
1690
1596
                else: #(dircmp > 0)
1691
1597
                    # We have a dirblock entry for this location, but there
1700
1606
                        self.current_block_pos = self.current_block_pos + 1
1701
1607
                        # entry referring to file not present on disk.
1702
1608
                        # advance the entry only, after processing.
1703
 
                        result, changed = self._process_entry(current_entry, None)
1704
 
                        if changed is not None:
1705
 
                            if changed:
1706
 
                                self._gather_result_for_consistency(result)
1707
 
                            if changed or self.include_unchanged:
 
1609
                        result = self._process_entry(current_entry, None)
 
1610
                        if result is not None:
 
1611
                            if result is not self.uninteresting:
1708
1612
                                return result
1709
1613
                    self.block_index = self.block_index + 1
1710
1614
                    self._update_current_block()
1716
1620
            # More supplied paths to process
1717
1621
            self.current_root = None
1718
1622
            return self._iter_next()
1719
 
        # Start expanding more conservatively, adding paths the user may not
1720
 
        # have intended but required for consistent deltas.
1721
 
        self.doing_consistency_expansion = 1
1722
 
        if not self._pending_consistent_entries:
1723
 
            self._pending_consistent_entries = self._next_consistent_entries()
1724
 
        while self._pending_consistent_entries:
1725
 
            result, changed = self._pending_consistent_entries.pop()
1726
 
            if changed is not None:
1727
 
                return result
1728
1623
        raise StopIteration()
1729
1624
 
1730
1625
    cdef object _maybe_tree_ref(self, current_path_info):
1780
1675
                    pass
1781
1676
                elif current_path_info is None:
1782
1677
                    # no path is fine: the per entry code will handle it.
1783
 
                    result, changed = self._process_entry(current_entry,
1784
 
                        current_path_info)
 
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
1785
1682
                else:
1786
1683
                    minikind = _minikind_from_string(
1787
1684
                        current_entry[1][self.target_index][0])
1802
1699
                        else:
1803
1700
                            # entry referring to file not present on disk.
1804
1701
                            # advance the entry only, after processing.
1805
 
                            result, changed = self._process_entry(current_entry,
1806
 
                                None)
 
1702
                            result = self._process_entry(current_entry, None)
 
1703
                            if result is not None:
 
1704
                                if result is self.uninteresting:
 
1705
                                    result = None
1807
1706
                            advance_path = 0
1808
1707
                    else:
1809
1708
                        # paths are the same,and the dirstate entry is not
1810
1709
                        # absent or renamed.
1811
 
                        result, changed = self._process_entry(current_entry,
1812
 
                            current_path_info)
1813
 
                        if changed is not None:
 
1710
                        result = self._process_entry(current_entry, current_path_info)
 
1711
                        if result is not None:
1814
1712
                            path_handled = -1
1815
 
                            if not changed and not self.include_unchanged:
1816
 
                                changed = None
 
1713
                            if result is self.uninteresting:
 
1714
                                result = None
1817
1715
                # >- loop control starts here:
1818
1716
                # >- entry
1819
1717
                if advance_entry and current_entry is not None:
1832
1730
                                and stat.S_IEXEC & current_path_info[3].st_mode)
1833
1731
                            try:
1834
1732
                                relpath_unicode = self.utf8_decode(current_path_info[0])[0]
1835
 
                            except UnicodeDecodeError, _:
 
1733
                            except UnicodeDecodeError:
1836
1734
                                raise errors.BadFilenameEncoding(
1837
1735
                                    current_path_info[0], osutils._fs_enc)
1838
 
                            if changed is not None:
 
1736
                            if result is not None:
1839
1737
                                raise AssertionError(
1840
1738
                                    "result is not None: %r" % result)
1841
1739
                            result = (None,
1846
1744
                                (None, self.utf8_decode(current_path_info[1])[0]),
1847
1745
                                (None, current_path_info[2]),
1848
1746
                                (None, new_executable))
1849
 
                            changed = True
1850
1747
                        # dont descend into this unversioned path if it is
1851
1748
                        # a dir
1852
1749
                        if current_path_info[2] in ('directory'):
1865
1762
                                current_path_info)
1866
1763
                    else:
1867
1764
                        current_path_info = None
1868
 
                if changed is not None:
 
1765
                if result is not None:
1869
1766
                    # Found a result on this pass, yield it
1870
 
                    if changed:
1871
 
                        self._gather_result_for_consistency(result)
1872
 
                    if changed or self.include_unchanged:
1873
 
                        return result
 
1767
                    return result
1874
1768
            if self.current_block is not None:
1875
1769
                self.block_index = self.block_index + 1
1876
1770
                self._update_current_block()
1880
1774
                try:
1881
1775
                    self.current_dir_info = self.dir_iterator.next()
1882
1776
                    self.current_dir_list = self.current_dir_info[1]
1883
 
                except StopIteration, _:
 
1777
                except StopIteration:
1884
1778
                    self.current_dir_info = None
1885
 
 
1886
 
    cdef object _next_consistent_entries(self):
1887
 
        """Grabs the next specific file parent case to consider.
1888
 
        
1889
 
        :return: A list of the results, each of which is as for _process_entry.
1890
 
        """
1891
 
        results = []
1892
 
        while self.search_specific_file_parents:
1893
 
            # Process the parent directories for the paths we were iterating.
1894
 
            # Even in extremely large trees this should be modest, so currently
1895
 
            # no attempt is made to optimise.
1896
 
            path_utf8 = self.search_specific_file_parents.pop()
1897
 
            if path_utf8 in self.searched_exact_paths:
1898
 
                # We've examined this path.
1899
 
                continue
1900
 
            if osutils.is_inside_any(self.searched_specific_files, path_utf8):
1901
 
                # We've examined this path.
1902
 
                continue
1903
 
            path_entries = self.state._entries_for_path(path_utf8)
1904
 
            # We need either one or two entries. If the path in
1905
 
            # self.target_index has moved (so the entry in source_index is in
1906
 
            # 'ar') then we need to also look for the entry for this path in
1907
 
            # self.source_index, to output the appropriate delete-or-rename.
1908
 
            selected_entries = []
1909
 
            found_item = False
1910
 
            for candidate_entry in path_entries:
1911
 
                # Find entries present in target at this path:
1912
 
                if candidate_entry[1][self.target_index][0] not in 'ar':
1913
 
                    found_item = True
1914
 
                    selected_entries.append(candidate_entry)
1915
 
                # Find entries present in source at this path:
1916
 
                elif (self.source_index is not None and
1917
 
                    candidate_entry[1][self.source_index][0] not in 'ar'):
1918
 
                    found_item = True
1919
 
                    if candidate_entry[1][self.target_index][0] == 'a':
1920
 
                        # Deleted, emit it here.
1921
 
                        selected_entries.append(candidate_entry)
1922
 
                    else:
1923
 
                        # renamed, emit it when we process the directory it
1924
 
                        # ended up at.
1925
 
                        self.search_specific_file_parents.add(
1926
 
                            candidate_entry[1][self.target_index][1])
1927
 
            if not found_item:
1928
 
                raise AssertionError(
1929
 
                    "Missing entry for specific path parent %r, %r" % (
1930
 
                    path_utf8, path_entries))
1931
 
            path_info = self._path_info(path_utf8, path_utf8.decode('utf8'))
1932
 
            for entry in selected_entries:
1933
 
                if entry[0][2] in self.seen_ids:
1934
 
                    continue
1935
 
                result, changed = self._process_entry(entry, path_info)
1936
 
                if changed is None:
1937
 
                    raise AssertionError(
1938
 
                        "Got entry<->path mismatch for specific path "
1939
 
                        "%r entry %r path_info %r " % (
1940
 
                        path_utf8, entry, path_info))
1941
 
                # Only include changes - we're outside the users requested
1942
 
                # expansion.
1943
 
                if changed:
1944
 
                    self._gather_result_for_consistency(result)
1945
 
                    if (result[6][0] == 'directory' and
1946
 
                        result[6][1] != 'directory'):
1947
 
                        # This stopped being a directory, the old children have
1948
 
                        # to be included.
1949
 
                        if entry[1][self.source_index][0] == 'r':
1950
 
                            # renamed, take the source path
1951
 
                            entry_path_utf8 = entry[1][self.source_index][1]
1952
 
                        else:
1953
 
                            entry_path_utf8 = path_utf8
1954
 
                        initial_key = (entry_path_utf8, '', '')
1955
 
                        block_index, _ = self.state._find_block_index_from_key(
1956
 
                            initial_key)
1957
 
                        if block_index == 0:
1958
 
                            # The children of the root are in block index 1.
1959
 
                            block_index = block_index + 1
1960
 
                        current_block = None
1961
 
                        if block_index < len(self.state._dirblocks):
1962
 
                            current_block = self.state._dirblocks[block_index]
1963
 
                            if not osutils.is_inside(
1964
 
                                entry_path_utf8, current_block[0]):
1965
 
                                # No entries for this directory at all.
1966
 
                                current_block = None
1967
 
                        if current_block is not None:
1968
 
                            for entry in current_block[1]:
1969
 
                                if entry[1][self.source_index][0] in 'ar':
1970
 
                                    # Not in the source tree, so doesn't have to be
1971
 
                                    # included.
1972
 
                                    continue
1973
 
                                # Path of the entry itself.
1974
 
                                self.search_specific_file_parents.add(
1975
 
                                    self.pathjoin(*entry[0][:2]))
1976
 
                if changed or self.include_unchanged:
1977
 
                    results.append((result, changed))
1978
 
            self.searched_exact_paths.add(path_utf8)
1979
 
        return results
1980
 
 
1981
 
    cdef object _path_info(self, utf8_path, unicode_path):
1982
 
        """Generate path_info for unicode_path.
1983
 
 
1984
 
        :return: None if unicode_path does not exist, or a path_info tuple.
1985
 
        """
1986
 
        abspath = self.tree.abspath(unicode_path)
1987
 
        try:
1988
 
            stat = os.lstat(abspath)
1989
 
        except OSError, e:
1990
 
            if e.errno == errno.ENOENT:
1991
 
                # the path does not exist.
1992
 
                return None
1993
 
            else:
1994
 
                raise
1995
 
        utf8_basename = utf8_path.rsplit('/', 1)[-1]
1996
 
        dir_info = (utf8_path, utf8_basename,
1997
 
            osutils.file_kind_from_stat_mode(stat.st_mode), stat,
1998
 
            abspath)
1999
 
        if dir_info[2] == 'directory':
2000
 
            if self.tree._directory_is_tree_reference(
2001
 
                unicode_path):
2002
 
                self.root_dir_info = self.root_dir_info[:2] + \
2003
 
                    ('tree-reference',) + self.root_dir_info[3:]
2004
 
        return dir_info