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