17
17
"""Tree classes, representing directory at point in time.
20
from osutils import pumpfile, appendpath, fingerprint_file
21
from cStringIO import StringIO
23
from bzrlib.trace import mutter, note
24
from bzrlib.errors import BzrError
24
from bzrlib.trace import mutter, note
25
from bzrlib.errors import BzrError, BzrCheckError
26
from bzrlib.inventory import Inventory
27
from bzrlib.osutils import appendpath, fingerprint_file
29
30
class Tree(object):
30
31
"""Abstract file tree.
68
64
def id2path(self, file_id):
69
65
return self.inventory.id2path(file_id)
71
def kind(self, file_id):
72
raise NotImplementedError("subclasses must implement kind")
74
67
def _get_inventory(self):
75
68
return self._inventory
77
def get_file_by_path(self, path):
78
return self.get_file(self._inventory.path2id(path))
80
70
inventory = property(_get_inventory,
81
71
doc="Inventory of this Tree")
83
73
def _check_retrieved(self, ie, f):
86
74
fp = fingerprint_file(f)
100
88
"store is probably damaged/corrupt"])
103
def print_file(self, file_id):
104
"""Print file with id `file_id` to stdout."""
91
def print_file(self, fileid):
92
"""Print file with id `fileid` to stdout."""
106
sys.stdout.write(self.get_file_text(file_id))
114
def filter_unversioned_files(self, paths):
115
"""Filter out paths that are not versioned.
117
:return: set of paths.
119
# NB: we specifically *don't* call self.has_filename, because for
120
# WorkingTrees that can indicate files that exist on disk but that
122
pred = self.inventory.has_filename
123
return set((p for p in paths if not pred(p)))
94
pumpfile(self.get_file(fileid), sys.stdout)
97
def export(self, dest, format='dir', root=None):
98
"""Export this tree."""
100
exporter = exporters[format]
102
from bzrlib.errors import BzrCommandError
103
raise BzrCommandError("export format %r not supported" % format)
104
exporter(self, dest, root)
126
108
class RevisionTree(Tree):
127
109
"""Tree viewing a previous revision.
133
115
or at least passing a description to the constructor.
136
def __init__(self, branch, inv, revision_id):
137
self._branch = branch
138
self._weave_store = branch.weave_store
118
def __init__(self, store, inv):
139
120
self._inventory = inv
140
self._revision_id = revision_id
142
def get_weave(self, file_id):
143
return self._weave_store.get_weave(file_id,
144
self._branch.get_transaction())
146
def get_file_lines(self, file_id):
147
ie = self._inventory[file_id]
148
weave = self.get_weave(file_id)
149
return weave.get_lines(ie.revision)
151
def get_file_text(self, file_id):
152
return ''.join(self.get_file_lines(file_id))
154
122
def get_file(self, file_id):
155
return StringIO(self.get_file_text(file_id))
123
ie = self._inventory[file_id]
124
f = self._store[ie.text_id]
125
mutter(" get fileid{%s} from %r" % (file_id, self))
126
self._check_retrieved(ie, f)
157
129
def get_file_size(self, file_id):
158
130
return self._inventory[file_id].text_size
160
132
def get_file_sha1(self, file_id):
161
133
ie = self._inventory[file_id]
162
if ie.kind == "file":
165
def is_executable(self, file_id):
166
ie = self._inventory[file_id]
167
if ie.kind != "file":
169
return self._inventory[file_id].executable
171
136
def has_filename(self, filename):
172
137
return bool(self.inventory.path2id(filename))
174
139
def list_files(self):
175
140
# The only files returned by this are those from the version
176
141
for path, entry in self.inventory.iter_entries():
177
yield path, 'V', entry.kind, entry.file_id, entry
179
def get_symlink_target(self, file_id):
180
ie = self._inventory[file_id]
181
return ie.symlink_target;
183
def kind(self, file_id):
184
return self._inventory[file_id].kind
187
self._branch.lock_read()
190
self._branch.unlock()
142
yield path, 'V', entry.kind, entry.file_id
193
145
class EmptyTree(Tree):
194
146
def __init__(self):
147
from bzrlib.inventory import Inventory
195
148
self._inventory = Inventory()
197
def get_symlink_target(self, file_id):
200
150
def has_filename(self, filename):
203
def kind(self, file_id):
204
assert self._inventory[file_id].kind == "root_directory"
205
return "root_directory"
207
153
def list_files(self):
154
if False: # just to make it a generator
210
def __contains__(self, file_id):
211
return file_id in self._inventory
213
def get_file_sha1(self, file_id):
214
assert self._inventory[file_id].kind == "root_directory"
218
159
######################################################################
225
######################################################################
228
def dir_exporter(tree, dest, root):
229
"""Export this tree to a new directory.
231
`dest` should not exist, and will be created holding the
232
contents of this tree.
234
TODO: To handle subdirectories we need to create the
237
:note: If the export fails, the destination directory will be
238
left in a half-assed state.
242
mutter('export version %r' % tree)
244
for dp, ie in inv.iter_entries():
246
fullpath = appendpath(dest, dp)
247
if kind == 'directory':
250
pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
252
raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
253
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
254
exporters['dir'] = dir_exporter
261
def get_root_name(dest):
262
"""Get just the root name for a tarball.
264
>>> get_root_name('mytar.tar')
266
>>> get_root_name('mytar.tar.bz2')
268
>>> get_root_name('tar.tar.tar.tgz')
270
>>> get_root_name('bzr-0.0.5.tar.gz')
272
>>> get_root_name('a/long/path/mytar.tgz')
274
>>> get_root_name('../parent/../dir/other.tbz2')
277
endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
278
dest = os.path.basename(dest)
280
if dest.endswith(end):
281
return dest[:-len(end)]
283
def tar_exporter(tree, dest, root, compression=None):
284
"""Export this tree to a new tar file.
286
`dest` will be created holding the contents of this tree; if it
287
already exists, it will be clobbered, like with "tar -c".
289
from time import time
291
compression = str(compression or '')
293
root = get_root_name(dest)
295
ball = tarfile.open(dest, 'w:' + compression)
296
except tarfile.CompressionError, e:
297
raise BzrError(str(e))
298
mutter('export version %r' % tree)
300
for dp, ie in inv.iter_entries():
301
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
302
item = tarfile.TarInfo(os.path.join(root, dp))
303
# TODO: would be cool to actually set it to the timestamp of the
304
# revision it was last changed
306
if ie.kind == 'directory':
307
item.type = tarfile.DIRTYPE
312
elif ie.kind == 'file':
313
item.type = tarfile.REGTYPE
314
fileobj = tree.get_file(ie.file_id)
315
item.size = _find_file_size(fileobj)
318
raise BzrError("don't know how to export {%s} of kind %r" %
319
(ie.file_id, ie.kind))
321
ball.addfile(item, fileobj)
323
exporters['tar'] = tar_exporter
325
def tgz_exporter(tree, dest, root):
326
tar_exporter(tree, dest, root, compression='gz')
327
exporters['tgz'] = tgz_exporter
329
def tbz_exporter(tree, dest, root):
330
tar_exporter(tree, dest, root, compression='bz2')
331
exporters['tbz2'] = tbz_exporter
334
def _find_file_size(fileobj):
335
offset = fileobj.tell()
338
size = fileobj.tell()
340
# gzip doesn't accept second argument to seek()
344
nread = len(fileobj.read())