246
258
raise NotImplementedError(self.iter_entries_by_dir)
248
def iter_child_entries(self, path, file_id=None):
260
def iter_child_entries(self, path):
249
261
"""Iterate over the children of a directory or tree reference.
251
263
: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
264
:raise NoSuchFile: When the path does not exist
254
265
:return: Iterator over entries in the directory
256
267
raise NotImplementedError(self.iter_child_entries)
272
283
if entry.kind == 'tree-reference':
273
284
yield path, entry.file_id
275
def kind(self, path, file_id=None):
286
def kind(self, path):
276
287
raise NotImplementedError("Tree subclass %s must implement kind"
277
% self.__class__.__name__)
288
% self.__class__.__name__)
279
def stored_kind(self, path, file_id=None):
280
"""File kind stored for this file_id.
290
def stored_kind(self, path):
291
"""File kind stored for this path.
282
293
May not match kind on disk for working trees. Always available
283
294
for versioned files, even when the file itself is missing.
285
return self.kind(path, file_id)
296
return self.kind(path)
287
298
def path_content_summary(self, path):
288
299
"""Get a summary of the information about path.
303
314
raise NotImplementedError(self.path_content_summary)
305
def get_reference_revision(self, path, file_id=None):
316
def get_reference_revision(self, path):
306
317
raise NotImplementedError("Tree subclass %s must implement "
307
318
"get_reference_revision"
308
% self.__class__.__name__)
319
% self.__class__.__name__)
310
321
def _comparison_data(self, entry, path):
311
322
"""Return a tuple of kind, executable, stat_value for a file.
319
330
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.
332
def get_file(self, path):
333
"""Return a file object for the file path in the tree.
327
335
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.
337
def get_file_with_stat(self, path):
338
"""Get a file handle and stat object for path.
332
340
The default implementation returns (self.get_file, None) for backwards
335
343
:param path: The path of the file.
336
:param file_id: The file id to read, if it is known.
337
344
:return: A tuple (file_handle, stat_value_or_None). If the tree has
338
345
no stat facility, or need for a stat cache feedback during commit,
339
346
it may return None for the second element of the tuple.
341
return (self.get_file(path, file_id), None)
348
return (self.get_file(path), None)
343
def get_file_text(self, path, file_id=None):
350
def get_file_text(self, path):
344
351
"""Return the byte content of a file.
346
353
: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
355
:returns: A single byte string for the whole file.
354
with self.get_file(path, file_id) as my_file:
357
with self.get_file(path) as my_file:
355
358
return my_file.read()
357
def get_file_lines(self, path, file_id=None):
360
def get_file_lines(self, path):
358
361
"""Return the content of a file, as lines.
360
363
: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))
365
return osutils.split_lines(self.get_file_text(path))
368
def get_file_verifier(self, path, file_id=None, stat_value=None):
367
def get_file_verifier(self, path, stat_value=None):
369
368
"""Return a verifier for a file.
371
370
The default implementation returns a sha1.
373
:param file_id: The handle for this file.
374
372
:param path: The path that this file can be found at.
375
373
These must point to the same object.
376
374
:param stat_value: Optional stat value for the object
377
375
:return: Tuple with verifier name and verifier data
379
return ("SHA1", self.get_file_sha1(path, file_id,
380
stat_value=stat_value))
377
return ("SHA1", self.get_file_sha1(path, stat_value=stat_value))
382
def get_file_sha1(self, path, file_id=None, stat_value=None):
379
def get_file_sha1(self, path, stat_value=None):
383
380
"""Return the SHA1 file for a file.
385
382
:note: callers should use get_file_verifier instead
387
384
have quicker access to a non-sha1 verifier.
389
386
: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
387
:param stat_value: Optional stat value for the object
394
389
raise NotImplementedError(self.get_file_sha1)
396
def get_file_mtime(self, path, file_id=None):
391
def get_file_mtime(self, path):
397
392
"""Return the modification time for a file.
399
394
: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
396
raise NotImplementedError(self.get_file_mtime)
405
def get_file_size(self, path, file_id=None):
398
def get_file_size(self, path):
406
399
"""Return the size of a file in bytes.
408
401
This applies only to regular files. If invoked on directories or
409
402
symlinks, it will return None.
410
:param file_id: The file-id of the file
412
404
raise NotImplementedError(self.get_file_size)
414
def is_executable(self, path, file_id=None):
406
def is_executable(self, path):
415
407
"""Check if a file is executable.
417
409
: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
411
raise NotImplementedError(self.is_executable)
446
436
cur_file = (self.get_file_text(path),)
447
437
yield identifier, cur_file
449
def get_symlink_target(self, path, file_id=None):
450
"""Get the target for a given file_id.
439
def get_symlink_target(self, path):
440
"""Get the target for a given path.
452
It is assumed that the caller already knows that file_id is referencing
442
It is assumed that the caller already knows that path is referencing
454
:param file_id: Handle for the symlink entry.
455
444
:param path: The path of the file.
456
If both file_id and path are supplied, an implementation may use
458
445
:return: The path the symlink points to.
460
447
raise NotImplementedError(self.get_symlink_target)
463
450
"""Return the file_id for the root of this tree."""
464
451
raise NotImplementedError(self.get_root_id)
466
def annotate_iter(self, path, file_id=None,
453
def annotate_iter(self, path,
467
454
default_revision=_mod_revision.CURRENT_REVISION):
468
455
"""Return an iterator of revision_id, line tuples.
470
457
For working trees (and mutable trees in general), the special
471
458
revision_id 'current:' will be used for lines that are new in this
472
459
tree, e.g. uncommitted changes.
473
:param file_id: The file to produce an annotated version from
460
:param path: The file to produce an annotated version from
474
461
:param default_revision: For lines that don't match a basis, mark them
475
462
with this revision id. Not all implementations will make use of
749
737
elif source_kind == 'file':
750
738
if not self.file_content_matches(
751
739
source_path, target_path,
752
file_id, file_id, source_stat, target_stat):
740
source_stat, target_stat):
753
741
changed_content = True
754
742
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)):
743
if (self.source.get_symlink_target(source_path) !=
744
self.target.get_symlink_target(target_path)):
757
745
changed_content = True
758
746
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)):
761
changed_content = True
747
if (self.source.get_reference_revision(source_path)
748
!= self.target.get_reference_revision(target_path)):
749
changed_content = True
762
750
parent = (source_parent, target_parent)
763
751
name = (source_name, target_name)
764
752
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]):
753
if (changed_content is not False or versioned[0] != versioned[1] or
754
parent[0] != parent[1] or name[0] != name[1] or
755
executable[0] != executable[1]):
772
760
versioned, parent, name, kind, executable), changes
774
762
def compare(self, want_unchanged=False, specific_files=None,
775
extra_trees=None, require_versioned=False, include_root=False,
776
want_unversioned=False):
763
extra_trees=None, require_versioned=False, include_root=False,
764
want_unversioned=False):
777
765
"""Return the changes from source to target.
779
767
:return: A TreeDelta.
795
783
trees = trees + tuple(extra_trees)
796
784
with self.lock_read():
797
785
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)
786
specific_files, include_root, extra_trees=extra_trees,
787
require_versioned=require_versioned,
788
want_unversioned=want_unversioned)
802
790
def iter_changes(self, include_unchanged=False,
803
specific_files=None, pb=None, extra_trees=[],
804
require_versioned=True, want_unversioned=False):
791
specific_files=None, pb=None, extra_trees=[],
792
require_versioned=True, want_unversioned=False):
805
793
"""Generate an iterator of changes between trees.
807
795
A tuple is returned:
847
835
source_specific_files = []
849
837
target_specific_files = self.target.find_related_paths_across_trees(
850
specific_files, [self.source] + extra_trees,
851
require_versioned=require_versioned)
838
specific_files, [self.source] + extra_trees,
839
require_versioned=require_versioned)
852
840
source_specific_files = self.source.find_related_paths_across_trees(
853
specific_files, [self.target] + extra_trees,
854
require_versioned=require_versioned)
841
specific_files, [self.target] + extra_trees,
842
require_versioned=require_versioned)
855
843
if specific_files is not None:
856
844
# reparented or added entries must have their parents included
857
845
# so that valid deltas can be created. The seen_parents set
863
851
seen_dirs = set()
864
852
if want_unversioned:
865
853
all_unversioned = sorted([(p.split('/'), p) for p in
867
if specific_files is None or
868
osutils.is_inside_any(specific_files, p)])
869
all_unversioned = collections.deque(all_unversioned)
855
if specific_files is None or
856
osutils.is_inside_any(specific_files, p)])
857
all_unversioned = deque(all_unversioned)
871
all_unversioned = collections.deque()
859
all_unversioned = deque()
873
861
from_entries_by_dir = list(self.source.iter_entries_by_dir(
874
862
specific_files=source_specific_files))
883
871
fake_entry = TreeFile()
884
872
for target_path, target_entry in to_entries_by_dir:
885
873
while (all_unversioned and
886
all_unversioned[0][0] < target_path.split('/')):
874
all_unversioned[0][0] < target_path.split('/')):
887
875
unversioned_path = all_unversioned.popleft()
888
876
target_kind, target_executable, target_stat = \
889
self.target._comparison_data(fake_entry, unversioned_path[1])
877
self.target._comparison_data(
878
fake_entry, unversioned_path[1])
890
879
yield (None, (None, unversioned_path[1]), True, (False, False),
892
(None, unversioned_path[0][-1]),
894
(None, target_executable))
881
(None, unversioned_path[0][-1]),
883
(None, target_executable))
895
884
source_path, source_entry = from_data.get(target_entry.file_id,
897
886
result, changes = self._changes_from_entries(source_entry,
898
target_entry, source_path=source_path, target_path=target_path)
887
target_entry, source_path=source_path, target_path=target_path)
899
888
to_paths[result[0]] = result[1][1]
924
913
to_kind, to_executable, to_stat = \
925
914
self.target._comparison_data(fake_entry, unversioned_path[1])
926
915
yield (None, (None, unversioned_path[1]), True, (False, False),
928
(None, unversioned_path[0][-1]),
930
(None, to_executable))
917
(None, unversioned_path[0][-1]),
919
(None, to_executable))
931
920
# Yield all remaining source paths
932
921
for path, from_entry in from_entries_by_dir:
933
922
file_id = from_entry.file_id
1029
1018
source_path = None
1030
1019
source_entry = None
1032
source_entry = self._get_entry(self.source, source_path)
1021
source_entry = self._get_entry(
1022
self.source, source_path)
1034
1024
target_path = self.target.id2path(file_id)
1035
1025
except errors.NoSuchId:
1036
1026
target_path = None
1037
1027
target_entry = None
1039
target_entry = self._get_entry(self.target, target_path)
1029
target_entry = self._get_entry(
1030
self.target, target_path)
1040
1031
result, changes = self._changes_from_entries(
1041
1032
source_entry, target_entry, source_path, target_path)
1051
1042
# to be included.
1052
1043
if source_entry is None:
1053
1044
# Reusing a discarded change.
1054
source_entry = self._get_entry(self.source, result[1][0])
1045
source_entry = self._get_entry(
1046
self.source, result[1][0])
1055
1047
precise_file_ids.update(
1057
for child in self.source.iter_child_entries(result[1][0]))
1049
for child in self.source.iter_child_entries(result[1][0]))
1058
1050
changed_file_ids.add(result[0])
1061
1053
def file_content_matches(
1062
1054
self, source_path, target_path,
1063
source_file_id=None, target_file_id=None,
1064
1055
source_stat=None, target_stat=None):
1065
1056
"""Check if two files are the same in the source and target trees.
1078
1069
with self.lock_read():
1079
1070
source_verifier_kind, source_verifier_data = (
1080
self.source.get_file_verifier(
1081
source_path, source_file_id, source_stat))
1071
self.source.get_file_verifier(source_path, source_stat))
1082
1072
target_verifier_kind, target_verifier_data = (
1083
1073
self.target.get_file_verifier(
1084
target_path, target_file_id, target_stat))
1074
target_path, target_stat))
1085
1075
if source_verifier_kind == target_verifier_kind:
1086
1076
return (source_verifier_data == target_verifier_data)
1087
1077
# Fall back to SHA1 for now
1088
1078
if source_verifier_kind != "SHA1":
1089
1079
source_sha1 = self.source.get_file_sha1(
1090
source_path, source_file_id, source_stat)
1080
source_path, source_file_id, source_stat)
1092
1082
source_sha1 = source_verifier_data
1093
1083
if target_verifier_kind != "SHA1":
1094
1084
target_sha1 = self.target.get_file_sha1(
1095
target_path, target_file_id, target_stat)
1085
target_path, target_file_id, target_stat)
1097
1087
target_sha1 = target_verifier_data
1098
1088
return (source_sha1 == target_sha1)
1100
1091
InterTree.register_optimiser(InterTree)
1324
1315
other_extra.pop(file_id)
1325
1316
other_values = [(None, None)] * idx
1326
1317
other_values.append((other_path, other_ie))
1327
for alt_idx, alt_extra in enumerate(self._others_extra[idx+1:]):
1318
for alt_idx, alt_extra in enumerate(self._others_extra[idx + 1:]):
1328
1319
alt_idx = alt_idx + idx + 1
1329
1320
alt_extra = self._others_extra[alt_idx]
1330
1321
alt_tree = self._other_trees[alt_idx]
1331
1322
other_values.append(self._lookup_by_file_id(
1332
alt_extra, alt_tree, file_id))
1323
alt_extra, alt_tree, file_id))
1333
1324
yield other_path, file_id, None, other_values
1375
1366
:return: The canonical path
1378
cur_id = tree.get_root_id()
1380
1370
bit_iter = iter(path.split("/"))
1381
1371
for elt in bit_iter:
1382
1372
lelt = normalize(elt)
1383
1373
new_path = None
1385
for child in tree.iter_child_entries(cur_path, cur_id):
1375
for child in tree.iter_child_entries(cur_path):
1387
1377
if child.name == elt:
1388
1378
# if we found an exact match, we can stop now; if
1389
1379
# we found an approximate match we need to keep
1390
1380
# searching because there might be an exact match
1392
cur_id = child.file_id
1393
1382
new_path = osutils.pathjoin(cur_path, child.name)
1395
1384
elif normalize(child.name) == lelt:
1396
cur_id = child.file_id
1397
1385
new_path = osutils.pathjoin(cur_path, child.name)
1398
1386
except errors.NoSuchId:
1399
1387
# before a change is committed we can see this error...