295
297
:param file_id: The file_id of the file.
296
298
:param path: The path of the file.
298
299
If both file_id and path are supplied, an implementation may use
301
302
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
304
def get_file_mtime(self, file_id, path=None):
332
305
"""Return the modification time for a file.
384
348
cur_file = (self.get_file_text(file_id),)
385
349
yield identifier, cur_file
387
def get_symlink_target(self, file_id, path=None):
351
def get_symlink_target(self, file_id):
388
352
"""Get the target for a given file_id.
390
354
It is assumed that the caller already knows that file_id is referencing
392
356
: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
357
:return: The path the symlink points to.
398
359
raise NotImplementedError(self.get_symlink_target)
361
def get_canonical_inventory_paths(self, paths):
362
"""Like get_canonical_inventory_path() but works on multiple items.
364
:param paths: A sequence of paths relative to the root of the tree.
365
:return: A list of paths, with each item the corresponding input path
366
adjusted to account for existing elements that match case
369
return list(self._yield_canonical_inventory_paths(paths))
371
def get_canonical_inventory_path(self, path):
372
"""Returns the first inventory item that case-insensitively matches path.
374
If a path matches exactly, it is returned. If no path matches exactly
375
but more than one path matches case-insensitively, it is implementation
376
defined which is returned.
378
If no path matches case-insensitively, the input path is returned, but
379
with as many path entries that do exist changed to their canonical
382
If you need to resolve many names from the same tree, you should
383
use get_canonical_inventory_paths() to avoid O(N) behaviour.
385
:param path: A paths relative to the root of the tree.
386
:return: The input path adjusted to account for existing elements
387
that match case insensitively.
389
return self._yield_canonical_inventory_paths([path]).next()
391
def _yield_canonical_inventory_paths(self, paths):
393
# First, if the path as specified exists exactly, just use it.
394
if self.path2id(path) is not None:
398
cur_id = self.get_root_id()
400
bit_iter = iter(path.split("/"))
403
for child in self.iter_children(cur_id):
405
child_base = os.path.basename(self.id2path(child))
406
if child_base.lower() == lelt:
408
cur_path = osutils.pathjoin(cur_path, child_base)
411
# before a change is committed we can see this error...
414
# got to the end of this directory and no entries matched.
415
# Return what matched so far, plus the rest as specified.
416
cur_path = osutils.pathjoin(cur_path, elt, *list(bit_iter))
400
421
def get_root_id(self):
401
422
"""Return the file_id for the root of this tree."""
402
423
raise NotImplementedError(self.get_root_id)
460
481
except errors.NoSuchRevisionInTree:
461
482
yield self.repository.revision_tree(revision_id)
485
def _file_revision(revision_tree, file_id):
486
"""Determine the revision associated with a file in a given tree."""
487
revision_tree.lock_read()
489
return revision_tree.inventory[file_id].revision
491
revision_tree.unlock()
463
493
def _get_file_revision(self, file_id, vf, tree_revision):
464
494
"""Ensure that file_id, tree_revision is in vf to plan the merge."""
466
496
if getattr(self, '_repository', None) is None:
467
497
last_revision = tree_revision
468
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
498
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
469
499
self._iter_parent_trees()]
470
500
vf.add_lines((file_id, last_revision), parent_keys,
471
self.get_file_lines(file_id))
501
self.get_file(file_id).readlines())
472
502
repo = self.branch.repository
473
503
base_vf = repo.texts
475
last_revision = self.get_file_revision(file_id)
505
last_revision = self._file_revision(self, file_id)
476
506
base_vf = self._repository.texts
477
507
if base_vf not in vf.fallback_versionedfiles:
478
508
vf.fallback_versionedfiles.append(base_vf)
479
509
return last_revision
511
inventory = property(_get_inventory,
512
doc="Inventory of this Tree")
481
514
def _check_retrieved(self, ie, f):
482
515
if not __debug__:
484
fp = osutils.fingerprint_file(f)
517
fp = fingerprint_file(f)
487
520
if ie.text_size is not None:
488
521
if ie.text_size != fp['size']:
489
raise errors.BzrError(
490
"mismatched size for file %r in %r" %
491
(ie.file_id, self._store),
522
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
492
523
["inventory expects %d bytes" % ie.text_size,
493
524
"file is actually %d bytes" % fp['size'],
494
525
"store is probably damaged/corrupt"])
496
527
if ie.text_sha1 != fp['sha1']:
497
raise errors.BzrError("wrong SHA-1 for file %r in %r" %
498
(ie.file_id, self._store),
528
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
499
529
["inventory expects %s" % ie.text_sha1,
500
530
"file is actually %s" % fp['sha1'],
501
531
"store is probably damaged/corrupt"])
503
534
def path2id(self, path):
504
535
"""Return the id for path in this tree."""
505
raise NotImplementedError(self.path2id)
536
return self._inventory.path2id(path)
507
538
def paths2ids(self, paths, trees=[], require_versioned=True):
508
539
"""Return all the ids that can be reached by walking from paths.
662
693
for path in path_names:
663
694
yield searcher.get_items(path)
665
697
def _get_rules_searcher(self, default_searcher):
666
698
"""Get the RulesSearcher for this tree given the default one."""
667
699
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.
703
######################################################################
706
# TODO: Merge these two functions into a single one that can operate
707
# on either a whole tree or a set of files.
709
# TODO: Return the diff in order by filename, not by category or in
710
# random order. Can probably be done by lock-stepping through the
711
# filenames from both trees.
714
def file_status(filename, old_tree, new_tree):
715
"""Return single-letter status, old and new names for a file.
717
The complexity here is in deciding how to represent renames;
718
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)
720
old_inv = old_tree.inventory
721
new_inv = new_tree.inventory
722
new_id = new_inv.path2id(filename)
723
old_id = old_inv.path2id(filename)
725
if not new_id and not old_id:
726
# easy: doesn't exist in either; not versioned at all
727
if new_tree.is_ignored(filename):
728
return 'I', None, None
730
return '?', None, None
732
# There is now a file of this name, great.
735
# There is no longer a file of this name, but we can describe
736
# what happened to the file that used to have
737
# this name. There are two possibilities: either it was
738
# deleted entirely, or renamed.
739
if new_inv.has_id(old_id):
740
return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
742
return 'D', old_inv.id2path(old_id), None
744
# if the file_id is new in this revision, it is added
745
if new_id and not old_inv.has_id(new_id):
748
# if there used to be a file of this name, but that ID has now
749
# disappeared, it is deleted
750
if old_id and not new_inv.has_id(old_id):
756
@deprecated_function(deprecated_in((1, 9, 0)))
757
def find_renames(old_inv, new_inv):
758
for file_id in old_inv:
759
if file_id not in new_inv:
761
old_name = old_inv.id2path(file_id)
762
new_name = new_inv.id2path(file_id)
763
if old_name != new_name:
764
yield (old_name, new_name)
825
767
def find_ids_across_trees(filenames, trees, require_versioned=True):
906
848
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
853
@needs_read_lock
1004
854
def compare(self, want_unchanged=False, specific_files=None,
1005
855
extra_trees=None, require_versioned=False, include_root=False,
1115
949
# the unversioned path lookup only occurs on real trees - where there
1116
950
# can be extras. So the fake_entry is solely used to look up
1117
951
# 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('/')):
952
fake_entry = InventoryFile('unused', 'unused', 'unused')
953
for to_path, to_entry in to_entries_by_dir:
954
while all_unversioned and all_unversioned[0][0] < to_path.split('/'):
1122
955
unversioned_path = all_unversioned.popleft()
1123
target_kind, target_executable, target_stat = \
956
to_kind, to_executable, to_stat = \
1124
957
self.target._comparison_data(fake_entry, unversioned_path[1])
1125
958
yield (None, (None, unversioned_path[1]), True, (False, False),
1127
960
(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]
962
(None, to_executable))
963
file_id = to_entry.file_id
964
to_paths[file_id] = to_path
1135
965
entry_count += 1
966
changed_content = False
967
from_path, from_entry = from_data.get(file_id, (None, None))
968
from_versioned = (from_entry is not None)
969
if from_entry is not None:
970
from_versioned = True
971
from_name = from_entry.name
972
from_parent = from_entry.parent_id
973
from_kind, from_executable, from_stat = \
974
self.source._comparison_data(from_entry, from_path)
1137
975
entry_count += 1
977
from_versioned = False
981
from_executable = None
982
versioned = (from_versioned, True)
983
to_kind, to_executable, to_stat = \
984
self.target._comparison_data(to_entry, to_path)
985
kind = (from_kind, to_kind)
986
if kind[0] != kind[1]:
987
changed_content = True
988
elif from_kind == 'file':
989
if (self.source.get_file_sha1(file_id, from_path, from_stat) !=
990
self.target.get_file_sha1(file_id, to_path, to_stat)):
991
changed_content = True
992
elif from_kind == 'symlink':
993
if (self.source.get_symlink_target(file_id) !=
994
self.target.get_symlink_target(file_id)):
995
changed_content = True
996
# XXX: Yes, the indentation below is wrong. But fixing it broke
997
# test_merge.TestMergerEntriesLCAOnDisk.
998
# test_nested_tree_subtree_renamed_and_modified. We'll wait for
999
# the fix from bzr.dev -- vila 2009026
1000
elif from_kind == 'tree-reference':
1001
if (self.source.get_reference_revision(file_id, from_path)
1002
!= self.target.get_reference_revision(file_id, to_path)):
1003
changed_content = True
1004
parent = (from_parent, to_entry.parent_id)
1005
name = (from_name, to_entry.name)
1006
executable = (from_executable, to_executable)
1138
1007
if pb is not None:
1139
1008
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])
1009
if (changed_content is not False or versioned[0] != versioned[1]
1010
or parent[0] != parent[1] or name[0] != name[1] or
1011
executable[0] != executable[1] or include_unchanged):
1012
yield (file_id, (from_path, to_path), changed_content,
1013
versioned, parent, name, kind, executable)
1156
1015
while all_unversioned:
1157
1016
# yield any trailing unversioned paths
1158
1017
unversioned_path = all_unversioned.popleft()
1187
1058
executable = (from_executable, None)
1188
1059
changed_content = from_kind is not None
1189
1060
# the parent's path is necessarily known at this point.
1190
changed_file_ids.append(file_id)
1191
1061
yield(file_id, (path, to_path), changed_content, versioned, parent,
1192
1062
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
1065
class MultiWalker(object):