3074
2774
raise errors.ObjectNotLocked(self)
3077
def py_update_entry(state, entry, abspath, stat_value,
3078
_stat_to_minikind=DirState._stat_to_minikind,
3079
_pack_stat=pack_stat):
3080
"""Update the entry based on what is actually on disk.
3082
This function only calculates the sha if it needs to - if the entry is
3083
uncachable, or clearly different to the first parent's entry, no sha
3084
is calculated, and None is returned.
3086
:param state: The dirstate this entry is in.
3087
:param entry: This is the dirblock entry for the file in question.
3088
:param abspath: The path on disk for this file.
3089
:param stat_value: The stat value done on the path.
3090
:return: None, or The sha1 hexdigest of the file (40 bytes) or link
3091
target of a symlink.
3094
minikind = _stat_to_minikind[stat_value.st_mode & 0170000]
3098
packed_stat = _pack_stat(stat_value)
3099
(saved_minikind, saved_link_or_sha1, saved_file_size,
3100
saved_executable, saved_packed_stat) = entry[1][0]
3102
if minikind == 'd' and saved_minikind == 't':
3104
if (minikind == saved_minikind
3105
and packed_stat == saved_packed_stat):
3106
# The stat hasn't changed since we saved, so we can re-use the
3111
# size should also be in packed_stat
3112
if saved_file_size == stat_value.st_size:
3113
return saved_link_or_sha1
3115
# If we have gotten this far, that means that we need to actually
3116
# process this entry.
3119
executable = state._is_executable(stat_value.st_mode,
3121
if state._cutoff_time is None:
3122
state._sha_cutoff_time()
3123
if (stat_value.st_mtime < state._cutoff_time
3124
and stat_value.st_ctime < state._cutoff_time
3125
and len(entry[1]) > 1
3126
and entry[1][1][0] != 'a'):
3127
# Could check for size changes for further optimised
3128
# avoidance of sha1's. However the most prominent case of
3129
# over-shaing is during initial add, which this catches.
3130
# Besides, if content filtering happens, size and sha
3131
# are calculated at the same time, so checking just the size
3132
# gains nothing w.r.t. performance.
3133
link_or_sha1 = state._sha1_file(abspath)
3134
entry[1][0] = ('f', link_or_sha1, stat_value.st_size,
3135
executable, packed_stat)
3137
entry[1][0] = ('f', '', stat_value.st_size,
3138
executable, DirState.NULLSTAT)
3139
elif minikind == 'd':
3141
entry[1][0] = ('d', '', 0, False, packed_stat)
3142
if saved_minikind != 'd':
3143
# This changed from something into a directory. Make sure we
3144
# have a directory block for it. This doesn't happen very
3145
# often, so this doesn't have to be super fast.
3146
block_index, entry_index, dir_present, file_present = \
3147
state._get_block_entry_index(entry[0][0], entry[0][1], 0)
3148
state._ensure_block(block_index, entry_index,
3149
osutils.pathjoin(entry[0][0], entry[0][1]))
3150
elif minikind == 'l':
3151
link_or_sha1 = state._read_link(abspath, saved_link_or_sha1)
3152
if state._cutoff_time is None:
3153
state._sha_cutoff_time()
3154
if (stat_value.st_mtime < state._cutoff_time
3155
and stat_value.st_ctime < state._cutoff_time):
3156
entry[1][0] = ('l', link_or_sha1, stat_value.st_size,
3159
entry[1][0] = ('l', '', stat_value.st_size,
3160
False, DirState.NULLSTAT)
3161
state._dirblock_state = DirState.IN_MEMORY_MODIFIED
3165
class ProcessEntryPython(object):
3167
__slots__ = ["old_dirname_to_file_id", "new_dirname_to_file_id",
3168
"last_source_parent", "last_target_parent", "include_unchanged",
3169
"use_filesystem_for_exec", "utf8_decode", "searched_specific_files",
3170
"search_specific_files", "state", "source_index", "target_index",
3171
"want_unversioned", "tree"]
3173
def __init__(self, include_unchanged, use_filesystem_for_exec,
3174
search_specific_files, state, source_index, target_index,
3175
want_unversioned, tree):
3176
self.old_dirname_to_file_id = {}
3177
self.new_dirname_to_file_id = {}
3178
# Using a list so that we can access the values and change them in
3179
# nested scope. Each one is [path, file_id, entry]
3180
self.last_source_parent = [None, None]
3181
self.last_target_parent = [None, None]
3182
self.include_unchanged = include_unchanged
3183
self.use_filesystem_for_exec = use_filesystem_for_exec
3184
self.utf8_decode = cache_utf8._utf8_decode
3185
# for all search_indexs in each path at or under each element of
3186
# search_specific_files, if the detail is relocated: add the id, and add the
3187
# relocated path as one to search if its not searched already. If the
3188
# detail is not relocated, add the id.
3189
self.searched_specific_files = set()
3190
self.search_specific_files = search_specific_files
3192
self.source_index = source_index
3193
self.target_index = target_index
3194
self.want_unversioned = want_unversioned
3197
def _process_entry(self, entry, path_info, pathjoin=osutils.pathjoin):
3198
"""Compare an entry and real disk to generate delta information.
3200
:param path_info: top_relpath, basename, kind, lstat, abspath for
3201
the path of entry. If None, then the path is considered absent.
3202
(Perhaps we should pass in a concrete entry for this ?)
3203
Basename is returned as a utf8 string because we expect this
3204
tuple will be ignored, and don't want to take the time to
3206
:return: (iter_changes_result, changed). If the entry has not been
3207
handled then changed is None. Otherwise it is False if no content
3208
or metadata changes have occured, and None if any content or
3209
metadata change has occured. If self.include_unchanged is True then
3210
if changed is not None, iter_changes_result will always be a result
3211
tuple. Otherwise, iter_changes_result is None unless changed is
3214
if self.source_index is None:
3215
source_details = DirState.NULL_PARENT_DETAILS
3217
source_details = entry[1][self.source_index]
3218
target_details = entry[1][self.target_index]
3219
target_minikind = target_details[0]
3220
if path_info is not None and target_minikind in 'fdlt':
3221
if not (self.target_index == 0):
3222
raise AssertionError()
3223
link_or_sha1 = update_entry(self.state, entry,
3224
abspath=path_info[4], stat_value=path_info[3])
3225
# The entry may have been modified by update_entry
3226
target_details = entry[1][self.target_index]
3227
target_minikind = target_details[0]
3230
file_id = entry[0][2]
3231
source_minikind = source_details[0]
3232
if source_minikind in 'fdltr' and target_minikind in 'fdlt':
3233
# claimed content in both: diff
3234
# r | fdlt | | add source to search, add id path move and perform
3235
# | | | diff check on source-target
3236
# r | fdlt | a | dangling file that was present in the basis.
3238
if source_minikind in 'r':
3239
# add the source to the search path to find any children it
3240
# has. TODO ? : only add if it is a container ?
3241
if not osutils.is_inside_any(self.searched_specific_files,
3243
self.search_specific_files.add(source_details[1])
3244
# generate the old path; this is needed for stating later
3246
old_path = source_details[1]
3247
old_dirname, old_basename = os.path.split(old_path)
3248
path = pathjoin(entry[0][0], entry[0][1])
3249
old_entry = self.state._get_entry(self.source_index,
3251
# update the source details variable to be the real
3253
if old_entry == (None, None):
3254
raise errors.CorruptDirstate(self.state._filename,
3255
"entry '%s/%s' is considered renamed from %r"
3256
" but source does not exist\n"
3257
"entry: %s" % (entry[0][0], entry[0][1], old_path, entry))
3258
source_details = old_entry[1][self.source_index]
3259
source_minikind = source_details[0]
3261
old_dirname = entry[0][0]
3262
old_basename = entry[0][1]
3263
old_path = path = None
3264
if path_info is None:
3265
# the file is missing on disk, show as removed.
3266
content_change = True
3270
# source and target are both versioned and disk file is present.
3271
target_kind = path_info[2]
3272
if target_kind == 'directory':
3274
old_path = path = pathjoin(old_dirname, old_basename)
3275
self.new_dirname_to_file_id[path] = file_id
3276
if source_minikind != 'd':
3277
content_change = True
3279
# directories have no fingerprint
3280
content_change = False
3282
elif target_kind == 'file':
3283
if source_minikind != 'f':
3284
content_change = True
3286
# Check the sha. We can't just rely on the size as
3287
# content filtering may mean differ sizes actually
3288
# map to the same content
3289
if link_or_sha1 is None:
3291
statvalue, link_or_sha1 = \
3292
self.state._sha1_provider.stat_and_sha1(
3294
self.state._observed_sha1(entry, link_or_sha1,
3296
content_change = (link_or_sha1 != source_details[1])
3297
# Target details is updated at update_entry time
3298
if self.use_filesystem_for_exec:
3299
# We don't need S_ISREG here, because we are sure
3300
# we are dealing with a file.
3301
target_exec = bool(stat.S_IEXEC & path_info[3].st_mode)
3303
target_exec = target_details[3]
3304
elif target_kind == 'symlink':
3305
if source_minikind != 'l':
3306
content_change = True
3308
content_change = (link_or_sha1 != source_details[1])
3310
elif target_kind == 'tree-reference':
3311
if source_minikind != 't':
3312
content_change = True
3314
content_change = False
3317
raise Exception, "unknown kind %s" % path_info[2]
3318
if source_minikind == 'd':
3320
old_path = path = pathjoin(old_dirname, old_basename)
3321
self.old_dirname_to_file_id[old_path] = file_id
3322
# parent id is the entry for the path in the target tree
3323
if old_basename and old_dirname == self.last_source_parent[0]:
3324
source_parent_id = self.last_source_parent[1]
3327
source_parent_id = self.old_dirname_to_file_id[old_dirname]
3329
source_parent_entry = self.state._get_entry(self.source_index,
3330
path_utf8=old_dirname)
3331
source_parent_id = source_parent_entry[0][2]
3332
if source_parent_id == entry[0][2]:
3333
# This is the root, so the parent is None
3334
source_parent_id = None
3336
self.last_source_parent[0] = old_dirname
3337
self.last_source_parent[1] = source_parent_id
3338
new_dirname = entry[0][0]
3339
if entry[0][1] and new_dirname == self.last_target_parent[0]:
3340
target_parent_id = self.last_target_parent[1]
3343
target_parent_id = self.new_dirname_to_file_id[new_dirname]
3345
# TODO: We don't always need to do the lookup, because the
3346
# parent entry will be the same as the source entry.
3347
target_parent_entry = self.state._get_entry(self.target_index,
3348
path_utf8=new_dirname)
3349
if target_parent_entry == (None, None):
3350
raise AssertionError(
3351
"Could not find target parent in wt: %s\nparent of: %s"
3352
% (new_dirname, entry))
3353
target_parent_id = target_parent_entry[0][2]
3354
if target_parent_id == entry[0][2]:
3355
# This is the root, so the parent is None
3356
target_parent_id = None
3358
self.last_target_parent[0] = new_dirname
3359
self.last_target_parent[1] = target_parent_id
3361
source_exec = source_details[3]
3362
changed = (content_change
3363
or source_parent_id != target_parent_id
3364
or old_basename != entry[0][1]
3365
or source_exec != target_exec
3367
if not changed and not self.include_unchanged:
3370
if old_path is None:
3371
old_path = path = pathjoin(old_dirname, old_basename)
3372
old_path_u = self.utf8_decode(old_path)[0]
3375
old_path_u = self.utf8_decode(old_path)[0]
3376
if old_path == path:
3379
path_u = self.utf8_decode(path)[0]
3380
source_kind = DirState._minikind_to_kind[source_minikind]
3381
return (entry[0][2],
3382
(old_path_u, path_u),
3385
(source_parent_id, target_parent_id),
3386
(self.utf8_decode(old_basename)[0], self.utf8_decode(entry[0][1])[0]),
3387
(source_kind, target_kind),
3388
(source_exec, target_exec)), changed
3389
elif source_minikind in 'a' and target_minikind in 'fdlt':
3390
# looks like a new file
3391
path = pathjoin(entry[0][0], entry[0][1])
3392
# parent id is the entry for the path in the target tree
3393
# TODO: these are the same for an entire directory: cache em.
3394
parent_id = self.state._get_entry(self.target_index,
3395
path_utf8=entry[0][0])[0][2]
3396
if parent_id == entry[0][2]:
3398
if path_info is not None:
3400
if self.use_filesystem_for_exec:
3401
# We need S_ISREG here, because we aren't sure if this
3404
stat.S_ISREG(path_info[3].st_mode)
3405
and stat.S_IEXEC & path_info[3].st_mode)
3407
target_exec = target_details[3]
3408
return (entry[0][2],
3409
(None, self.utf8_decode(path)[0]),
3413
(None, self.utf8_decode(entry[0][1])[0]),
3414
(None, path_info[2]),
3415
(None, target_exec)), True
3417
# Its a missing file, report it as such.
3418
return (entry[0][2],
3419
(None, self.utf8_decode(path)[0]),
3423
(None, self.utf8_decode(entry[0][1])[0]),
3425
(None, False)), True
3426
elif source_minikind in 'fdlt' and target_minikind in 'a':
3427
# unversioned, possibly, or possibly not deleted: we dont care.
3428
# if its still on disk, *and* theres no other entry at this
3429
# path [we dont know this in this routine at the moment -
3430
# perhaps we should change this - then it would be an unknown.
3431
old_path = pathjoin(entry[0][0], entry[0][1])
3432
# parent id is the entry for the path in the target tree
3433
parent_id = self.state._get_entry(self.source_index, path_utf8=entry[0][0])[0][2]
3434
if parent_id == entry[0][2]:
3436
return (entry[0][2],
3437
(self.utf8_decode(old_path)[0], None),
3441
(self.utf8_decode(entry[0][1])[0], None),
3442
(DirState._minikind_to_kind[source_minikind], None),
3443
(source_details[3], None)), True
3444
elif source_minikind in 'fdlt' and target_minikind in 'r':
3445
# a rename; could be a true rename, or a rename inherited from
3446
# a renamed parent. TODO: handle this efficiently. Its not
3447
# common case to rename dirs though, so a correct but slow
3448
# implementation will do.
3449
if not osutils.is_inside_any(self.searched_specific_files, target_details[1]):
3450
self.search_specific_files.add(target_details[1])
3451
elif source_minikind in 'ra' and target_minikind in 'ra':
3452
# neither of the selected trees contain this file,
3453
# so skip over it. This is not currently directly tested, but
3454
# is indirectly via test_too_much.TestCommands.test_conflicts.
3457
raise AssertionError("don't know how to compare "
3458
"source_minikind=%r, target_minikind=%r"
3459
% (source_minikind, target_minikind))
3460
## import pdb;pdb.set_trace()
3466
def iter_changes(self):
3467
"""Iterate over the changes."""
3468
utf8_decode = cache_utf8._utf8_decode
3469
_cmp_by_dirs = cmp_by_dirs
3470
_process_entry = self._process_entry
3471
search_specific_files = self.search_specific_files
3472
searched_specific_files = self.searched_specific_files
3473
splitpath = osutils.splitpath
3475
# compare source_index and target_index at or under each element of search_specific_files.
3476
# follow the following comparison table. Note that we only want to do diff operations when
3477
# the target is fdl because thats when the walkdirs logic will have exposed the pathinfo
3481
# Source | Target | disk | action
3482
# r | fdlt | | add source to search, add id path move and perform
3483
# | | | diff check on source-target
3484
# r | fdlt | a | dangling file that was present in the basis.
3486
# r | a | | add source to search
3488
# r | r | | this path is present in a non-examined tree, skip.
3489
# r | r | a | this path is present in a non-examined tree, skip.
3490
# a | fdlt | | add new id
3491
# a | fdlt | a | dangling locally added file, skip
3492
# a | a | | not present in either tree, skip
3493
# a | a | a | not present in any tree, skip
3494
# a | r | | not present in either tree at this path, skip as it
3495
# | | | may not be selected by the users list of paths.
3496
# a | r | a | not present in either tree at this path, skip as it
3497
# | | | may not be selected by the users list of paths.
3498
# fdlt | fdlt | | content in both: diff them
3499
# fdlt | fdlt | a | deleted locally, but not unversioned - show as deleted ?
3500
# fdlt | a | | unversioned: output deleted id for now
3501
# fdlt | a | a | unversioned and deleted: output deleted id
3502
# fdlt | r | | relocated in this tree, so add target to search.
3503
# | | | Dont diff, we will see an r,fd; pair when we reach
3504
# | | | this id at the other path.
3505
# fdlt | r | a | relocated in this tree, so add target to search.
3506
# | | | Dont diff, we will see an r,fd; pair when we reach
3507
# | | | this id at the other path.
3509
# TODO: jam 20070516 - Avoid the _get_entry lookup overhead by
3510
# keeping a cache of directories that we have seen.
3512
while search_specific_files:
3513
# TODO: the pending list should be lexically sorted? the
3514
# interface doesn't require it.
3515
current_root = search_specific_files.pop()
3516
current_root_unicode = current_root.decode('utf8')
3517
searched_specific_files.add(current_root)
3518
# process the entries for this containing directory: the rest will be
3519
# found by their parents recursively.
3520
root_entries = self.state._entries_for_path(current_root)
3521
root_abspath = self.tree.abspath(current_root_unicode)
3523
root_stat = os.lstat(root_abspath)
3525
if e.errno == errno.ENOENT:
3526
# the path does not exist: let _process_entry know that.
3527
root_dir_info = None
3529
# some other random error: hand it up.
3532
root_dir_info = ('', current_root,
3533
osutils.file_kind_from_stat_mode(root_stat.st_mode), root_stat,
3535
if root_dir_info[2] == 'directory':
3536
if self.tree._directory_is_tree_reference(
3537
current_root.decode('utf8')):
3538
root_dir_info = root_dir_info[:2] + \
3539
('tree-reference',) + root_dir_info[3:]
3541
if not root_entries and not root_dir_info:
3542
# this specified path is not present at all, skip it.
3544
path_handled = False
3545
for entry in root_entries:
3546
result, changed = _process_entry(entry, root_dir_info)
3547
if changed is not None:
3549
if changed or self.include_unchanged:
3551
if self.want_unversioned and not path_handled and root_dir_info:
3552
new_executable = bool(
3553
stat.S_ISREG(root_dir_info[3].st_mode)
3554
and stat.S_IEXEC & root_dir_info[3].st_mode)
3556
(None, current_root_unicode),
3560
(None, splitpath(current_root_unicode)[-1]),
3561
(None, root_dir_info[2]),
3562
(None, new_executable)
3564
initial_key = (current_root, '', '')
3565
block_index, _ = self.state._find_block_index_from_key(initial_key)
3566
if block_index == 0:
3567
# we have processed the total root already, but because the
3568
# initial key matched it we should skip it here.
3570
if root_dir_info and root_dir_info[2] == 'tree-reference':
3571
current_dir_info = None
3573
dir_iterator = osutils._walkdirs_utf8(root_abspath, prefix=current_root)
3575
current_dir_info = dir_iterator.next()
3577
# on win32, python2.4 has e.errno == ERROR_DIRECTORY, but
3578
# python 2.5 has e.errno == EINVAL,
3579
# and e.winerror == ERROR_DIRECTORY
3580
e_winerror = getattr(e, 'winerror', None)
3581
win_errors = (ERROR_DIRECTORY, ERROR_PATH_NOT_FOUND)
3582
# there may be directories in the inventory even though
3583
# this path is not a file on disk: so mark it as end of
3585
if e.errno in (errno.ENOENT, errno.ENOTDIR, errno.EINVAL):
3586
current_dir_info = None
3587
elif (sys.platform == 'win32'
3588
and (e.errno in win_errors
3589
or e_winerror in win_errors)):
3590
current_dir_info = None
3594
if current_dir_info[0][0] == '':
3595
# remove .bzr from iteration
3596
bzr_index = bisect.bisect_left(current_dir_info[1], ('.bzr',))
3597
if current_dir_info[1][bzr_index][0] != '.bzr':
3598
raise AssertionError()
3599
del current_dir_info[1][bzr_index]
3600
# walk until both the directory listing and the versioned metadata
3602
if (block_index < len(self.state._dirblocks) and
3603
osutils.is_inside(current_root, self.state._dirblocks[block_index][0])):
3604
current_block = self.state._dirblocks[block_index]
3606
current_block = None
3607
while (current_dir_info is not None or
3608
current_block is not None):
3609
if (current_dir_info and current_block
3610
and current_dir_info[0][0] != current_block[0]):
3611
if _cmp_by_dirs(current_dir_info[0][0], current_block[0]) < 0:
3612
# filesystem data refers to paths not covered by the dirblock.
3613
# this has two possibilities:
3614
# A) it is versioned but empty, so there is no block for it
3615
# B) it is not versioned.
3617
# if (A) then we need to recurse into it to check for
3618
# new unknown files or directories.
3619
# if (B) then we should ignore it, because we don't
3620
# recurse into unknown directories.
3622
while path_index < len(current_dir_info[1]):
3623
current_path_info = current_dir_info[1][path_index]
3624
if self.want_unversioned:
3625
if current_path_info[2] == 'directory':
3626
if self.tree._directory_is_tree_reference(
3627
current_path_info[0].decode('utf8')):
3628
current_path_info = current_path_info[:2] + \
3629
('tree-reference',) + current_path_info[3:]
3630
new_executable = bool(
3631
stat.S_ISREG(current_path_info[3].st_mode)
3632
and stat.S_IEXEC & current_path_info[3].st_mode)
3634
(None, utf8_decode(current_path_info[0])[0]),
3638
(None, utf8_decode(current_path_info[1])[0]),
3639
(None, current_path_info[2]),
3640
(None, new_executable))
3641
# dont descend into this unversioned path if it is
3643
if current_path_info[2] in ('directory',
3645
del current_dir_info[1][path_index]
3649
# This dir info has been handled, go to the next
3651
current_dir_info = dir_iterator.next()
3652
except StopIteration:
3653
current_dir_info = None
3655
# We have a dirblock entry for this location, but there
3656
# is no filesystem path for this. This is most likely
3657
# because a directory was removed from the disk.
3658
# We don't have to report the missing directory,
3659
# because that should have already been handled, but we
3660
# need to handle all of the files that are contained
3662
for current_entry in current_block[1]:
3663
# entry referring to file not present on disk.
3664
# advance the entry only, after processing.
3665
result, changed = _process_entry(current_entry, None)
3666
if changed is not None:
3667
if changed or self.include_unchanged:
3670
if (block_index < len(self.state._dirblocks) and
3671
osutils.is_inside(current_root,
3672
self.state._dirblocks[block_index][0])):
3673
current_block = self.state._dirblocks[block_index]
3675
current_block = None
3678
if current_block and entry_index < len(current_block[1]):
3679
current_entry = current_block[1][entry_index]
3681
current_entry = None
3682
advance_entry = True
3684
if current_dir_info and path_index < len(current_dir_info[1]):
3685
current_path_info = current_dir_info[1][path_index]
3686
if current_path_info[2] == 'directory':
3687
if self.tree._directory_is_tree_reference(
3688
current_path_info[0].decode('utf8')):
3689
current_path_info = current_path_info[:2] + \
3690
('tree-reference',) + current_path_info[3:]
3692
current_path_info = None
3694
path_handled = False
3695
while (current_entry is not None or
3696
current_path_info is not None):
3697
if current_entry is None:
3698
# the check for path_handled when the path is advanced
3699
# will yield this path if needed.
3701
elif current_path_info is None:
3702
# no path is fine: the per entry code will handle it.
3703
result, changed = _process_entry(current_entry, current_path_info)
3704
if changed is not None:
3705
if changed or self.include_unchanged:
3707
elif (current_entry[0][1] != current_path_info[1]
3708
or current_entry[1][self.target_index][0] in 'ar'):
3709
# The current path on disk doesn't match the dirblock
3710
# record. Either the dirblock is marked as absent, or
3711
# the file on disk is not present at all in the
3712
# dirblock. Either way, report about the dirblock
3713
# entry, and let other code handle the filesystem one.
3715
# Compare the basename for these files to determine
3717
if current_path_info[1] < current_entry[0][1]:
3718
# extra file on disk: pass for now, but only
3719
# increment the path, not the entry
3720
advance_entry = False
3722
# entry referring to file not present on disk.
3723
# advance the entry only, after processing.
3724
result, changed = _process_entry(current_entry, None)
3725
if changed is not None:
3726
if changed or self.include_unchanged:
3728
advance_path = False
3730
result, changed = _process_entry(current_entry, current_path_info)
3731
if changed is not None:
3733
if changed or self.include_unchanged:
3735
if advance_entry and current_entry is not None:
3737
if entry_index < len(current_block[1]):
3738
current_entry = current_block[1][entry_index]
3740
current_entry = None
3742
advance_entry = True # reset the advance flaga
3743
if advance_path and current_path_info is not None:
3744
if not path_handled:
3745
# unversioned in all regards
3746
if self.want_unversioned:
3747
new_executable = bool(
3748
stat.S_ISREG(current_path_info[3].st_mode)
3749
and stat.S_IEXEC & current_path_info[3].st_mode)
3751
relpath_unicode = utf8_decode(current_path_info[0])[0]
3752
except UnicodeDecodeError:
3753
raise errors.BadFilenameEncoding(
3754
current_path_info[0], osutils._fs_enc)
3756
(None, relpath_unicode),
3760
(None, utf8_decode(current_path_info[1])[0]),
3761
(None, current_path_info[2]),
3762
(None, new_executable))
3763
# dont descend into this unversioned path if it is
3765
if current_path_info[2] in ('directory'):
3766
del current_dir_info[1][path_index]
3768
# dont descend the disk iterator into any tree
3770
if current_path_info[2] == 'tree-reference':
3771
del current_dir_info[1][path_index]
3774
if path_index < len(current_dir_info[1]):
3775
current_path_info = current_dir_info[1][path_index]
3776
if current_path_info[2] == 'directory':
3777
if self.tree._directory_is_tree_reference(
3778
current_path_info[0].decode('utf8')):
3779
current_path_info = current_path_info[:2] + \
3780
('tree-reference',) + current_path_info[3:]
3782
current_path_info = None
3783
path_handled = False
3785
advance_path = True # reset the advance flagg.
3786
if current_block is not None:
3788
if (block_index < len(self.state._dirblocks) and
3789
osutils.is_inside(current_root, self.state._dirblocks[block_index][0])):
3790
current_block = self.state._dirblocks[block_index]
3792
current_block = None
3793
if current_dir_info is not None:
3795
current_dir_info = dir_iterator.next()
3796
except StopIteration:
3797
current_dir_info = None
3800
2777
# Try to load the compiled form if possible
3802
from bzrlib._dirstate_helpers_pyx import (
3808
ProcessEntryC as _process_entry,
3809
update_entry as update_entry,
2779
from bzrlib._dirstate_helpers_c import (
2780
_read_dirblocks_c as _read_dirblocks,
2781
bisect_dirblock_c as bisect_dirblock,
2782
_bisect_path_left_c as _bisect_path_left,
2783
_bisect_path_right_c as _bisect_path_right,
2784
cmp_by_dirs_c as cmp_by_dirs,
3811
2786
except ImportError:
3812
2787
from bzrlib._dirstate_helpers_py import (
2788
_read_dirblocks_py as _read_dirblocks,
2789
bisect_dirblock_py as bisect_dirblock,
2790
_bisect_path_left_py as _bisect_path_left,
2791
_bisect_path_right_py as _bisect_path_right,
2792
cmp_by_dirs_py as cmp_by_dirs,
3819
# FIXME: It would be nice to be able to track moved lines so that the
3820
# corresponding python code can be moved to the _dirstate_helpers_py
3821
# module. I don't want to break the history for this important piece of
3822
# code so I left the code here -- vila 20090622
3823
update_entry = py_update_entry
3824
_process_entry = ProcessEntryPython