246
244
raise NotImplementedError(self.iter_entries_by_dir)
248
def iter_child_entries(self, path, file_id=None):
246
def iter_child_entries(self, path):
249
247
"""Iterate over the children of a directory or tree reference.
251
249
:param path: Path of the directory
252
:param file_id: Optional file id of the directory/tree-reference
253
:raise NoSuchId: When the file_id does not exist
250
:raise NoSuchFile: When the path does not exist
254
251
:return: Iterator over entries in the directory
256
253
raise NotImplementedError(self.iter_child_entries)
272
269
if entry.kind == 'tree-reference':
273
270
yield path, entry.file_id
275
def kind(self, path, file_id=None):
272
def kind(self, path):
276
273
raise NotImplementedError("Tree subclass %s must implement kind"
277
% self.__class__.__name__)
274
% self.__class__.__name__)
279
def stored_kind(self, path, file_id=None):
280
"""File kind stored for this file_id.
276
def stored_kind(self, path):
277
"""File kind stored for this path.
282
279
May not match kind on disk for working trees. Always available
283
280
for versioned files, even when the file itself is missing.
285
return self.kind(path, file_id)
282
return self.kind(path)
287
284
def path_content_summary(self, path):
288
285
"""Get a summary of the information about path.
303
300
raise NotImplementedError(self.path_content_summary)
305
def get_reference_revision(self, path, file_id=None):
302
def get_reference_revision(self, path):
306
303
raise NotImplementedError("Tree subclass %s must implement "
307
304
"get_reference_revision"
308
% self.__class__.__name__)
305
% self.__class__.__name__)
310
307
def _comparison_data(self, entry, path):
311
308
"""Return a tuple of kind, executable, stat_value for a file.
319
316
raise NotImplementedError(self._comparison_data)
321
def get_file(self, path, file_id=None):
322
"""Return a file object for the file file_id in the tree.
324
If both file_id and path are defined, it is implementation defined as
325
to which one is used.
318
def get_file(self, path):
319
"""Return a file object for the file path in the tree.
327
321
raise NotImplementedError(self.get_file)
329
def get_file_with_stat(self, path, file_id=None):
330
"""Get a file handle and stat object for file_id.
323
def get_file_with_stat(self, path):
324
"""Get a file handle and stat object for path.
332
326
The default implementation returns (self.get_file, None) for backwards
335
329
:param path: The path of the file.
336
:param file_id: The file id to read, if it is known.
337
330
:return: A tuple (file_handle, stat_value_or_None). If the tree has
338
331
no stat facility, or need for a stat cache feedback during commit,
339
332
it may return None for the second element of the tuple.
341
return (self.get_file(path, file_id), None)
334
return (self.get_file(path), None)
343
def get_file_text(self, path, file_id=None):
336
def get_file_text(self, path):
344
337
"""Return the byte content of a file.
346
339
:param path: The path of the file.
347
:param file_id: The file_id of the file.
349
If both file_id and path are supplied, an implementation may use
352
341
:returns: A single byte string for the whole file.
354
with self.get_file(path, file_id) as my_file:
343
with self.get_file(path) as my_file:
355
344
return my_file.read()
357
def get_file_lines(self, path, file_id=None):
346
def get_file_lines(self, path):
358
347
"""Return the content of a file, as lines.
360
349
:param path: The path of the file.
361
:param file_id: The file_id of the file.
363
If both file_id and path are supplied, an implementation may use
366
return osutils.split_lines(self.get_file_text(path, file_id))
351
return osutils.split_lines(self.get_file_text(path))
368
def get_file_verifier(self, path, file_id=None, stat_value=None):
353
def get_file_verifier(self, path, stat_value=None):
369
354
"""Return a verifier for a file.
371
356
The default implementation returns a sha1.
373
:param file_id: The handle for this file.
374
358
:param path: The path that this file can be found at.
375
359
These must point to the same object.
376
360
:param stat_value: Optional stat value for the object
377
361
:return: Tuple with verifier name and verifier data
379
return ("SHA1", self.get_file_sha1(path, file_id,
380
stat_value=stat_value))
363
return ("SHA1", self.get_file_sha1(path, stat_value=stat_value))
382
def get_file_sha1(self, path, file_id=None, stat_value=None):
365
def get_file_sha1(self, path, stat_value=None):
383
366
"""Return the SHA1 file for a file.
385
368
:note: callers should use get_file_verifier instead
387
370
have quicker access to a non-sha1 verifier.
389
372
:param path: The path that this file can be found at.
390
:param file_id: The handle for this file.
391
These must point to the same object.
392
373
:param stat_value: Optional stat value for the object
394
375
raise NotImplementedError(self.get_file_sha1)
396
def get_file_mtime(self, path, file_id=None):
377
def get_file_mtime(self, path):
397
378
"""Return the modification time for a file.
399
380
:param path: The path that this file can be found at.
400
:param file_id: The handle for this file.
401
These must point to the same object.
403
382
raise NotImplementedError(self.get_file_mtime)
405
def get_file_size(self, path, file_id=None):
384
def get_file_size(self, path):
406
385
"""Return the size of a file in bytes.
408
387
This applies only to regular files. If invoked on directories or
409
388
symlinks, it will return None.
410
:param file_id: The file-id of the file
412
390
raise NotImplementedError(self.get_file_size)
414
def is_executable(self, path, file_id=None):
392
def is_executable(self, path):
415
393
"""Check if a file is executable.
417
395
:param path: The path that this file can be found at.
418
:param file_id: The handle for this file.
419
These must point to the same object.
421
397
raise NotImplementedError(self.is_executable)
446
422
cur_file = (self.get_file_text(path),)
447
423
yield identifier, cur_file
449
def get_symlink_target(self, path, file_id=None):
450
"""Get the target for a given file_id.
425
def get_symlink_target(self, path):
426
"""Get the target for a given path.
452
It is assumed that the caller already knows that file_id is referencing
428
It is assumed that the caller already knows that path is referencing
454
:param file_id: Handle for the symlink entry.
455
430
:param path: The path of the file.
456
If both file_id and path are supplied, an implementation may use
458
431
:return: The path the symlink points to.
460
433
raise NotImplementedError(self.get_symlink_target)
463
436
"""Return the file_id for the root of this tree."""
464
437
raise NotImplementedError(self.get_root_id)
466
def annotate_iter(self, path, file_id=None,
439
def annotate_iter(self, path,
467
440
default_revision=_mod_revision.CURRENT_REVISION):
468
441
"""Return an iterator of revision_id, line tuples.
470
443
For working trees (and mutable trees in general), the special
471
444
revision_id 'current:' will be used for lines that are new in this
472
445
tree, e.g. uncommitted changes.
473
:param file_id: The file to produce an annotated version from
446
:param path: The file to produce an annotated version from
474
447
:param default_revision: For lines that don't match a basis, mark them
475
448
with this revision id. Not all implementations will make use of
749
723
elif source_kind == 'file':
750
724
if not self.file_content_matches(
751
725
source_path, target_path,
752
file_id, file_id, source_stat, target_stat):
726
source_stat, target_stat):
753
727
changed_content = True
754
728
elif source_kind == 'symlink':
755
if (self.source.get_symlink_target(source_path, file_id) !=
756
self.target.get_symlink_target(target_path, file_id)):
729
if (self.source.get_symlink_target(source_path) !=
730
self.target.get_symlink_target(target_path)):
757
731
changed_content = True
758
732
elif source_kind == 'tree-reference':
759
if (self.source.get_reference_revision(source_path, file_id)
760
!= self.target.get_reference_revision(target_path, file_id)):
733
if (self.source.get_reference_revision(source_path)
734
!= self.target.get_reference_revision(target_path)):
761
735
changed_content = True
762
736
parent = (source_parent, target_parent)
763
737
name = (source_name, target_name)
764
738
executable = (source_executable, target_executable)
765
if (changed_content is not False or versioned[0] != versioned[1]
766
or parent[0] != parent[1] or name[0] != name[1] or
767
executable[0] != executable[1]):
739
if (changed_content is not False or versioned[0] != versioned[1] or
740
parent[0] != parent[1] or name[0] != name[1] or
741
executable[0] != executable[1]):
772
746
versioned, parent, name, kind, executable), changes
774
748
def compare(self, want_unchanged=False, specific_files=None,
775
extra_trees=None, require_versioned=False, include_root=False,
776
want_unversioned=False):
749
extra_trees=None, require_versioned=False, include_root=False,
750
want_unversioned=False):
777
751
"""Return the changes from source to target.
779
753
:return: A TreeDelta.
795
769
trees = trees + tuple(extra_trees)
796
770
with self.lock_read():
797
771
return delta._compare_trees(self.source, self.target, want_unchanged,
798
specific_files, include_root, extra_trees=extra_trees,
799
require_versioned=require_versioned,
800
want_unversioned=want_unversioned)
772
specific_files, include_root, extra_trees=extra_trees,
773
require_versioned=require_versioned,
774
want_unversioned=want_unversioned)
802
776
def iter_changes(self, include_unchanged=False,
803
specific_files=None, pb=None, extra_trees=[],
804
require_versioned=True, want_unversioned=False):
777
specific_files=None, pb=None, extra_trees=[],
778
require_versioned=True, want_unversioned=False):
805
779
"""Generate an iterator of changes between trees.
807
781
A tuple is returned:
847
821
source_specific_files = []
849
823
target_specific_files = self.target.find_related_paths_across_trees(
850
specific_files, [self.source] + extra_trees,
851
require_versioned=require_versioned)
824
specific_files, [self.source] + extra_trees,
825
require_versioned=require_versioned)
852
826
source_specific_files = self.source.find_related_paths_across_trees(
853
specific_files, [self.target] + extra_trees,
854
require_versioned=require_versioned)
827
specific_files, [self.target] + extra_trees,
828
require_versioned=require_versioned)
855
829
if specific_files is not None:
856
830
# reparented or added entries must have their parents included
857
831
# so that valid deltas can be created. The seen_parents set
883
857
fake_entry = TreeFile()
884
858
for target_path, target_entry in to_entries_by_dir:
885
859
while (all_unversioned and
886
all_unversioned[0][0] < target_path.split('/')):
860
all_unversioned[0][0] < target_path.split('/')):
887
861
unversioned_path = all_unversioned.popleft()
888
862
target_kind, target_executable, target_stat = \
889
self.target._comparison_data(fake_entry, unversioned_path[1])
863
self.target._comparison_data(
864
fake_entry, unversioned_path[1])
890
865
yield (None, (None, unversioned_path[1]), True, (False, False),
892
(None, unversioned_path[0][-1]),
894
(None, target_executable))
867
(None, unversioned_path[0][-1]),
869
(None, target_executable))
895
870
source_path, source_entry = from_data.get(target_entry.file_id,
897
872
result, changes = self._changes_from_entries(source_entry,
898
target_entry, source_path=source_path, target_path=target_path)
873
target_entry, source_path=source_path, target_path=target_path)
899
874
to_paths[result[0]] = result[1][1]
924
899
to_kind, to_executable, to_stat = \
925
900
self.target._comparison_data(fake_entry, unversioned_path[1])
926
901
yield (None, (None, unversioned_path[1]), True, (False, False),
928
(None, unversioned_path[0][-1]),
930
(None, to_executable))
903
(None, unversioned_path[0][-1]),
905
(None, to_executable))
931
906
# Yield all remaining source paths
932
907
for path, from_entry in from_entries_by_dir:
933
908
file_id = from_entry.file_id
1029
1004
source_path = None
1030
1005
source_entry = None
1032
source_entry = self._get_entry(self.source, source_path)
1007
source_entry = self._get_entry(
1008
self.source, source_path)
1034
1010
target_path = self.target.id2path(file_id)
1035
1011
except errors.NoSuchId:
1036
1012
target_path = None
1037
1013
target_entry = None
1039
target_entry = self._get_entry(self.target, target_path)
1015
target_entry = self._get_entry(
1016
self.target, target_path)
1040
1017
result, changes = self._changes_from_entries(
1041
1018
source_entry, target_entry, source_path, target_path)
1051
1028
# to be included.
1052
1029
if source_entry is None:
1053
1030
# Reusing a discarded change.
1054
source_entry = self._get_entry(self.source, result[1][0])
1031
source_entry = self._get_entry(
1032
self.source, result[1][0])
1055
1033
precise_file_ids.update(
1057
for child in self.source.iter_child_entries(result[1][0]))
1035
for child in self.source.iter_child_entries(result[1][0]))
1058
1036
changed_file_ids.add(result[0])
1061
1039
def file_content_matches(
1062
1040
self, source_path, target_path,
1063
source_file_id=None, target_file_id=None,
1064
1041
source_stat=None, target_stat=None):
1065
1042
"""Check if two files are the same in the source and target trees.
1078
1055
with self.lock_read():
1079
1056
source_verifier_kind, source_verifier_data = (
1080
self.source.get_file_verifier(
1081
source_path, source_file_id, source_stat))
1057
self.source.get_file_verifier(source_path, source_stat))
1082
1058
target_verifier_kind, target_verifier_data = (
1083
1059
self.target.get_file_verifier(
1084
target_path, target_file_id, target_stat))
1060
target_path, target_stat))
1085
1061
if source_verifier_kind == target_verifier_kind:
1086
1062
return (source_verifier_data == target_verifier_data)
1087
1063
# Fall back to SHA1 for now
1088
1064
if source_verifier_kind != "SHA1":
1089
1065
source_sha1 = self.source.get_file_sha1(
1090
source_path, source_file_id, source_stat)
1066
source_path, source_file_id, source_stat)
1092
1068
source_sha1 = source_verifier_data
1093
1069
if target_verifier_kind != "SHA1":
1094
1070
target_sha1 = self.target.get_file_sha1(
1095
target_path, target_file_id, target_stat)
1071
target_path, target_file_id, target_stat)
1097
1073
target_sha1 = target_verifier_data
1098
1074
return (source_sha1 == target_sha1)
1100
1077
InterTree.register_optimiser(InterTree)
1324
1301
other_extra.pop(file_id)
1325
1302
other_values = [(None, None)] * idx
1326
1303
other_values.append((other_path, other_ie))
1327
for alt_idx, alt_extra in enumerate(self._others_extra[idx+1:]):
1304
for alt_idx, alt_extra in enumerate(self._others_extra[idx + 1:]):
1328
1305
alt_idx = alt_idx + idx + 1
1329
1306
alt_extra = self._others_extra[alt_idx]
1330
1307
alt_tree = self._other_trees[alt_idx]
1331
1308
other_values.append(self._lookup_by_file_id(
1332
alt_extra, alt_tree, file_id))
1309
alt_extra, alt_tree, file_id))
1333
1310
yield other_path, file_id, None, other_values