17
17
"""Tree classes, representing directory at point in time.
21
from cStringIO import StringIO
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
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 pumpfile, appendpath, fingerprint_file
32
35
class Tree(object):
33
36
"""Abstract file tree.
82
82
if ie.text_size != None:
83
83
if ie.text_size != fp['size']:
84
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),
85
85
["inventory expects %d bytes" % ie.text_size,
86
86
"file is actually %d bytes" % fp['size'],
87
87
"store is probably damaged/corrupt"])
89
89
if ie.text_sha1 != fp['sha1']:
90
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),
91
91
["inventory expects %s" % ie.text_sha1,
92
92
"file is actually %s" % fp['sha1'],
93
93
"store is probably damaged/corrupt"])
96
def print_file(self, file_id):
97
"""Print file with id `file_id` to stdout."""
96
def print_file(self, fileid):
97
"""Print file with id `fileid` to stdout."""
99
sys.stdout.write(self.get_file_text(file_id))
102
def export(self, dest, format='dir', root=None):
103
"""Export this tree."""
105
exporter = exporters[format]
107
from bzrlib.errors import BzrCommandError
108
raise BzrCommandError("export format %r not supported" % format)
109
exporter(self, dest, root)
99
pumpfile(self.get_file(fileid), sys.stdout)
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))
120
137
or at least passing a description to the constructor.
123
def __init__(self, weave_store, inv, revision_id):
124
self._weave_store = weave_store
140
def __init__(self, store, inv):
125
142
self._inventory = inv
126
self._revision_id = revision_id
128
def get_weave(self, file_id):
129
return self._weave_store.get_weave(file_id)
132
def get_file_text(self, file_id):
133
ie = self._inventory[file_id]
134
weave = self.get_weave(file_id)
135
idx = weave.lookup(ie.text_version)
136
content = weave.get_text(idx)
137
if len(content) != ie.text_size:
138
raise BzrCheckError('mismatched size on revision %s of file %s: '
140
% (self._revision_id, file_id, len(content),
144
144
def get_file(self, file_id):
145
return StringIO(self.get_file_text(file_id))
145
ie = self._inventory[file_id]
146
f = self._store[ie.text_id]
147
mutter(" get fileid{%s} from %r" % (file_id, self))
148
self._check_retrieved(ie, f)
147
151
def get_file_size(self, file_id):
148
152
return self._inventory[file_id].text_size
150
154
def get_file_sha1(self, file_id):
151
155
ie = self._inventory[file_id]
152
if ie.kind == "file":
155
158
def has_filename(self, filename):
156
159
return bool(self.inventory.path2id(filename))
246
241
if old_name != new_name:
247
242
yield (old_name, new_name)
251
######################################################################
254
def dir_exporter(tree, dest, root):
255
"""Export this tree to a new directory.
257
`dest` should not exist, and will be created holding the
258
contents of this tree.
260
TODO: To handle subdirectories we need to create the
263
:note: If the export fails, the destination directory will be
264
left in a half-assed state.
268
mutter('export version %r' % tree)
270
for dp, ie in inv.iter_entries():
272
fullpath = appendpath(dest, dp)
273
if kind == 'directory':
276
pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
278
raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
279
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
280
exporters['dir'] = dir_exporter
287
def get_root_name(dest):
288
"""Get just the root name for a tarball.
290
>>> get_root_name('mytar.tar')
292
>>> get_root_name('mytar.tar.bz2')
294
>>> get_root_name('tar.tar.tar.tgz')
296
>>> get_root_name('bzr-0.0.5.tar.gz')
298
>>> get_root_name('a/long/path/mytar.tgz')
300
>>> get_root_name('../parent/../dir/other.tbz2')
303
endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
304
dest = os.path.basename(dest)
306
if dest.endswith(end):
307
return dest[:-len(end)]
309
def tar_exporter(tree, dest, root, compression=None):
310
"""Export this tree to a new tar file.
312
`dest` will be created holding the contents of this tree; if it
313
already exists, it will be clobbered, like with "tar -c".
315
from time import time
317
compression = str(compression or '')
319
root = get_root_name(dest)
321
ball = tarfile.open(dest, 'w:' + compression)
322
except tarfile.CompressionError, e:
323
raise BzrError(str(e))
324
mutter('export version %r' % tree)
326
for dp, ie in inv.iter_entries():
327
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
328
item = tarfile.TarInfo(os.path.join(root, dp))
329
# TODO: would be cool to actually set it to the timestamp of the
330
# revision it was last changed
332
if ie.kind == 'directory':
333
item.type = tarfile.DIRTYPE
338
elif ie.kind == 'file':
339
item.type = tarfile.REGTYPE
340
fileobj = tree.get_file(ie.file_id)
341
item.size = _find_file_size(fileobj)
344
raise BzrError("don't know how to export {%s} of kind %r" %
345
(ie.file_id, ie.kind))
347
ball.addfile(item, fileobj)
349
exporters['tar'] = tar_exporter
351
def tgz_exporter(tree, dest, root):
352
tar_exporter(tree, dest, root, compression='gz')
353
exporters['tgz'] = tgz_exporter
355
def tbz_exporter(tree, dest, root):
356
tar_exporter(tree, dest, root, compression='bz2')
357
exporters['tbz2'] = tbz_exporter
360
def _find_file_size(fileobj):
361
offset = fileobj.tell()
364
size = fileobj.tell()
366
# gzip doesn't accept second argument to seek()
370
nread = len(fileobj.read())