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.
272
272
def kind(self, path):
273
273
raise NotImplementedError("Tree subclass %s must implement kind"
274
% self.__class__.__name__)
274
% self.__class__.__name__)
276
276
def stored_kind(self, path):
277
277
"""File kind stored for this path.
302
302
def get_reference_revision(self, path):
303
303
raise NotImplementedError("Tree subclass %s must implement "
304
304
"get_reference_revision"
305
% self.__class__.__name__)
305
% self.__class__.__name__)
307
307
def _comparison_data(self, entry, path):
308
308
"""Return a tuple of kind, executable, stat_value for a file.
471
471
return self.path2id(path) is not None
473
473
def find_related_paths_across_trees(self, paths, trees=[],
474
require_versioned=True):
474
require_versioned=True):
475
475
"""Find related paths in tree corresponding to specified filenames in any
476
476
of `lookup_trees`.
583
583
prefs = next(self.iter_search_rules([path], filter_pref_names))
584
584
stk = filters._get_filter_stack_for(prefs)
585
585
if 'filters' in debug.debug_flags:
586
trace.note(gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
587
gettext("*** {0} content-filter: {1} => {2!r}").format(path, prefs, stk))
589
590
def _content_filter_stack_provider(self):
597
598
if self.supports_content_filtering():
598
599
return lambda path, file_id: \
599
self._content_filter_stack(path)
600
self._content_filter_stack(path)
603
604
def iter_search_rules(self, path_names, pref_names=None,
604
_default_searcher=None):
605
_default_searcher=None):
605
606
"""Find the preferences for filenames in a tree.
607
608
:param path_names: an iterable of paths to find attributes for.
640
641
from .archive import create_archive
641
642
with self.lock_read():
642
643
return create_archive(format, self, name, root,
643
subdir, force_mtime=force_mtime)
644
subdir, force_mtime=force_mtime)
646
647
def versionable_kind(cls, kind):
726
727
changed_content = True
727
728
elif source_kind == 'symlink':
728
729
if (self.source.get_symlink_target(source_path) !=
729
self.target.get_symlink_target(target_path)):
730
self.target.get_symlink_target(target_path)):
730
731
changed_content = True
731
732
elif source_kind == 'tree-reference':
732
733
if (self.source.get_reference_revision(source_path)
733
!= self.target.get_reference_revision(target_path)):
734
!= self.target.get_reference_revision(target_path)):
734
735
changed_content = True
735
736
parent = (source_parent, target_parent)
736
737
name = (source_name, target_name)
737
738
executable = (source_executable, target_executable)
738
if (changed_content is not False or versioned[0] != versioned[1]
739
or parent[0] != parent[1] or name[0] != name[1] or
740
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]):
745
746
versioned, parent, name, kind, executable), changes
747
748
def compare(self, want_unchanged=False, specific_files=None,
748
extra_trees=None, require_versioned=False, include_root=False,
749
want_unversioned=False):
749
extra_trees=None, require_versioned=False, include_root=False,
750
want_unversioned=False):
750
751
"""Return the changes from source to target.
752
753
:return: A TreeDelta.
768
769
trees = trees + tuple(extra_trees)
769
770
with self.lock_read():
770
771
return delta._compare_trees(self.source, self.target, want_unchanged,
771
specific_files, include_root, extra_trees=extra_trees,
772
require_versioned=require_versioned,
773
want_unversioned=want_unversioned)
772
specific_files, include_root, extra_trees=extra_trees,
773
require_versioned=require_versioned,
774
want_unversioned=want_unversioned)
775
776
def iter_changes(self, include_unchanged=False,
776
specific_files=None, pb=None, extra_trees=[],
777
require_versioned=True, want_unversioned=False):
777
specific_files=None, pb=None, extra_trees=[],
778
require_versioned=True, want_unversioned=False):
778
779
"""Generate an iterator of changes between trees.
780
781
A tuple is returned:
820
821
source_specific_files = []
822
823
target_specific_files = self.target.find_related_paths_across_trees(
823
specific_files, [self.source] + extra_trees,
824
require_versioned=require_versioned)
824
specific_files, [self.source] + extra_trees,
825
require_versioned=require_versioned)
825
826
source_specific_files = self.source.find_related_paths_across_trees(
826
specific_files, [self.target] + extra_trees,
827
require_versioned=require_versioned)
827
specific_files, [self.target] + extra_trees,
828
require_versioned=require_versioned)
828
829
if specific_files is not None:
829
830
# reparented or added entries must have their parents included
830
831
# so that valid deltas can be created. The seen_parents set
836
837
seen_dirs = set()
837
838
if want_unversioned:
838
839
all_unversioned = sorted([(p.split('/'), p) for p in
840
if specific_files is None or
841
osutils.is_inside_any(specific_files, p)])
841
if specific_files is None or
842
osutils.is_inside_any(specific_files, p)])
842
843
all_unversioned = collections.deque(all_unversioned)
844
845
all_unversioned = collections.deque()
856
857
fake_entry = TreeFile()
857
858
for target_path, target_entry in to_entries_by_dir:
858
859
while (all_unversioned and
859
all_unversioned[0][0] < target_path.split('/')):
860
all_unversioned[0][0] < target_path.split('/')):
860
861
unversioned_path = all_unversioned.popleft()
861
862
target_kind, target_executable, target_stat = \
862
self.target._comparison_data(fake_entry, unversioned_path[1])
863
self.target._comparison_data(
864
fake_entry, unversioned_path[1])
863
865
yield (None, (None, unversioned_path[1]), True, (False, False),
865
(None, unversioned_path[0][-1]),
867
(None, target_executable))
867
(None, unversioned_path[0][-1]),
869
(None, target_executable))
868
870
source_path, source_entry = from_data.get(target_entry.file_id,
870
872
result, changes = self._changes_from_entries(source_entry,
871
target_entry, source_path=source_path, target_path=target_path)
873
target_entry, source_path=source_path, target_path=target_path)
872
874
to_paths[result[0]] = result[1][1]
897
899
to_kind, to_executable, to_stat = \
898
900
self.target._comparison_data(fake_entry, unversioned_path[1])
899
901
yield (None, (None, unversioned_path[1]), True, (False, False),
901
(None, unversioned_path[0][-1]),
903
(None, to_executable))
903
(None, unversioned_path[0][-1]),
905
(None, to_executable))
904
906
# Yield all remaining source paths
905
907
for path, from_entry in from_entries_by_dir:
906
908
file_id = from_entry.file_id
926
928
changed_file_ids = set(changed_file_ids)
927
929
if specific_files is not None:
928
930
for result in self._handle_precise_ids(precise_file_ids,
932
934
def _get_entry(self, tree, path):
949
951
def _handle_precise_ids(self, precise_file_ids, changed_file_ids,
950
discarded_changes=None):
952
discarded_changes=None):
951
953
"""Fill out a partial iter_changes to be consistent.
953
955
:param precise_file_ids: The file ids of parents that were seen during
1002
1004
source_path = None
1003
1005
source_entry = None
1005
source_entry = self._get_entry(self.source, source_path)
1007
source_entry = self._get_entry(
1008
self.source, source_path)
1007
1010
target_path = self.target.id2path(file_id)
1008
1011
except errors.NoSuchId:
1009
1012
target_path = None
1010
1013
target_entry = None
1012
target_entry = self._get_entry(self.target, target_path)
1015
target_entry = self._get_entry(
1016
self.target, target_path)
1013
1017
result, changes = self._changes_from_entries(
1014
1018
source_entry, target_entry, source_path, target_path)
1024
1028
# to be included.
1025
1029
if source_entry is None:
1026
1030
# Reusing a discarded change.
1027
source_entry = self._get_entry(self.source, result[1][0])
1031
source_entry = self._get_entry(
1032
self.source, result[1][0])
1028
1033
precise_file_ids.update(
1030
for child in self.source.iter_child_entries(result[1][0]))
1035
for child in self.source.iter_child_entries(result[1][0]))
1031
1036
changed_file_ids.add(result[0])
1050
1055
with self.lock_read():
1051
1056
source_verifier_kind, source_verifier_data = (
1052
self.source.get_file_verifier(source_path, source_stat))
1057
self.source.get_file_verifier(source_path, source_stat))
1053
1058
target_verifier_kind, target_verifier_data = (
1054
1059
self.target.get_file_verifier(
1055
1060
target_path, target_stat))
1058
1063
# Fall back to SHA1 for now
1059
1064
if source_verifier_kind != "SHA1":
1060
1065
source_sha1 = self.source.get_file_sha1(
1061
source_path, source_file_id, source_stat)
1066
source_path, source_file_id, source_stat)
1063
1068
source_sha1 = source_verifier_data
1064
1069
if target_verifier_kind != "SHA1":
1065
1070
target_sha1 = self.target.get_file_sha1(
1066
target_path, target_file_id, target_stat)
1071
target_path, target_file_id, target_stat)
1068
1073
target_sha1 = target_verifier_data
1069
1074
return (source_sha1 == target_sha1)
1071
1077
InterTree.register_optimiser(InterTree)
1295
1301
other_extra.pop(file_id)
1296
1302
other_values = [(None, None)] * idx
1297
1303
other_values.append((other_path, other_ie))
1298
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:]):
1299
1305
alt_idx = alt_idx + idx + 1
1300
1306
alt_extra = self._others_extra[alt_idx]
1301
1307
alt_tree = self._other_trees[alt_idx]
1302
1308
other_values.append(self._lookup_by_file_id(
1303
alt_extra, alt_tree, file_id))
1309
alt_extra, alt_tree, file_id))
1304
1310
yield other_path, file_id, None, other_values