1
# Copyright (C) 2005 Canonical Ltd
1
# Copyright (C) 2005, 2009 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tree classes, representing directory at point in time.
60
62
Trees can be compared, etc, regardless of whether they are working
61
63
trees or versioned trees.
64
66
def changes_from(self, other, want_unchanged=False, specific_files=None,
65
67
extra_trees=None, require_versioned=False, include_root=False,
66
68
want_unversioned=False):
80
82
a PathsNotVersionedError will be thrown.
81
83
:param want_unversioned: Scan for unversioned paths.
83
The comparison will be performed by an InterTree object looked up on
85
The comparison will be performed by an InterTree object looked up on
86
88
# Martin observes that Tree.changes_from returns a TreeDelta and this
95
97
want_unversioned=want_unversioned,
98
@symbol_versioning.deprecated_method(symbol_versioning.one_three)
99
def _iter_changes(self, *args, **kwargs):
100
return self.iter_changes(*args, **kwargs)
102
100
def iter_changes(self, from_tree, include_unchanged=False,
103
101
specific_files=None, pb=None, extra_trees=None,
104
102
require_versioned=True, want_unversioned=False):
105
103
intertree = InterTree.get(from_tree, self)
106
104
return intertree.iter_changes(include_unchanged, specific_files, pb,
107
105
extra_trees, require_versioned, want_unversioned=want_unversioned)
109
107
def conflicts(self):
110
108
"""Get a list of the conflicts in the tree.
120
118
def get_parent_ids(self):
121
"""Get the parent ids for this tree.
119
"""Get the parent ids for this tree.
123
121
:return: a list of parent ids. [] is returned to indicate
124
122
a tree with no parents.
125
123
:raises: BzrError if the parents are not known.
127
125
raise NotImplementedError(self.get_parent_ids)
129
127
def has_filename(self, filename):
130
128
"""True if the tree has given filename."""
131
129
raise NotImplementedError(self.has_filename)
166
164
def is_control_filename(self, filename):
167
165
"""True if filename is the name of a control file in this tree.
169
167
:param filename: A filename within the tree. This is a relative path
170
168
from the root of this tree.
223
221
def path_content_summary(self, path):
224
222
"""Get a summary of the information about path.
226
224
:param path: A relative path within the tree.
227
225
:return: A tuple containing kind, size, exec, sha1-or-link.
228
226
Kind is always present (see tree.kind()).
256
254
def _get_inventory(self):
257
255
return self._inventory
259
257
def get_file(self, file_id, path=None):
260
258
"""Return a file object for the file file_id in the tree.
262
260
If both file_id and path are defined, it is implementation defined as
263
261
to which one is used.
501
499
def _check_retrieved(self, ie, f):
502
500
if not __debug__:
504
502
fp = fingerprint_file(f)
507
505
if ie.text_size is not None:
508
506
if ie.text_size != fp['size']:
509
507
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
525
523
def paths2ids(self, paths, trees=[], require_versioned=True):
526
524
"""Return all the ids that can be reached by walking from paths.
528
526
Each path is looked up in this tree and any extras provided in
529
527
trees, and this is repeated recursively: the children in an extra tree
530
528
of a directory that has been renamed under a provided path in this tree
546
544
for child in getattr(entry, 'children', {}).itervalues():
547
545
yield child.file_id
549
@symbol_versioning.deprecated_method(symbol_versioning.one_six)
550
def print_file(self, file_id):
551
"""Print file with id `file_id` to stdout."""
553
sys.stdout.write(self.get_file_text(file_id))
555
547
def lock_read(self):
561
553
The intention of this method is to allow access to possibly cached
562
554
tree data. Implementors of this method should raise NoSuchRevision if
563
the tree is not locally available, even if they could obtain the
564
tree via a repository or some other means. Callers are responsible
555
the tree is not locally available, even if they could obtain the
556
tree via a repository or some other means. Callers are responsible
565
557
for finding the ultimate source for a revision tree.
567
559
:param revision_id: The revision_id of the requested tree.
586
578
:return: set of paths.
588
580
# NB: we specifically *don't* call self.has_filename, because for
589
# WorkingTrees that can indicate files that exist on disk but that
581
# WorkingTrees that can indicate files that exist on disk but that
590
582
# are not versioned.
591
583
pred = self.inventory.has_filename
592
584
return set((p for p in paths if not pred(p)))
597
589
This yields all the data about the contents of a directory at a time.
598
590
After each directory has been yielded, if the caller has mutated the
599
591
list to exclude some directories, they are then not descended into.
601
593
The data yielded is of the form:
602
594
((directory-relpath, directory-path-from-root, directory-fileid),
603
[(relpath, basename, kind, lstat, path_from_tree_root, file_id,
595
[(relpath, basename, kind, lstat, path_from_tree_root, file_id,
604
596
versioned_kind), ...]),
605
597
- directory-relpath is the containing dirs relpath from prefix
606
598
- directory-path-from-root is the containing dirs path from /
613
605
- lstat is the stat data *if* the file was statted.
614
606
- path_from_tree_root is the path from the root of the tree.
615
607
- file_id is the file_id if the entry is versioned.
616
- versioned_kind is the kind of the file as last recorded in the
608
- versioned_kind is the kind of the file as last recorded in the
617
609
versioning system. If 'unknown' the file is not versioned.
618
610
One of 'kind' and 'versioned_kind' must not be 'unknown'.
627
619
def supports_content_filtering(self):
622
def _content_filter_stack(self, path=None, file_id=None):
623
"""The stack of content filters for a path if filtering is supported.
625
Readers will be applied in first-to-last order.
626
Writers will be applied in last-to-first order.
627
Either the path or the file-id needs to be provided.
629
:param path: path relative to the root of the tree
631
:param file_id: file_id or None if unknown
632
:return: the list of filters - [] if there are none
634
filter_pref_names = filters._get_registered_names()
635
if len(filter_pref_names) == 0:
638
path = self.id2path(file_id)
639
prefs = self.iter_search_rules([path], filter_pref_names).next()
640
stk = filters._get_filter_stack_for(prefs)
641
if 'filters' in debug.debug_flags:
642
note("*** %s content-filter: %s => %r" % (path,prefs,stk))
645
def _content_filter_stack_provider(self):
646
"""A function that returns a stack of ContentFilters.
648
The function takes a path (relative to the top of the tree) and a
649
file-id as parameters.
651
:return: None if content filtering is not supported by this tree.
653
if self.supports_content_filtering():
654
return lambda path, file_id: \
655
self._content_filter_stack(path, file_id)
630
659
def iter_search_rules(self, path_names, pref_names=None,
631
660
_default_searcher=rules._per_user_searcher):
632
661
"""Find the preferences for filenames in a tree.
657
class EmptyTree(Tree):
660
self._inventory = Inventory(root_id=None)
661
symbol_versioning.warn('EmptyTree is deprecated as of bzr 0.9 please'
662
' use repository.revision_tree instead.',
663
DeprecationWarning, stacklevel=2)
665
def get_parent_ids(self):
668
def get_symlink_target(self, file_id):
671
def has_filename(self, filename):
674
def kind(self, file_id):
677
def list_files(self, include_root=False):
680
def __contains__(self, file_id):
681
return (file_id in self._inventory)
683
def get_file_sha1(self, file_id, path=None, stat_value=None):
687
686
######################################################################
751
750
def find_ids_across_trees(filenames, trees, require_versioned=True):
752
751
"""Find the ids corresponding to specified filenames.
754
753
All matches in all trees will be used, and all children of matched
755
754
directories will be used.
771
770
def _find_ids_across_trees(filenames, trees, require_versioned):
772
771
"""Find the ids corresponding to specified filenames.
774
773
All matches in all trees will be used, but subdirectories are not scanned.
776
775
:param filenames: The filenames to find file_ids for
798
797
def _find_children_across_trees(specified_ids, trees):
799
798
"""Return a set including specified ids and their children.
801
800
All matches in all trees will be used.
803
802
:param trees: The trees to find file_ids within
804
:return: a set containing all specified ids and their children
803
:return: a set containing all specified ids and their children
806
805
interesting_ids = set(specified_ids)
807
806
pending = interesting_ids
931
929
specific_file_ids=specific_file_ids))
932
930
num_entries = len(from_entries_by_dir) + len(to_entries_by_dir)
934
# the unversioned path lookup only occurs on real trees - where there
932
# the unversioned path lookup only occurs on real trees - where there
935
933
# can be extras. So the fake_entry is solely used to look up
936
934
# executable it values when execute is not supported.
937
935
fake_entry = InventoryFile('unused', 'unused', 'unused')
971
969
if kind[0] != kind[1]:
972
970
changed_content = True
973
971
elif from_kind == 'file':
974
from_size = self.source._file_size(from_entry, from_stat)
975
to_size = self.target._file_size(to_entry, to_stat)
976
if from_size != to_size:
977
changed_content = True
978
elif (self.source.get_file_sha1(file_id, from_path, from_stat) !=
972
if (self.source.get_file_sha1(file_id, from_path, from_stat) !=
979
973
self.target.get_file_sha1(file_id, to_path, to_stat)):
980
974
changed_content = True
981
975
elif from_kind == 'symlink':
982
976
if (self.source.get_symlink_target(file_id) !=
983
977
self.target.get_symlink_target(file_id)):
984
978
changed_content = True
979
# XXX: Yes, the indentation below is wrong. But fixing it broke
980
# test_merge.TestMergerEntriesLCAOnDisk.
981
# test_nested_tree_subtree_renamed_and_modified. We'll wait for
982
# the fix from bzr.dev -- vila 2009026
985
983
elif from_kind == 'tree-reference':
986
984
if (self.source.get_reference_revision(file_id, from_path)
987
985
!= self.target.get_reference_revision(file_id, to_path)):
988
changed_content = True
986
changed_content = True
989
987
parent = (from_parent, to_entry.parent_id)
990
988
name = (from_name, to_entry.name)
991
989
executable = (from_executable, to_executable)
992
990
if pb is not None:
993
991
pb.update('comparing files', entry_count, num_entries)
994
992
if (changed_content is not False or versioned[0] != versioned[1]
995
or parent[0] != parent[1] or name[0] != name[1] or
993
or parent[0] != parent[1] or name[0] != name[1] or
996
994
executable[0] != executable[1] or include_unchanged):
997
995
yield (file_id, (from_path, to_path), changed_content,
998
996
versioned, parent, name, kind, executable)
1168
1166
def _walk_master_tree(self):
1169
1167
"""First pass, walk all trees in lock-step.
1171
1169
When we are done, all nodes in the master_tree will have been
1172
1170
processed. _other_walkers, _other_entries, and _others_extra will be
1173
1171
set on 'self' for future processing.