295
295
:param file_id: The file_id of the file.
296
296
:param path: The path of the file.
298
297
If both file_id and path are supplied, an implementation may use
301
300
return osutils.split_lines(self.get_file_text(file_id, path))
303
def get_file_verifier(self, file_id, path=None, stat_value=None):
304
"""Return a verifier for a file.
306
The default implementation returns a sha1.
308
:param file_id: The handle for this file.
309
:param path: The path that this file can be found at.
310
These must point to the same object.
311
:param stat_value: Optional stat value for the object
312
:return: Tuple with verifier name and verifier data
314
return ("SHA1", self.get_file_sha1(file_id, path=path,
315
stat_value=stat_value))
317
def get_file_sha1(self, file_id, path=None, stat_value=None):
318
"""Return the SHA1 file for a file.
320
:note: callers should use get_file_verifier instead
321
where possible, as the underlying repository implementation may
322
have quicker access to a non-sha1 verifier.
324
:param file_id: The handle for this file.
325
:param path: The path that this file can be found at.
326
These must point to the same object.
327
:param stat_value: Optional stat value for the object
329
raise NotImplementedError(self.get_file_sha1)
331
302
def get_file_mtime(self, file_id, path=None):
332
303
"""Return the modification time for a file.
384
346
cur_file = (self.get_file_text(file_id),)
385
347
yield identifier, cur_file
387
def get_symlink_target(self, file_id, path=None):
349
def get_symlink_target(self, file_id):
388
350
"""Get the target for a given file_id.
390
352
It is assumed that the caller already knows that file_id is referencing
392
354
:param file_id: Handle for the symlink entry.
393
:param path: The path of the file.
394
If both file_id and path are supplied, an implementation may use
396
355
:return: The path the symlink points to.
398
357
raise NotImplementedError(self.get_symlink_target)
359
def get_canonical_inventory_paths(self, paths):
360
"""Like get_canonical_inventory_path() but works on multiple items.
362
:param paths: A sequence of paths relative to the root of the tree.
363
:return: A list of paths, with each item the corresponding input path
364
adjusted to account for existing elements that match case
367
return list(self._yield_canonical_inventory_paths(paths))
369
def get_canonical_inventory_path(self, path):
370
"""Returns the first inventory item that case-insensitively matches path.
372
If a path matches exactly, it is returned. If no path matches exactly
373
but more than one path matches case-insensitively, it is implementation
374
defined which is returned.
376
If no path matches case-insensitively, the input path is returned, but
377
with as many path entries that do exist changed to their canonical
380
If you need to resolve many names from the same tree, you should
381
use get_canonical_inventory_paths() to avoid O(N) behaviour.
383
:param path: A paths relative to the root of the tree.
384
:return: The input path adjusted to account for existing elements
385
that match case insensitively.
387
return self._yield_canonical_inventory_paths([path]).next()
389
def _yield_canonical_inventory_paths(self, paths):
391
# First, if the path as specified exists exactly, just use it.
392
if self.path2id(path) is not None:
396
cur_id = self.get_root_id()
398
bit_iter = iter(path.split("/"))
401
for child in self.iter_children(cur_id):
403
child_base = os.path.basename(self.id2path(child))
404
if child_base.lower() == lelt:
406
cur_path = osutils.pathjoin(cur_path, child_base)
409
# before a change is committed we can see this error...
412
# got to the end of this directory and no entries matched.
413
# Return what matched so far, plus the rest as specified.
414
cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
400
419
def get_root_id(self):
401
420
"""Return the file_id for the root of this tree."""
402
421
raise NotImplementedError(self.get_root_id)
460
479
except errors.NoSuchRevisionInTree:
461
480
yield self.repository.revision_tree(revision_id)
483
def _file_revision(revision_tree, file_id):
484
"""Determine the revision associated with a file in a given tree."""
485
revision_tree.lock_read()
487
return revision_tree.inventory[file_id].revision
489
revision_tree.unlock()
463
491
def _get_file_revision(self, file_id, vf, tree_revision):
464
492
"""Ensure that file_id, tree_revision is in vf to plan the merge."""
466
494
if getattr(self, '_repository', None) is None:
467
495
last_revision = tree_revision
468
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
496
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
469
497
self._iter_parent_trees()]
470
498
vf.add_lines((file_id, last_revision), parent_keys,
471
self.get_file_lines(file_id))
499
self.get_file(file_id).readlines())
472
500
repo = self.branch.repository
473
501
base_vf = repo.texts
475
last_revision = self.get_file_revision(file_id)
503
last_revision = self._file_revision(self, file_id)
476
504
base_vf = self._repository.texts
477
505
if base_vf not in vf.fallback_versionedfiles:
478
506
vf.fallback_versionedfiles.append(base_vf)
479
507
return last_revision
509
inventory = property(_get_inventory,
510
doc="Inventory of this Tree")
481
512
def _check_retrieved(self, ie, f):
482
513
if not __debug__:
484
fp = osutils.fingerprint_file(f)
515
fp = fingerprint_file(f)
487
518
if ie.text_size is not None:
488
519
if ie.text_size != fp['size']:
489
raise errors.BzrError(
490
"mismatched size for file %r in %r" %
491
(ie.file_id, self._store),
520
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
492
521
["inventory expects %d bytes" % ie.text_size,
493
522
"file is actually %d bytes" % fp['size'],
494
523
"store is probably damaged/corrupt"])
496
525
if ie.text_sha1 != fp['sha1']:
497
raise errors.BzrError("wrong SHA-1 for file %r in %r" %
498
(ie.file_id, self._store),
526
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
499
527
["inventory expects %s" % ie.text_sha1,
500
528
"file is actually %s" % fp['sha1'],
501
529
"store is probably damaged/corrupt"])
503
532
def path2id(self, path):
504
533
"""Return the id for path in this tree."""
505
raise NotImplementedError(self.path2id)
534
return self._inventory.path2id(path)
507
536
def paths2ids(self, paths, trees=[], require_versioned=True):
508
537
"""Return all the ids that can be reached by walking from paths.
662
691
for path in path_names:
663
692
yield searcher.get_items(path)
665
695
def _get_rules_searcher(self, default_searcher):
666
696
"""Get the RulesSearcher for this tree given the default one."""
667
697
searcher = default_searcher
671
class InventoryTree(Tree):
672
"""A tree that relies on an inventory for its metadata.
674
Trees contain an `Inventory` object, and also know how to retrieve
675
file texts mentioned in the inventory, either from a working
676
directory or from a store.
678
It is possible for trees to contain files that are not described
679
in their inventory or vice versa; for this use `filenames()`.
681
Subclasses should set the _inventory attribute, which is considered
682
private to external API users.
701
######################################################################
704
# TODO: Merge these two functions into a single one that can operate
705
# on either a whole tree or a set of files.
707
# TODO: Return the diff in order by filename, not by category or in
708
# random order. Can probably be done by lock-stepping through the
709
# filenames from both trees.
712
def file_status(filename, old_tree, new_tree):
713
"""Return single-letter status, old and new names for a file.
715
The complexity here is in deciding how to represent renames;
716
many complex cases are possible.
685
def get_canonical_inventory_paths(self, paths):
686
"""Like get_canonical_inventory_path() but works on multiple items.
688
:param paths: A sequence of paths relative to the root of the tree.
689
:return: A list of paths, with each item the corresponding input path
690
adjusted to account for existing elements that match case
693
return list(self._yield_canonical_inventory_paths(paths))
695
def get_canonical_inventory_path(self, path):
696
"""Returns the first inventory item that case-insensitively matches path.
698
If a path matches exactly, it is returned. If no path matches exactly
699
but more than one path matches case-insensitively, it is implementation
700
defined which is returned.
702
If no path matches case-insensitively, the input path is returned, but
703
with as many path entries that do exist changed to their canonical
706
If you need to resolve many names from the same tree, you should
707
use get_canonical_inventory_paths() to avoid O(N) behaviour.
709
:param path: A paths relative to the root of the tree.
710
:return: The input path adjusted to account for existing elements
711
that match case insensitively.
713
return self._yield_canonical_inventory_paths([path]).next()
715
def _yield_canonical_inventory_paths(self, paths):
717
# First, if the path as specified exists exactly, just use it.
718
if self.path2id(path) is not None:
722
cur_id = self.get_root_id()
724
bit_iter = iter(path.split("/"))
728
for child in self.iter_children(cur_id):
730
# XXX: it seem like if the child is known to be in the
731
# tree, we shouldn't need to go from its id back to
732
# its path -- mbp 2010-02-11
734
# XXX: it seems like we could be more efficient
735
# by just directly looking up the original name and
736
# only then searching all children; also by not
737
# chopping paths so much. -- mbp 2010-02-11
738
child_base = os.path.basename(self.id2path(child))
739
if (child_base == elt):
740
# if we found an exact match, we can stop now; if
741
# we found an approximate match we need to keep
742
# searching because there might be an exact match
745
new_path = osutils.pathjoin(cur_path, child_base)
747
elif child_base.lower() == lelt:
749
new_path = osutils.pathjoin(cur_path, child_base)
750
except errors.NoSuchId:
751
# before a change is committed we can see this error...
756
# got to the end of this directory and no entries matched.
757
# Return what matched so far, plus the rest as specified.
758
cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
763
def _get_inventory(self):
764
return self._inventory
766
inventory = property(_get_inventory,
767
doc="Inventory of this Tree")
770
def path2id(self, path):
771
"""Return the id for path in this tree."""
772
return self._inventory.path2id(path)
774
def id2path(self, file_id):
775
"""Return the path for a file id.
779
return self.inventory.id2path(file_id)
781
def has_id(self, file_id):
782
return self.inventory.has_id(file_id)
784
def has_or_had_id(self, file_id):
785
return self.inventory.has_id(file_id)
787
def all_file_ids(self):
788
return set(self.inventory)
790
@deprecated_method(deprecated_in((2, 4, 0)))
792
return iter(self.inventory)
794
def filter_unversioned_files(self, paths):
795
"""Filter out paths that are versioned.
797
:return: set of paths.
799
# NB: we specifically *don't* call self.has_filename, because for
800
# WorkingTrees that can indicate files that exist on disk but that
802
pred = self.inventory.has_filename
803
return set((p for p in paths if not pred(p)))
806
def iter_entries_by_dir(self, specific_file_ids=None, yield_parents=False):
807
"""Walk the tree in 'by_dir' order.
809
This will yield each entry in the tree as a (path, entry) tuple.
810
The order that they are yielded is:
812
See Tree.iter_entries_by_dir for details.
814
:param yield_parents: If True, yield the parents from the root leading
815
down to specific_file_ids that have been requested. This has no
816
impact if specific_file_ids is None.
818
return self.inventory.iter_entries_by_dir(
819
specific_file_ids=specific_file_ids, yield_parents=yield_parents)
821
def get_file_by_path(self, path):
822
return self.get_file(self._inventory.path2id(path), path)
718
old_inv = old_tree.inventory
719
new_inv = new_tree.inventory
720
new_id = new_inv.path2id(filename)
721
old_id = old_inv.path2id(filename)
723
if not new_id and not old_id:
724
# easy: doesn't exist in either; not versioned at all
725
if new_tree.is_ignored(filename):
726
return 'I', None, None
728
return '?', None, None
730
# There is now a file of this name, great.
733
# There is no longer a file of this name, but we can describe
734
# what happened to the file that used to have
735
# this name. There are two possibilities: either it was
736
# deleted entirely, or renamed.
737
if new_inv.has_id(old_id):
738
return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
740
return 'D', old_inv.id2path(old_id), None
742
# if the file_id is new in this revision, it is added
743
if new_id and not old_inv.has_id(new_id):
746
# if there used to be a file of this name, but that ID has now
747
# disappeared, it is deleted
748
if old_id and not new_inv.has_id(old_id):
754
@deprecated_function(deprecated_in((1, 9, 0)))
755
def find_renames(old_inv, new_inv):
756
for file_id in old_inv:
757
if file_id not in new_inv:
759
old_name = old_inv.id2path(file_id)
760
new_name = new_inv.id2path(file_id)
761
if old_name != new_name:
762
yield (old_name, new_name)
825
765
def find_ids_across_trees(filenames, trees, require_versioned=True):
906
846
will pass through to InterTree as appropriate.
909
# Formats that will be used to test this InterTree. If both are
910
# None, this InterTree will not be tested (e.g. because a complex
912
_matching_from_tree_format = None
913
_matching_to_tree_format = None
918
def is_compatible(kls, source, target):
919
# The default implementation is naive and uses the public API, so
920
# it works for all trees.
923
def _changes_from_entries(self, source_entry, target_entry,
924
source_path=None, target_path=None):
925
"""Generate a iter_changes tuple between source_entry and target_entry.
927
:param source_entry: An inventory entry from self.source, or None.
928
:param target_entry: An inventory entry from self.target, or None.
929
:param source_path: The path of source_entry, if known. If not known
930
it will be looked up.
931
:param target_path: The path of target_entry, if known. If not known
932
it will be looked up.
933
:return: A tuple, item 0 of which is an iter_changes result tuple, and
934
item 1 is True if there are any changes in the result tuple.
936
if source_entry is None:
937
if target_entry is None:
939
file_id = target_entry.file_id
941
file_id = source_entry.file_id
942
if source_entry is not None:
943
source_versioned = True
944
source_name = source_entry.name
945
source_parent = source_entry.parent_id
946
if source_path is None:
947
source_path = self.source.id2path(file_id)
948
source_kind, source_executable, source_stat = \
949
self.source._comparison_data(source_entry, source_path)
951
source_versioned = False
955
source_executable = None
956
if target_entry is not None:
957
target_versioned = True
958
target_name = target_entry.name
959
target_parent = target_entry.parent_id
960
if target_path is None:
961
target_path = self.target.id2path(file_id)
962
target_kind, target_executable, target_stat = \
963
self.target._comparison_data(target_entry, target_path)
965
target_versioned = False
969
target_executable = None
970
versioned = (source_versioned, target_versioned)
971
kind = (source_kind, target_kind)
972
changed_content = False
973
if source_kind != target_kind:
974
changed_content = True
975
elif source_kind == 'file':
976
if not self.file_content_matches(file_id, file_id, source_path,
977
target_path, source_stat, target_stat):
978
changed_content = True
979
elif source_kind == 'symlink':
980
if (self.source.get_symlink_target(file_id) !=
981
self.target.get_symlink_target(file_id)):
982
changed_content = True
983
# XXX: Yes, the indentation below is wrong. But fixing it broke
984
# test_merge.TestMergerEntriesLCAOnDisk.
985
# test_nested_tree_subtree_renamed_and_modified. We'll wait for
986
# the fix from bzr.dev -- vila 2009026
987
elif source_kind == 'tree-reference':
988
if (self.source.get_reference_revision(file_id, source_path)
989
!= self.target.get_reference_revision(file_id, target_path)):
990
changed_content = True
991
parent = (source_parent, target_parent)
992
name = (source_name, target_name)
993
executable = (source_executable, target_executable)
994
if (changed_content is not False or versioned[0] != versioned[1]
995
or parent[0] != parent[1] or name[0] != name[1] or
996
executable[0] != executable[1]):
1000
return (file_id, (source_path, target_path), changed_content,
1001
versioned, parent, name, kind, executable), changes
1003
851
@needs_read_lock
1004
852
def compare(self, want_unchanged=False, specific_files=None,
1005
853
extra_trees=None, require_versioned=False, include_root=False,
1115
947
# the unversioned path lookup only occurs on real trees - where there
1116
948
# can be extras. So the fake_entry is solely used to look up
1117
949
# executable it values when execute is not supported.
1118
fake_entry = inventory.InventoryFile('unused', 'unused', 'unused')
1119
for target_path, target_entry in to_entries_by_dir:
1120
while (all_unversioned and
1121
all_unversioned[0][0] < target_path.split('/')):
950
fake_entry = InventoryFile('unused', 'unused', 'unused')
951
for to_path, to_entry in to_entries_by_dir:
952
while all_unversioned and all_unversioned[0][0] < to_path.split('/'):
1122
953
unversioned_path = all_unversioned.popleft()
1123
target_kind, target_executable, target_stat = \
954
to_kind, to_executable, to_stat = \
1124
955
self.target._comparison_data(fake_entry, unversioned_path[1])
1125
956
yield (None, (None, unversioned_path[1]), True, (False, False),
1127
958
(None, unversioned_path[0][-1]),
1128
(None, target_kind),
1129
(None, target_executable))
1130
source_path, source_entry = from_data.get(target_entry.file_id,
1132
result, changes = self._changes_from_entries(source_entry,
1133
target_entry, source_path=source_path, target_path=target_path)
1134
to_paths[result[0]] = result[1][1]
960
(None, to_executable))
961
file_id = to_entry.file_id
962
to_paths[file_id] = to_path
1135
963
entry_count += 1
964
changed_content = False
965
from_path, from_entry = from_data.get(file_id, (None, None))
966
from_versioned = (from_entry is not None)
967
if from_entry is not None:
968
from_versioned = True
969
from_name = from_entry.name
970
from_parent = from_entry.parent_id
971
from_kind, from_executable, from_stat = \
972
self.source._comparison_data(from_entry, from_path)
1137
973
entry_count += 1
975
from_versioned = False
979
from_executable = None
980
versioned = (from_versioned, True)
981
to_kind, to_executable, to_stat = \
982
self.target._comparison_data(to_entry, to_path)
983
kind = (from_kind, to_kind)
984
if kind[0] != kind[1]:
985
changed_content = True
986
elif from_kind == 'file':
987
if (self.source.get_file_sha1(file_id, from_path, from_stat) !=
988
self.target.get_file_sha1(file_id, to_path, to_stat)):
989
changed_content = True
990
elif from_kind == 'symlink':
991
if (self.source.get_symlink_target(file_id) !=
992
self.target.get_symlink_target(file_id)):
993
changed_content = True
994
# XXX: Yes, the indentation below is wrong. But fixing it broke
995
# test_merge.TestMergerEntriesLCAOnDisk.
996
# test_nested_tree_subtree_renamed_and_modified. We'll wait for
997
# the fix from bzr.dev -- vila 2009026
998
elif from_kind == 'tree-reference':
999
if (self.source.get_reference_revision(file_id, from_path)
1000
!= self.target.get_reference_revision(file_id, to_path)):
1001
changed_content = True
1002
parent = (from_parent, to_entry.parent_id)
1003
name = (from_name, to_entry.name)
1004
executable = (from_executable, to_executable)
1138
1005
if pb is not None:
1139
1006
pb.update('comparing files', entry_count, num_entries)
1140
if changes or include_unchanged:
1141
if specific_file_ids is not None:
1142
new_parent_id = result[4][1]
1143
precise_file_ids.add(new_parent_id)
1144
changed_file_ids.append(result[0])
1146
# Ensure correct behaviour for reparented/added specific files.
1147
if specific_files is not None:
1148
# Record output dirs
1149
if result[6][1] == 'directory':
1150
seen_dirs.add(result[0])
1151
# Record parents of reparented/added entries.
1152
versioned = result[3]
1154
if not versioned[0] or parents[0] != parents[1]:
1155
seen_parents.add(parents[1])
1007
if (changed_content is not False or versioned[0] != versioned[1]
1008
or parent[0] != parent[1] or name[0] != name[1] or
1009
executable[0] != executable[1] or include_unchanged):
1010
yield (file_id, (from_path, to_path), changed_content,
1011
versioned, parent, name, kind, executable)
1156
1013
while all_unversioned:
1157
1014
# yield any trailing unversioned paths
1158
1015
unversioned_path = all_unversioned.popleft()
1187
1056
executable = (from_executable, None)
1188
1057
changed_content = from_kind is not None
1189
1058
# the parent's path is necessarily known at this point.
1190
changed_file_ids.append(file_id)
1191
1059
yield(file_id, (path, to_path), changed_content, versioned, parent,
1192
1060
name, kind, executable)
1193
changed_file_ids = set(changed_file_ids)
1194
if specific_file_ids is not None:
1195
for result in self._handle_precise_ids(precise_file_ids,
1199
def _get_entry(self, tree, file_id):
1200
"""Get an inventory entry from a tree, with missing entries as None.
1202
If the tree raises NotImplementedError on accessing .inventory, then
1203
this is worked around using iter_entries_by_dir on just the file id
1206
:param tree: The tree to lookup the entry in.
1207
:param file_id: The file_id to lookup.
1210
inventory = tree.inventory
1211
except NotImplementedError:
1212
# No inventory available.
1214
iterator = tree.iter_entries_by_dir(specific_file_ids=[file_id])
1215
return iterator.next()[1]
1216
except StopIteration:
1220
return inventory[file_id]
1221
except errors.NoSuchId:
1224
def _handle_precise_ids(self, precise_file_ids, changed_file_ids,
1225
discarded_changes=None):
1226
"""Fill out a partial iter_changes to be consistent.
1228
:param precise_file_ids: The file ids of parents that were seen during
1230
:param changed_file_ids: The file ids of already emitted items.
1231
:param discarded_changes: An optional dict of precalculated
1232
iter_changes items which the partial iter_changes had not output
1234
:return: A generator of iter_changes items to output.
1236
# process parents of things that had changed under the users
1237
# requested paths to prevent incorrect paths or parent ids which
1238
# aren't in the tree.
1239
while precise_file_ids:
1240
precise_file_ids.discard(None)
1241
# Don't emit file_ids twice
1242
precise_file_ids.difference_update(changed_file_ids)
1243
if not precise_file_ids:
1245
# If the there was something at a given output path in source, we
1246
# have to include the entry from source in the delta, or we would
1247
# be putting this entry into a used path.
1249
for parent_id in precise_file_ids:
1251
paths.append(self.target.id2path(parent_id))
1252
except errors.NoSuchId:
1253
# This id has been dragged in from the source by delta
1254
# expansion and isn't present in target at all: we don't
1255
# need to check for path collisions on it.
1258
old_id = self.source.path2id(path)
1259
precise_file_ids.add(old_id)
1260
precise_file_ids.discard(None)
1261
current_ids = precise_file_ids
1262
precise_file_ids = set()
1263
# We have to emit all of precise_file_ids that have been altered.
1264
# We may have to output the children of some of those ids if any
1265
# directories have stopped being directories.
1266
for file_id in current_ids:
1268
if discarded_changes:
1269
result = discarded_changes.get(file_id)
1274
old_entry = self._get_entry(self.source, file_id)
1275
new_entry = self._get_entry(self.target, file_id)
1276
result, changes = self._changes_from_entries(
1277
old_entry, new_entry)
1280
# Get this parents parent to examine.
1281
new_parent_id = result[4][1]
1282
precise_file_ids.add(new_parent_id)
1284
if (result[6][0] == 'directory' and
1285
result[6][1] != 'directory'):
1286
# This stopped being a directory, the old children have
1288
if old_entry is None:
1289
# Reusing a discarded change.
1290
old_entry = self._get_entry(self.source, file_id)
1291
for child in old_entry.children.values():
1292
precise_file_ids.add(child.file_id)
1293
changed_file_ids.add(result[0])
1297
def file_content_matches(self, source_file_id, target_file_id,
1298
source_path=None, target_path=None, source_stat=None, target_stat=None):
1299
"""Check if two files are the same in the source and target trees.
1301
This only checks that the contents of the files are the same,
1302
it does not touch anything else.
1304
:param source_file_id: File id of the file in the source tree
1305
:param target_file_id: File id of the file in the target tree
1306
:param source_path: Path of the file in the source tree
1307
:param target_path: Path of the file in the target tree
1308
:param source_stat: Optional stat value of the file in the source tree
1309
:param target_stat: Optional stat value of the file in the target tree
1310
:return: Boolean indicating whether the files have the same contents
1312
source_verifier_kind, source_verifier_data = self.source.get_file_verifier(
1313
source_file_id, source_path, source_stat)
1314
target_verifier_kind, target_verifier_data = self.target.get_file_verifier(
1315
target_file_id, target_path, target_stat)
1316
if source_verifier_kind == target_verifier_kind:
1317
return (source_verifier_data == target_verifier_data)
1318
# Fall back to SHA1 for now
1319
if source_verifier_kind != "SHA1":
1320
source_sha1 = self.source.get_file_sha1(source_file_id,
1321
source_path, source_stat)
1323
source_sha1 = source_verifier_data
1324
if target_verifier_kind != "SHA1":
1325
target_sha1 = self.target.get_file_sha1(target_file_id,
1326
target_path, target_stat)
1328
target_sha1 = target_verifier_data
1329
return (source_sha1 == target_sha1)
1331
InterTree.register_optimiser(InterTree)
1334
1063
class MultiWalker(object):