1339
1339
minikind = child[1][0][0]
1340
1340
fingerprint = child[1][0][4]
1341
1341
executable = child[1][0][3]
1342
old_child_path = osutils.pathjoin(child_dirname,
1342
old_child_path = osutils.pathjoin(child[0][0],
1344
1344
removals[child[0][2]] = old_child_path
1345
1345
child_suffix = child_dirname[len(old_path):]
1346
1346
new_child_dirname = (new_path + child_suffix)
1347
1347
key = (new_child_dirname, child_basename, child[0][2])
1348
new_child_path = osutils.pathjoin(new_child_dirname,
1348
new_child_path = os.path.join(new_child_dirname,
1350
1350
insertions[child[0][2]] = (key, minikind, executable,
1351
1351
fingerprint, new_child_path)
1352
1352
self._check_delta_ids_absent(new_ids, delta, 0)
3172
3165
class ProcessEntryPython(object):
3174
__slots__ = ["old_dirname_to_file_id", "new_dirname_to_file_id",
3167
__slots__ = ["old_dirname_to_file_id", "new_dirname_to_file_id", "uninteresting",
3175
3168
"last_source_parent", "last_target_parent", "include_unchanged",
3176
"partial", "use_filesystem_for_exec", "utf8_decode",
3177
"searched_specific_files", "search_specific_files",
3178
"searched_exact_paths", "search_specific_file_parents", "seen_ids",
3179
"state", "source_index", "target_index", "want_unversioned", "tree"]
3169
"use_filesystem_for_exec", "utf8_decode", "searched_specific_files",
3170
"search_specific_files", "state", "source_index", "target_index",
3171
"want_unversioned", "tree"]
3181
3173
def __init__(self, include_unchanged, use_filesystem_for_exec,
3182
3174
search_specific_files, state, source_index, target_index,
3183
3175
want_unversioned, tree):
3184
3176
self.old_dirname_to_file_id = {}
3185
3177
self.new_dirname_to_file_id = {}
3186
# Are we doing a partial iter_changes?
3187
self.partial = search_specific_files != set([''])
3178
# Just a sentry, so that _process_entry can say that this
3179
# record is handled, but isn't interesting to process (unchanged)
3180
self.uninteresting = object()
3188
3181
# Using a list so that we can access the values and change them in
3189
3182
# nested scope. Each one is [path, file_id, entry]
3190
3183
self.last_source_parent = [None, None]
3193
3186
self.use_filesystem_for_exec = use_filesystem_for_exec
3194
3187
self.utf8_decode = cache_utf8._utf8_decode
3195
3188
# for all search_indexs in each path at or under each element of
3196
# search_specific_files, if the detail is relocated: add the id, and
3197
# add the relocated path as one to search if its not searched already.
3198
# If the detail is not relocated, add the id.
3189
# search_specific_files, if the detail is relocated: add the id, and add the
3190
# relocated path as one to search if its not searched already. If the
3191
# detail is not relocated, add the id.
3199
3192
self.searched_specific_files = set()
3200
# When we search exact paths without expanding downwards, we record
3202
self.searched_exact_paths = set()
3203
3193
self.search_specific_files = search_specific_files
3204
# The parents up to the root of the paths we are searching.
3205
# After all normal paths are returned, these specific items are returned.
3206
self.search_specific_file_parents = set()
3207
# The ids we've sent out in the delta.
3208
self.seen_ids = set()
3209
3194
self.state = state
3210
3195
self.source_index = source_index
3211
3196
self.target_index = target_index
3212
if target_index != 0:
3213
# A lot of code in here depends on target_index == 0
3214
raise errors.BzrError('unsupported target index')
3215
3197
self.want_unversioned = want_unversioned
3216
3198
self.tree = tree
3219
3201
"""Compare an entry and real disk to generate delta information.
3221
3203
:param path_info: top_relpath, basename, kind, lstat, abspath for
3222
the path of entry. If None, then the path is considered absent in
3223
the target (Perhaps we should pass in a concrete entry for this ?)
3204
the path of entry. If None, then the path is considered absent.
3205
(Perhaps we should pass in a concrete entry for this ?)
3224
3206
Basename is returned as a utf8 string because we expect this
3225
3207
tuple will be ignored, and don't want to take the time to
3227
:return: (iter_changes_result, changed). If the entry has not been
3228
handled then changed is None. Otherwise it is False if no content
3229
or metadata changes have occurred, and True if any content or
3230
metadata change has occurred. If self.include_unchanged is True then
3231
if changed is not None, iter_changes_result will always be a result
3232
tuple. Otherwise, iter_changes_result is None unless changed is
3209
:return: None if these don't match
3210
A tuple of information about the change, or
3211
the object 'uninteresting' if these match, but are
3212
basically identical.
3235
3214
if self.source_index is None:
3236
3215
source_details = DirState.NULL_PARENT_DETAILS
3481
3458
"source_minikind=%r, target_minikind=%r"
3482
3459
% (source_minikind, target_minikind))
3483
3460
## import pdb;pdb.set_trace()
3486
3463
def __iter__(self):
3489
def _gather_result_for_consistency(self, result):
3490
"""Check a result we will yield to make sure we are consistent later.
3492
This gathers result's parents into a set to output later.
3494
:param result: A result tuple.
3496
if not self.partial or not result[0]:
3498
self.seen_ids.add(result[0])
3499
new_path = result[1][1]
3501
# Not the root and not a delete: queue up the parents of the path.
3502
self.search_specific_file_parents.update(
3503
osutils.parent_directories(new_path.encode('utf8')))
3504
# Add the root directory which parent_directories does not
3506
self.search_specific_file_parents.add('')
3508
3466
def iter_changes(self):
3509
3467
"""Iterate over the changes."""
3510
3468
utf8_decode = cache_utf8._utf8_decode
3511
3469
_cmp_by_dirs = cmp_by_dirs
3512
3470
_process_entry = self._process_entry
3471
uninteresting = self.uninteresting
3513
3472
search_specific_files = self.search_specific_files
3514
3473
searched_specific_files = self.searched_specific_files
3515
3474
splitpath = osutils.splitpath
3770
3723
# entry referring to file not present on disk.
3771
3724
# advance the entry only, after processing.
3772
result, changed = _process_entry(current_entry, None)
3773
if changed is not None:
3775
self._gather_result_for_consistency(result)
3776
if changed or self.include_unchanged:
3725
result = _process_entry(current_entry, None)
3726
if result is not None:
3727
if result is not uninteresting:
3778
3729
advance_path = False
3780
result, changed = _process_entry(current_entry, current_path_info)
3781
if changed is not None:
3731
result = _process_entry(current_entry, current_path_info)
3732
if result is not None:
3782
3733
path_handled = True
3784
self._gather_result_for_consistency(result)
3785
if changed or self.include_unchanged:
3734
if result is not uninteresting:
3787
3736
if advance_entry and current_entry is not None:
3788
3737
entry_index += 1
3847
3796
current_dir_info = dir_iterator.next()
3848
3797
except StopIteration:
3849
3798
current_dir_info = None
3850
for result in self._iter_specific_file_parents():
3853
def _iter_specific_file_parents(self):
3854
"""Iter over the specific file parents."""
3855
while self.search_specific_file_parents:
3856
# Process the parent directories for the paths we were iterating.
3857
# Even in extremely large trees this should be modest, so currently
3858
# no attempt is made to optimise.
3859
path_utf8 = self.search_specific_file_parents.pop()
3860
if osutils.is_inside_any(self.searched_specific_files, path_utf8):
3861
# We've examined this path.
3863
if path_utf8 in self.searched_exact_paths:
3864
# We've examined this path.
3866
path_entries = self.state._entries_for_path(path_utf8)
3867
# We need either one or two entries. If the path in
3868
# self.target_index has moved (so the entry in source_index is in
3869
# 'ar') then we need to also look for the entry for this path in
3870
# self.source_index, to output the appropriate delete-or-rename.
3871
selected_entries = []
3873
for candidate_entry in path_entries:
3874
# Find entries present in target at this path:
3875
if candidate_entry[1][self.target_index][0] not in 'ar':
3877
selected_entries.append(candidate_entry)
3878
# Find entries present in source at this path:
3879
elif (self.source_index is not None and
3880
candidate_entry[1][self.source_index][0] not in 'ar'):
3882
if candidate_entry[1][self.target_index][0] == 'a':
3883
# Deleted, emit it here.
3884
selected_entries.append(candidate_entry)
3886
# renamed, emit it when we process the directory it
3888
self.search_specific_file_parents.add(
3889
candidate_entry[1][self.target_index][1])
3891
raise AssertionError(
3892
"Missing entry for specific path parent %r, %r" % (
3893
path_utf8, path_entries))
3894
path_info = self._path_info(path_utf8, path_utf8.decode('utf8'))
3895
for entry in selected_entries:
3896
if entry[0][2] in self.seen_ids:
3898
result, changed = self._process_entry(entry, path_info)
3900
raise AssertionError(
3901
"Got entry<->path mismatch for specific path "
3902
"%r entry %r path_info %r " % (
3903
path_utf8, entry, path_info))
3904
# Only include changes - we're outside the users requested
3907
self._gather_result_for_consistency(result)
3908
if (result[6][0] == 'directory' and
3909
result[6][1] != 'directory'):
3910
# This stopped being a directory, the old children have
3912
if entry[1][self.source_index][0] == 'r':
3913
# renamed, take the source path
3914
entry_path_utf8 = entry[1][self.source_index][1]
3916
entry_path_utf8 = path_utf8
3917
initial_key = (entry_path_utf8, '', '')
3918
block_index, _ = self.state._find_block_index_from_key(
3920
if block_index == 0:
3921
# The children of the root are in block index 1.
3923
current_block = None
3924
if block_index < len(self.state._dirblocks):
3925
current_block = self.state._dirblocks[block_index]
3926
if not osutils.is_inside(
3927
entry_path_utf8, current_block[0]):
3928
# No entries for this directory at all.
3929
current_block = None
3930
if current_block is not None:
3931
for entry in current_block[1]:
3932
if entry[1][self.source_index][0] in 'ar':
3933
# Not in the source tree, so doesn't have to be
3936
# Path of the entry itself.
3938
self.search_specific_file_parents.add(
3939
osutils.pathjoin(*entry[0][:2]))
3940
if changed or self.include_unchanged:
3942
self.searched_exact_paths.add(path_utf8)
3944
def _path_info(self, utf8_path, unicode_path):
3945
"""Generate path_info for unicode_path.
3947
:return: None if unicode_path does not exist, or a path_info tuple.
3949
abspath = self.tree.abspath(unicode_path)
3951
stat = os.lstat(abspath)
3953
if e.errno == errno.ENOENT:
3954
# the path does not exist.
3958
utf8_basename = utf8_path.rsplit('/', 1)[-1]
3959
dir_info = (utf8_path, utf8_basename,
3960
osutils.file_kind_from_stat_mode(stat.st_mode), stat,
3962
if dir_info[2] == 'directory':
3963
if self.tree._directory_is_tree_reference(
3965
self.root_dir_info = self.root_dir_info[:2] + \
3966
('tree-reference',) + self.root_dir_info[3:]
3970
3801
# Try to load the compiled form if possible