121
123
def has_filename(self, filename):
122
124
"""True if the tree has given filename."""
123
raise NotImplementedError()
125
raise NotImplementedError(self.has_filename)
125
127
def has_id(self, file_id):
126
file_id = osutils.safe_file_id(file_id)
127
128
return self.inventory.has_id(file_id)
129
130
__contains__ = has_id
131
132
def has_or_had_id(self, file_id):
132
file_id = osutils.safe_file_id(file_id)
133
133
if file_id == self.inventory.root.file_id:
135
135
return self.inventory.has_id(file_id)
186
185
raise NotImplementedError("Tree subclass %s must implement kind"
187
186
% self.__class__.__name__)
188
def path_content_summary(self, path):
189
"""Get a summary of the information about path.
191
:param path: A relative path within the tree.
192
:return: A tuple containing kind, size, exec, sha1-or-link.
193
Kind is always present (see tree.kind()).
194
size is present if kind is file, None otherwise.
195
exec is None unless kind is file and the platform supports the 'x'
197
sha1-or-link is the link target if kind is symlink, or the sha1 if
198
it can be obtained without reading the file.
200
raise NotImplementedError(self.path_content_summary)
189
202
def get_reference_revision(self, file_id, path=None):
190
203
raise NotImplementedError("Tree subclass %s must implement "
191
204
"get_reference_revision"
208
221
def _get_inventory(self):
209
222
return self._inventory
211
def get_file(self, file_id):
212
"""Return a file object for the file file_id in the tree."""
224
def get_file(self, file_id, path=None):
225
"""Return a file object for the file file_id in the tree.
227
If both file_id and path are defined, it is implementation defined as
228
to which one is used.
213
230
raise NotImplementedError(self.get_file)
215
232
def get_file_mtime(self, file_id, path=None):
222
239
raise NotImplementedError(self.get_file_mtime)
224
241
def get_file_by_path(self, path):
225
return self.get_file(self._inventory.path2id(path))
242
return self.get_file(self._inventory.path2id(path), path)
244
def iter_files_bytes(self, desired_files):
245
"""Iterate through file contents.
247
Files will not necessarily be returned in the order they occur in
248
desired_files. No specific order is guaranteed.
250
Yields pairs of identifier, bytes_iterator. identifier is an opaque
251
value supplied by the caller as part of desired_files. It should
252
uniquely identify the file version in the caller's context. (Examples:
253
an index number or a TreeTransform trans_id.)
255
bytes_iterator is an iterable of bytestrings for the file. The
256
kind of iterable and length of the bytestrings are unspecified, but for
257
this implementation, it is a tuple containing a single bytestring with
258
the complete text of the file.
260
:param desired_files: a list of (file_id, identifier) pairs
262
for file_id, identifier in desired_files:
263
# We wrap the string in a tuple so that we can return an iterable
264
# of bytestrings. (Technically, a bytestring is also an iterable
265
# of bytestrings, but iterating through each character is not
267
cur_file = (self.get_file_text(file_id),)
268
yield identifier, cur_file
227
270
def get_symlink_target(self, file_id):
228
271
"""Get the target for a given file_id.
245
292
raise NotImplementedError(self.annotate_iter)
294
def _get_plan_merge_data(self, file_id, other, base):
295
from bzrlib import merge, versionedfile
296
vf = versionedfile._PlanMergeVersionedFile(file_id)
297
last_revision_a = self._get_file_revision(file_id, vf, 'this:')
298
last_revision_b = other._get_file_revision(file_id, vf, 'other:')
300
last_revision_base = None
302
last_revision_base = base._get_file_revision(file_id, vf, 'base:')
303
return vf, last_revision_a, last_revision_b, last_revision_base
305
def plan_file_merge(self, file_id, other, base=None):
306
"""Generate a merge plan based on annotations.
308
If the file contains uncommitted changes in this tree, they will be
309
attributed to the 'current:' pseudo-revision. If the file contains
310
uncommitted changes in the other tree, they will be assigned to the
311
'other:' pseudo-revision.
313
data = self._get_plan_merge_data(file_id, other, base)
314
vf, last_revision_a, last_revision_b, last_revision_base = data
315
return vf.plan_merge(last_revision_a, last_revision_b,
318
def plan_file_lca_merge(self, file_id, other, base=None):
319
"""Generate a merge plan based lca-newness.
321
If the file contains uncommitted changes in this tree, they will be
322
attributed to the 'current:' pseudo-revision. If the file contains
323
uncommitted changes in the other tree, they will be assigned to the
324
'other:' pseudo-revision.
326
data = self._get_plan_merge_data(file_id, other, base)
327
vf, last_revision_a, last_revision_b, last_revision_base = data
328
return vf.plan_lca_merge(last_revision_a, last_revision_b,
331
def _get_file_revision(self, file_id, vf, tree_revision):
332
def file_revision(revision_tree):
333
revision_tree.lock_read()
335
return revision_tree.inventory[file_id].revision
337
revision_tree.unlock()
339
def iter_parent_trees():
340
for revision_id in self.get_parent_ids():
342
yield self.revision_tree(revision_id)
344
yield self.repository.revision_tree(revision_id)
346
if getattr(self, '_get_weave', None) is None:
347
last_revision = tree_revision
348
parent_revisions = [file_revision(t) for t in iter_parent_trees()]
349
vf.add_lines(last_revision, parent_revisions,
350
self.get_file(file_id).readlines())
351
repo = self.branch.repository
352
transaction = repo.get_transaction()
353
base_vf = repo.weave_store.get_weave(file_id, transaction)
355
last_revision = file_revision(self)
356
base_vf = self._get_weave(file_id)
357
vf.fallback_versionedfiles.append(base_vf)
247
360
inventory = property(_get_inventory,
248
361
doc="Inventory of this Tree")
274
387
def paths2ids(self, paths, trees=[], require_versioned=True):
275
388
"""Return all the ids that can be reached by walking from paths.
277
Each path is looked up in each this tree and any extras provided in
390
Each path is looked up in this tree and any extras provided in
278
391
trees, and this is repeated recursively: the children in an extra tree
279
392
of a directory that has been renamed under a provided path in this tree
280
are all returned, even if none exist until a provided path in this
393
are all returned, even if none exist under a provided path in this
281
394
tree, and vice versa.
283
396
:param paths: An iterable of paths to start converting to ids from.
629
740
lookup_trees = [self.source]
631
742
lookup_trees.extend(extra_trees)
632
specific_file_ids = self.target.paths2ids(specific_files,
633
lookup_trees, require_versioned=require_versioned)
743
if specific_files == []:
744
specific_file_ids = []
746
specific_file_ids = self.target.paths2ids(specific_files,
747
lookup_trees, require_versioned=require_versioned)
634
748
if want_unversioned:
635
all_unversioned = sorted([(p.split('/'), p) for p in self.target.extras()
636
if not specific_files or
749
all_unversioned = sorted([(p.split('/'), p) for p in
751
if specific_files is None or
637
752
osutils.is_inside_any(specific_files, p)])
638
753
all_unversioned = deque(all_unversioned)