130
130
def changes_from(self, other, want_unchanged=False, specific_files=None,
131
extra_trees=None, require_versioned=False, include_root=False,
132
want_unversioned=False):
131
extra_trees=None, require_versioned=False, include_root=False,
132
want_unversioned=False):
133
133
"""Return a TreeDelta of the changes from other to this tree.
135
135
:param other: A tree to compare with.
167
167
"""See InterTree.iter_changes"""
168
168
intertree = InterTree.get(from_tree, self)
169
169
return intertree.iter_changes(include_unchanged, specific_files, pb,
170
extra_trees, require_versioned, want_unversioned=want_unversioned)
170
extra_trees, require_versioned, want_unversioned=want_unversioned)
172
172
def conflicts(self):
173
173
"""Get a list of the conflicts in the tree.
273
273
def kind(self, path, file_id=None):
274
274
raise NotImplementedError("Tree subclass %s must implement kind"
275
% self.__class__.__name__)
275
% self.__class__.__name__)
277
277
def stored_kind(self, path, file_id=None):
278
278
"""File kind stored for this file_id.
303
303
def get_reference_revision(self, path, file_id=None):
304
304
raise NotImplementedError("Tree subclass %s must implement "
305
305
"get_reference_revision"
306
% self.__class__.__name__)
306
% self.__class__.__name__)
308
308
def _comparison_data(self, entry, path):
309
309
"""Return a tuple of kind, executable, stat_value for a file.
375
375
:return: Tuple with verifier name and verifier data
377
377
return ("SHA1", self.get_file_sha1(path, file_id,
378
stat_value=stat_value))
378
stat_value=stat_value))
380
380
def get_file_sha1(self, path, file_id=None, stat_value=None):
381
381
"""Return the SHA1 file for a file.
496
496
return self.path2id(path) is not None
498
498
def find_related_paths_across_trees(self, paths, trees=[],
499
require_versioned=True):
499
require_versioned=True):
500
500
"""Find related paths in tree corresponding to specified filenames in any
501
501
of `lookup_trees`.
608
608
prefs = next(self.iter_search_rules([path], filter_pref_names))
609
609
stk = filters._get_filter_stack_for(prefs)
610
610
if 'filters' in debug.debug_flags:
611
trace.note(gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
612
gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
614
615
def _content_filter_stack_provider(self):
622
623
if self.supports_content_filtering():
623
624
return lambda path, file_id: \
624
self._content_filter_stack(path)
625
self._content_filter_stack(path)
628
629
def iter_search_rules(self, path_names, pref_names=None,
629
_default_searcher=None):
630
_default_searcher=None):
630
631
"""Find the preferences for filenames in a tree.
632
633
:param path_names: an iterable of paths to find attributes for.
665
666
from .archive import create_archive
666
667
with self.lock_read():
667
668
return create_archive(format, self, name, root,
668
subdir, force_mtime=force_mtime)
669
subdir, force_mtime=force_mtime)
671
672
def versionable_kind(cls, kind):
751
752
changed_content = True
752
753
elif source_kind == 'symlink':
753
754
if (self.source.get_symlink_target(source_path, file_id) !=
754
self.target.get_symlink_target(target_path, file_id)):
755
self.target.get_symlink_target(target_path, file_id)):
755
756
changed_content = True
756
757
elif source_kind == 'tree-reference':
757
758
if (self.source.get_reference_revision(source_path, file_id)
758
!= self.target.get_reference_revision(target_path, file_id)):
759
changed_content = True
759
!= self.target.get_reference_revision(target_path, file_id)):
760
changed_content = True
760
761
parent = (source_parent, target_parent)
761
762
name = (source_name, target_name)
762
763
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]):
764
if (changed_content is not False or versioned[0] != versioned[1] or
765
parent[0] != parent[1] or name[0] != name[1] or
766
executable[0] != executable[1]):
770
771
versioned, parent, name, kind, executable), changes
772
773
def compare(self, want_unchanged=False, specific_files=None,
773
extra_trees=None, require_versioned=False, include_root=False,
774
want_unversioned=False):
774
extra_trees=None, require_versioned=False, include_root=False,
775
want_unversioned=False):
775
776
"""Return the changes from source to target.
777
778
:return: A TreeDelta.
793
794
trees = trees + tuple(extra_trees)
794
795
with self.lock_read():
795
796
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)
797
specific_files, include_root, extra_trees=extra_trees,
798
require_versioned=require_versioned,
799
want_unversioned=want_unversioned)
800
801
def iter_changes(self, include_unchanged=False,
801
specific_files=None, pb=None, extra_trees=[],
802
require_versioned=True, want_unversioned=False):
802
specific_files=None, pb=None, extra_trees=[],
803
require_versioned=True, want_unversioned=False):
803
804
"""Generate an iterator of changes between trees.
805
806
A tuple is returned:
845
846
source_specific_files = []
847
848
target_specific_files = self.target.find_related_paths_across_trees(
848
specific_files, [self.source] + extra_trees,
849
require_versioned=require_versioned)
849
specific_files, [self.source] + extra_trees,
850
require_versioned=require_versioned)
850
851
source_specific_files = self.source.find_related_paths_across_trees(
851
specific_files, [self.target] + extra_trees,
852
require_versioned=require_versioned)
852
specific_files, [self.target] + extra_trees,
853
require_versioned=require_versioned)
853
854
if specific_files is not None:
854
855
# reparented or added entries must have their parents included
855
856
# so that valid deltas can be created. The seen_parents set
861
862
seen_dirs = set()
862
863
if want_unversioned:
863
864
all_unversioned = sorted([(p.split('/'), p) for p in
865
if specific_files is None or
866
osutils.is_inside_any(specific_files, p)])
866
if specific_files is None or
867
osutils.is_inside_any(specific_files, p)])
867
868
all_unversioned = collections.deque(all_unversioned)
869
870
all_unversioned = collections.deque()
881
882
fake_entry = TreeFile()
882
883
for target_path, target_entry in to_entries_by_dir:
883
884
while (all_unversioned and
884
all_unversioned[0][0] < target_path.split('/')):
885
all_unversioned[0][0] < target_path.split('/')):
885
886
unversioned_path = all_unversioned.popleft()
886
887
target_kind, target_executable, target_stat = \
887
self.target._comparison_data(fake_entry, unversioned_path[1])
888
self.target._comparison_data(
889
fake_entry, unversioned_path[1])
888
890
yield (None, (None, unversioned_path[1]), True, (False, False),
890
(None, unversioned_path[0][-1]),
892
(None, target_executable))
892
(None, unversioned_path[0][-1]),
894
(None, target_executable))
893
895
source_path, source_entry = from_data.get(target_entry.file_id,
895
897
result, changes = self._changes_from_entries(source_entry,
896
target_entry, source_path=source_path, target_path=target_path)
898
target_entry, source_path=source_path, target_path=target_path)
897
899
to_paths[result[0]] = result[1][1]
922
924
to_kind, to_executable, to_stat = \
923
925
self.target._comparison_data(fake_entry, unversioned_path[1])
924
926
yield (None, (None, unversioned_path[1]), True, (False, False),
926
(None, unversioned_path[0][-1]),
928
(None, to_executable))
928
(None, unversioned_path[0][-1]),
930
(None, to_executable))
929
931
# Yield all remaining source paths
930
932
for path, from_entry in from_entries_by_dir:
931
933
file_id = from_entry.file_id
951
953
changed_file_ids = set(changed_file_ids)
952
954
if specific_files is not None:
953
955
for result in self._handle_precise_ids(precise_file_ids,
957
959
def _get_entry(self, tree, path):
974
976
def _handle_precise_ids(self, precise_file_ids, changed_file_ids,
975
discarded_changes=None):
977
discarded_changes=None):
976
978
"""Fill out a partial iter_changes to be consistent.
978
980
:param precise_file_ids: The file ids of parents that were seen during
1027
1029
source_path = None
1028
1030
source_entry = None
1030
source_entry = self._get_entry(self.source, source_path)
1032
source_entry = self._get_entry(
1033
self.source, source_path)
1032
1035
target_path = self.target.id2path(file_id)
1033
1036
except errors.NoSuchId:
1034
1037
target_path = None
1035
1038
target_entry = None
1037
target_entry = self._get_entry(self.target, target_path)
1040
target_entry = self._get_entry(
1041
self.target, target_path)
1038
1042
result, changes = self._changes_from_entries(
1039
1043
source_entry, target_entry, source_path, target_path)
1049
1053
# to be included.
1050
1054
if source_entry is None:
1051
1055
# Reusing a discarded change.
1052
source_entry = self._get_entry(self.source, result[1][0])
1056
source_entry = self._get_entry(
1057
self.source, result[1][0])
1053
1058
precise_file_ids.update(
1055
for child in self.source.iter_child_entries(result[1][0]))
1060
for child in self.source.iter_child_entries(result[1][0]))
1056
1061
changed_file_ids.add(result[0])
1076
1081
with self.lock_read():
1077
1082
source_verifier_kind, source_verifier_data = (
1078
self.source.get_file_verifier(
1079
source_path, source_file_id, source_stat))
1083
self.source.get_file_verifier(
1084
source_path, source_file_id, source_stat))
1080
1085
target_verifier_kind, target_verifier_data = (
1081
1086
self.target.get_file_verifier(
1082
1087
target_path, target_file_id, target_stat))
1085
1090
# Fall back to SHA1 for now
1086
1091
if source_verifier_kind != "SHA1":
1087
1092
source_sha1 = self.source.get_file_sha1(
1088
source_path, source_file_id, source_stat)
1093
source_path, source_file_id, source_stat)
1090
1095
source_sha1 = source_verifier_data
1091
1096
if target_verifier_kind != "SHA1":
1092
1097
target_sha1 = self.target.get_file_sha1(
1093
target_path, target_file_id, target_stat)
1098
target_path, target_file_id, target_stat)
1095
1100
target_sha1 = target_verifier_data
1096
1101
return (source_sha1 == target_sha1)
1098
1104
InterTree.register_optimiser(InterTree)
1322
1328
other_extra.pop(file_id)
1323
1329
other_values = [(None, None)] * idx
1324
1330
other_values.append((other_path, other_ie))
1325
for alt_idx, alt_extra in enumerate(self._others_extra[idx+1:]):
1331
for alt_idx, alt_extra in enumerate(self._others_extra[idx + 1:]):
1326
1332
alt_idx = alt_idx + idx + 1
1327
1333
alt_extra = self._others_extra[alt_idx]
1328
1334
alt_tree = self._other_trees[alt_idx]
1329
1335
other_values.append(self._lookup_by_file_id(
1330
alt_extra, alt_tree, file_id))
1336
alt_extra, alt_tree, file_id))
1331
1337
yield other_path, file_id, None, other_values