21
21
from cStringIO import StringIO
24
from bzrlib.trace import mutter, note
25
24
from bzrlib.errors import BzrError, BzrCheckError
25
from bzrlib import errors
26
26
from bzrlib.inventory import Inventory
27
from bzrlib.osutils import appendpath, fingerprint_file
27
from bzrlib.osutils import fingerprint_file
28
import bzrlib.revision
29
from bzrlib.trace import mutter, note
29
31
class Tree(object):
30
32
"""Abstract file tree.
48
50
trees or versioned trees.
54
"""Get a list of the conflicts in the tree.
56
Each conflict is an instance of bzrlib.conflicts.Conflict.
60
def get_parent_ids(self):
61
"""Get the parent ids for this tree.
63
:return: a list of parent ids. [] is returned to indicate
64
a tree with no parents.
65
:raises: BzrError if the parents are not known.
67
raise NotImplementedError(self.get_parent_ids)
51
69
def has_filename(self, filename):
52
70
"""True if the tree has given filename."""
53
71
raise NotImplementedError()
136
161
def __init__(self, branch, inv, revision_id):
137
self._branch = branch
162
# for compatability the 'branch' parameter has not been renamed to
163
# repository at this point. However, we should change RevisionTree's
164
# construction to always be via Repository and not via direct
165
# construction - this will mean that we can change the constructor
166
# with much less chance of breaking client code.
167
self._repository = branch
138
168
self._weave_store = branch.weave_store
139
169
self._inventory = inv
140
170
self._revision_id = revision_id
172
def get_parent_ids(self):
173
"""See Tree.get_parent_ids.
175
A RevisionTree's parents match the revision graph.
177
parent_ids = self._repository.get_revision(self._revision_id).parent_ids
180
def get_revision_id(self):
181
"""Return the revision id associated with this tree."""
182
return self._revision_id
142
184
def get_weave(self, file_id):
143
185
return self._weave_store.get_weave(file_id,
144
self._branch.get_transaction())
186
self._repository.get_transaction())
146
188
def get_file_lines(self, file_id):
147
189
ie = self._inventory[file_id]
157
199
def get_file_size(self, file_id):
158
200
return self._inventory[file_id].text_size
160
def get_file_sha1(self, file_id):
202
def get_file_sha1(self, file_id, path=None):
161
203
ie = self._inventory[file_id]
162
204
if ie.kind == "file":
163
205
return ie.text_sha1
165
def is_executable(self, file_id):
208
def get_file_mtime(self, file_id, path=None):
209
ie = self._inventory[file_id]
210
revision = self._repository.get_revision(ie.revision)
211
return revision.timestamp
213
def is_executable(self, file_id, path=None):
166
214
ie = self._inventory[file_id]
167
215
if ie.kind != "file":
184
232
return self._inventory[file_id].kind
186
234
def lock_read(self):
187
self._branch.lock_read()
235
self._repository.lock_read()
189
237
def unlock(self):
190
self._branch.unlock()
238
self._repository.unlock()
193
241
class EmptyTree(Tree):
194
243
def __init__(self):
195
244
self._inventory = Inventory()
246
def get_parent_ids(self):
247
"""See Tree.get_parent_ids.
249
An EmptyTree always has NULL_REVISION as the only parent.
197
253
def get_symlink_target(self, file_id):
210
266
def __contains__(self, file_id):
211
267
return file_id in self._inventory
213
def get_file_sha1(self, file_id):
269
def get_file_sha1(self, file_id, path=None):
214
270
assert self._inventory[file_id].kind == "root_directory"
280
336
yield (old_name, new_name)
339
def find_ids_across_trees(filenames, trees, require_versioned=True):
340
"""Find the ids corresponding to specified filenames.
342
All matches in all trees will be used, and all children of matched
343
directories will be used.
345
:param filenames: The filenames to find file_ids for
346
:param trees: The trees to find file_ids within
347
:param require_versioned: if true, all specified filenames must occur in
349
:return: a set of file ids for the specified filenames and their children.
353
specified_ids = _find_filename_ids_across_trees(filenames, trees,
355
return _find_children_across_trees(specified_ids, trees)
358
def _find_filename_ids_across_trees(filenames, trees, require_versioned):
359
"""Find the ids corresponding to specified filenames.
361
All matches in all trees will be used.
363
:param filenames: The filenames to find file_ids for
364
:param trees: The trees to find file_ids within
365
:param require_versioned: if true, all specified filenames must occur in
367
:return: a set of file ids for the specified filenames
370
interesting_ids = set()
371
for tree_path in filenames:
374
file_id = tree.inventory.path2id(tree_path)
375
if file_id is not None:
376
interesting_ids.add(file_id)
379
not_versioned.append(tree_path)
380
if len(not_versioned) > 0 and require_versioned:
381
raise errors.PathsNotVersionedError(not_versioned)
382
return interesting_ids
385
def _find_children_across_trees(specified_ids, trees):
386
"""Return a set including specified ids and their children
388
All matches in all trees will be used.
390
:param trees: The trees to find file_ids within
391
:return: a set containing all specified ids and their children
393
interesting_ids = set(specified_ids)
394
pending = interesting_ids
395
# now handle children of interesting ids
396
# we loop so that we handle all children of each id in both trees
397
while len(pending) > 0:
399
for file_id in pending:
401
if file_id not in tree:
403
entry = tree.inventory[file_id]
404
for child in getattr(entry, 'children', {}).itervalues():
405
if child.file_id not in interesting_ids:
406
new_pending.add(child.file_id)
407
interesting_ids.update(new_pending)
408
pending = new_pending
409
return interesting_ids