17
17
"""Tree classes, representing directory at point in time.
21
import os.path, os, fnmatch
23
from osutils import pumpfile, filesize, quotefn, sha_file, \
24
joinpath, splitpath, appendpath, isdir, isfile, file_kind, fingerprint_file
26
from stat import S_ISREG, S_ISDIR, ST_MODE, ST_SIZE
28
from inventory import Inventory
29
from trace import mutter, note
30
from errors import bailout
23
from bzrlib.trace import mutter, note
24
from bzrlib.errors import BzrError
25
from bzrlib.inventory import Inventory
26
from bzrlib.osutils import pumpfile, appendpath, fingerprint_file
31
35
class Tree(object):
32
36
"""Abstract file tree.
81
82
if ie.text_size != None:
82
83
if ie.text_size != fp['size']:
83
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
84
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
84
85
["inventory expects %d bytes" % ie.text_size,
85
86
"file is actually %d bytes" % fp['size'],
86
87
"store is probably damaged/corrupt"])
88
89
if ie.text_sha1 != fp['sha1']:
89
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
90
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
90
91
["inventory expects %s" % ie.text_sha1,
91
92
"file is actually %s" % fp['sha1'],
92
93
"store is probably damaged/corrupt"])
98
99
pumpfile(self.get_file(fileid), sys.stdout)
101
def export(self, dest, format='dir', root=None):
102
"""Export this tree."""
104
exporter = exporters[format]
106
from bzrlib.errors import BzrCommandError
107
raise BzrCommandError("export format %r not supported" % format)
108
exporter(self, dest, root)
102
def export(self, dest):
103
"""Export this tree to a new directory.
105
`dest` should not exist, and will be created holding the
106
contents of this tree.
108
TODO: To handle subdirectories we need to create the
111
:note: If the export fails, the destination directory will be
112
left in a half-assed state.
115
mutter('export version %r' % self)
117
for dp, ie in inv.iter_entries():
119
fullpath = appendpath(dest, dp)
120
if kind == 'directory':
123
pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
125
bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
126
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
227
241
if old_name != new_name:
228
242
yield (old_name, new_name)
232
######################################################################
235
def dir_exporter(tree, dest, root):
236
"""Export this tree to a new directory.
238
`dest` should not exist, and will be created holding the
239
contents of this tree.
241
TODO: To handle subdirectories we need to create the
244
:note: If the export fails, the destination directory will be
245
left in a half-assed state.
249
mutter('export version %r' % tree)
251
for dp, ie in inv.iter_entries():
253
fullpath = appendpath(dest, dp)
254
if kind == 'directory':
257
pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
259
raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
260
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
261
exporters['dir'] = dir_exporter
268
def get_root_name(dest):
269
"""Get just the root name for a tarball.
271
>>> get_root_name('mytar.tar')
273
>>> get_root_name('mytar.tar.bz2')
275
>>> get_root_name('tar.tar.tar.tgz')
277
>>> get_root_name('bzr-0.0.5.tar.gz')
279
>>> get_root_name('a/long/path/mytar.tgz')
281
>>> get_root_name('../parent/../dir/other.tbz2')
284
endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
285
dest = os.path.basename(dest)
287
if dest.endswith(end):
288
return dest[:-len(end)]
290
def tar_exporter(tree, dest, root, compression=None):
291
"""Export this tree to a new tar file.
293
`dest` will be created holding the contents of this tree; if it
294
already exists, it will be clobbered, like with "tar -c".
296
from time import time
298
compression = str(compression or '')
300
root = get_root_name(dest)
302
ball = tarfile.open(dest, 'w:' + compression)
303
except tarfile.CompressionError, e:
304
raise BzrError(str(e))
305
mutter('export version %r' % tree)
307
for dp, ie in inv.iter_entries():
308
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
309
item = tarfile.TarInfo(os.path.join(root, dp))
310
# TODO: would be cool to actually set it to the timestamp of the
311
# revision it was last changed
313
if ie.kind == 'directory':
314
item.type = tarfile.DIRTYPE
319
elif ie.kind == 'file':
320
item.type = tarfile.REGTYPE
321
fileobj = tree.get_file(ie.file_id)
322
item.size = _find_file_size(fileobj)
325
raise BzrError("don't know how to export {%s} of kind %r" %
326
(ie.file_id, ie.kind))
328
ball.addfile(item, fileobj)
330
exporters['tar'] = tar_exporter
332
def tgz_exporter(tree, dest, root):
333
tar_exporter(tree, dest, root, compression='gz')
334
exporters['tgz'] = tgz_exporter
336
def tbz_exporter(tree, dest, root):
337
tar_exporter(tree, dest, root, compression='bz2')
338
exporters['tbz2'] = tbz_exporter
341
def _find_file_size(fileobj):
342
offset = fileobj.tell()
345
size = fileobj.tell()
347
# gzip doesn't accept second argument to seek()
351
nread = len(fileobj.read())