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()
108
126
def lock_read(self):
130
"""What files are present in this tree and unknown.
132
:return: an iterator over the unknown files.
111
136
def unlock(self):
139
def filter_unversioned_files(self, paths):
140
"""Filter out paths that are not versioned.
142
:return: set of paths.
144
# NB: we specifically *don't* call self.has_filename, because for
145
# WorkingTrees that can indicate files that exist on disk but that
147
pred = self.inventory.has_filename
148
return set((p for p in paths if not pred(p)))
115
151
class RevisionTree(Tree):
125
161
def __init__(self, branch, inv, revision_id):
126
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
127
168
self._weave_store = branch.weave_store
128
169
self._inventory = inv
129
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
131
184
def get_weave(self, file_id):
132
import bzrlib.transactions as transactions
133
185
return self._weave_store.get_weave(file_id,
134
self._branch.get_transaction())
136
def get_weave_prelude(self, file_id):
137
import bzrlib.transactions as transactions
138
return self._weave_store.get_weave_prelude(file_id,
139
self._branch.get_transaction())
186
self._repository.get_transaction())
141
188
def get_file_lines(self, file_id):
142
189
ie = self._inventory[file_id]
143
190
weave = self.get_weave(file_id)
144
return weave.get(ie.revision)
191
return weave.get_lines(ie.revision)
146
193
def get_file_text(self, file_id):
147
194
return ''.join(self.get_file_lines(file_id))
152
199
def get_file_size(self, file_id):
153
200
return self._inventory[file_id].text_size
155
def get_file_sha1(self, file_id):
202
def get_file_sha1(self, file_id, path=None):
156
203
ie = self._inventory[file_id]
157
204
if ie.kind == "file":
158
205
return ie.text_sha1
160
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):
161
214
ie = self._inventory[file_id]
162
215
if ie.kind != "file":
179
232
return self._inventory[file_id].kind
181
234
def lock_read(self):
182
self._branch.lock_read()
235
self._repository.lock_read()
184
237
def unlock(self):
185
self._branch.unlock()
238
self._repository.unlock()
188
241
class EmptyTree(Tree):
189
243
def __init__(self):
190
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.
192
253
def get_symlink_target(self, file_id):
205
266
def __contains__(self, file_id):
206
267
return file_id in self._inventory
208
def get_file_sha1(self, file_id):
269
def get_file_sha1(self, file_id, path=None):
209
270
assert self._inventory[file_id].kind == "root_directory"
275
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