59
54
trees or versioned trees.
62
def has_versioned_directories(self):
63
"""Whether this tree can contain explicitly versioned directories.
65
This defaults to True, but some implementations may want to override
70
57
def changes_from(self, other, want_unchanged=False, specific_files=None,
71
58
extra_trees=None, require_versioned=False, include_root=False,
72
59
want_unversioned=False):
136
123
def has_id(self, file_id):
137
124
raise NotImplementedError(self.has_id)
139
@deprecated_method(deprecated_in((2, 4, 0)))
140
126
def __contains__(self, file_id):
141
127
return self.has_id(file_id)
141
"""Yield all file ids in this tree."""
142
raise NotImplementedError(self.__iter__)
154
144
def all_file_ids(self):
155
145
"""Iterate through all file ids, including ids for missing files."""
156
raise NotImplementedError(self.all_file_ids)
146
return set(self.inventory)
158
148
def id2path(self, file_id):
159
149
"""Return the path for a file id.
186
176
The yield order (ignoring root) would be::
188
177
a, f, a/b, a/d, a/b/c, a/d/e, f/g
190
179
:param yield_parents: If True, yield the parents from the root leading
194
183
raise NotImplementedError(self.iter_entries_by_dir)
196
def list_files(self, include_root=False, from_dir=None, recursive=True):
197
"""List all files in this tree.
199
:param include_root: Whether to include the entry for the tree root
200
:param from_dir: Directory under which to list files
201
:param recursive: Whether to list files recursively
202
:return: iterator over tuples of (path, versioned, kind, file_id,
205
raise NotImplementedError(self.list_files)
207
185
def iter_references(self):
208
186
if self.supports_tree_reference():
209
187
for path, entry in self.iter_entries_by_dir():
287
265
:param file_id: The file_id of the file.
288
266
:param path: The path of the file.
290
267
If both file_id and path are supplied, an implementation may use
293
:returns: A single byte string for the whole file.
295
270
my_file = self.get_file(file_id, path)
304
279
:param file_id: The file_id of the file.
305
280
:param path: The path of the file.
307
281
If both file_id and path are supplied, an implementation may use
310
284
return osutils.split_lines(self.get_file_text(file_id, path))
312
def get_file_verifier(self, file_id, path=None, stat_value=None):
313
"""Return a verifier for a file.
315
The default implementation returns a sha1.
317
:param file_id: The handle for this file.
318
:param path: The path that this file can be found at.
319
These must point to the same object.
320
:param stat_value: Optional stat value for the object
321
:return: Tuple with verifier name and verifier data
323
return ("SHA1", self.get_file_sha1(file_id, path=path,
324
stat_value=stat_value))
326
def get_file_sha1(self, file_id, path=None, stat_value=None):
327
"""Return the SHA1 file for a file.
329
:note: callers should use get_file_verifier instead
330
where possible, as the underlying repository implementation may
331
have quicker access to a non-sha1 verifier.
333
:param file_id: The handle for this file.
334
:param path: The path that this file can be found at.
335
These must point to the same object.
336
:param stat_value: Optional stat value for the object
338
raise NotImplementedError(self.get_file_sha1)
340
286
def get_file_mtime(self, file_id, path=None):
341
287
"""Return the modification time for a file.
358
304
def get_file_by_path(self, path):
359
305
raise NotImplementedError(self.get_file_by_path)
361
def is_executable(self, file_id, path=None):
362
"""Check if a file is executable.
364
:param file_id: The handle for this file.
365
:param path: The path that this file can be found at.
366
These must point to the same object.
368
raise NotImplementedError(self.is_executable)
370
307
def iter_files_bytes(self, desired_files):
371
308
"""Iterate through file contents.
393
330
cur_file = (self.get_file_text(file_id),)
394
331
yield identifier, cur_file
396
def get_symlink_target(self, file_id, path=None):
333
def get_symlink_target(self, file_id):
397
334
"""Get the target for a given file_id.
399
336
It is assumed that the caller already knows that file_id is referencing
401
338
:param file_id: Handle for the symlink entry.
402
:param path: The path of the file.
403
If both file_id and path are supplied, an implementation may use
405
339
:return: The path the symlink points to.
407
341
raise NotImplementedError(self.get_symlink_target)
409
344
def get_root_id(self):
410
345
"""Return the file_id for the root of this tree."""
411
346
raise NotImplementedError(self.get_root_id)
469
404
except errors.NoSuchRevisionInTree:
470
405
yield self.repository.revision_tree(revision_id)
408
def _file_revision(revision_tree, file_id):
409
"""Determine the revision associated with a file in a given tree."""
410
# FIXME: Shouldn't this be a RevisionTree method?
411
revision_tree.lock_read()
413
return revision_tree.inventory[file_id].revision
415
revision_tree.unlock()
472
417
def _get_file_revision(self, file_id, vf, tree_revision):
473
418
"""Ensure that file_id, tree_revision is in vf to plan the merge."""
475
420
if getattr(self, '_repository', None) is None:
476
421
last_revision = tree_revision
477
parent_keys = [(file_id, t.get_file_revision(file_id)) for t in
422
parent_keys = [(file_id, self._file_revision(t, file_id)) for t in
478
423
self._iter_parent_trees()]
479
424
vf.add_lines((file_id, last_revision), parent_keys,
480
425
self.get_file_lines(file_id))
481
426
repo = self.branch.repository
482
427
base_vf = repo.texts
484
last_revision = self.get_file_revision(file_id)
429
last_revision = self._file_revision(self, file_id)
485
430
base_vf = self._repository.texts
486
431
if base_vf not in vf.fallback_versionedfiles:
487
432
vf.fallback_versionedfiles.append(base_vf)
632
577
prefs = self.iter_search_rules([path], filter_pref_names).next()
633
578
stk = filters._get_filter_stack_for(prefs)
634
579
if 'filters' in debug.debug_flags:
635
trace.note(gettext("*** {0} content-filter: {1} => {2!r}").format(path,prefs,stk))
580
trace.note("*** %s content-filter: %s => %r" % (path,prefs,stk))
638
583
def _content_filter_stack_provider(self):
687
632
It is possible for trees to contain files that are not described
688
633
in their inventory or vice versa; for this use `filenames()`.
690
Subclasses should set the _inventory attribute, which is considered
691
private to external API users.
694
636
def get_canonical_inventory_paths(self, paths):
793
735
def has_or_had_id(self, file_id):
794
736
return self.inventory.has_id(file_id)
796
def all_file_ids(self):
797
return set(self.inventory)
799
@deprecated_method(deprecated_in((2, 4, 0)))
800
738
def __iter__(self):
801
739
return iter(self.inventory)
831
769
return self.get_file(self._inventory.path2id(path), path)
772
######################################################################
775
# TODO: Merge these two functions into a single one that can operate
776
# on either a whole tree or a set of files.
778
# TODO: Return the diff in order by filename, not by category or in
779
# random order. Can probably be done by lock-stepping through the
780
# filenames from both trees.
783
def file_status(filename, old_tree, new_tree):
784
"""Return single-letter status, old and new names for a file.
786
The complexity here is in deciding how to represent renames;
787
many complex cases are possible.
789
old_inv = old_tree.inventory
790
new_inv = new_tree.inventory
791
new_id = new_inv.path2id(filename)
792
old_id = old_inv.path2id(filename)
794
if not new_id and not old_id:
795
# easy: doesn't exist in either; not versioned at all
796
if new_tree.is_ignored(filename):
797
return 'I', None, None
799
return '?', None, None
801
# There is now a file of this name, great.
804
# There is no longer a file of this name, but we can describe
805
# what happened to the file that used to have
806
# this name. There are two possibilities: either it was
807
# deleted entirely, or renamed.
808
if new_inv.has_id(old_id):
809
return 'X', old_inv.id2path(old_id), new_inv.id2path(old_id)
811
return 'D', old_inv.id2path(old_id), None
813
# if the file_id is new in this revision, it is added
814
if new_id and not old_inv.has_id(new_id):
817
# if there used to be a file of this name, but that ID has now
818
# disappeared, it is deleted
819
if old_id and not new_inv.has_id(old_id):
834
825
def find_ids_across_trees(filenames, trees, require_versioned=True):
835
826
"""Find the ids corresponding to specified filenames.
927
def is_compatible(kls, source, target):
928
# The default implementation is naive and uses the public API, so
929
# it works for all trees.
932
917
def _changes_from_entries(self, source_entry, target_entry,
933
918
source_path=None, target_path=None):
934
919
"""Generate a iter_changes tuple between source_entry and target_entry.
982
967
if source_kind != target_kind:
983
968
changed_content = True
984
969
elif source_kind == 'file':
985
if not self.file_content_matches(file_id, file_id, source_path,
986
target_path, source_stat, target_stat):
970
if (self.source.get_file_sha1(file_id, source_path, source_stat) !=
971
self.target.get_file_sha1(file_id, target_path, target_stat)):
987
972
changed_content = True
988
973
elif source_kind == 'symlink':
989
974
if (self.source.get_symlink_target(file_id) !=
1302
1287
changed_file_ids.add(result[0])
1306
def file_content_matches(self, source_file_id, target_file_id,
1307
source_path=None, target_path=None, source_stat=None, target_stat=None):
1308
"""Check if two files are the same in the source and target trees.
1310
This only checks that the contents of the files are the same,
1311
it does not touch anything else.
1313
:param source_file_id: File id of the file in the source tree
1314
:param target_file_id: File id of the file in the target tree
1315
:param source_path: Path of the file in the source tree
1316
:param target_path: Path of the file in the target tree
1317
:param source_stat: Optional stat value of the file in the source tree
1318
:param target_stat: Optional stat value of the file in the target tree
1319
:return: Boolean indicating whether the files have the same contents
1321
source_verifier_kind, source_verifier_data = self.source.get_file_verifier(
1322
source_file_id, source_path, source_stat)
1323
target_verifier_kind, target_verifier_data = self.target.get_file_verifier(
1324
target_file_id, target_path, target_stat)
1325
if source_verifier_kind == target_verifier_kind:
1326
return (source_verifier_data == target_verifier_data)
1327
# Fall back to SHA1 for now
1328
if source_verifier_kind != "SHA1":
1329
source_sha1 = self.source.get_file_sha1(source_file_id,
1330
source_path, source_stat)
1332
source_sha1 = source_verifier_data
1333
if target_verifier_kind != "SHA1":
1334
target_sha1 = self.target.get_file_sha1(target_file_id,
1335
target_path, target_stat)
1337
target_sha1 = target_verifier_data
1338
return (source_sha1 == target_sha1)
1340
InterTree.register_optimiser(InterTree)
1343
1291
class MultiWalker(object):
1344
1292
"""Walk multiple trees simultaneously, getting combined results."""