17
17
"""Tree classes, representing directory at point in time.
21
import os.path, os, fnmatch
23
from osutils import pumpfile, compare_files, 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
21
from cStringIO import StringIO
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
36
33
"""Abstract file tree.
38
35
There are several subclasses:
86
82
if ie.text_size != None:
87
83
if ie.text_size != fp['size']:
88
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
84
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
89
85
["inventory expects %d bytes" % ie.text_size,
90
86
"file is actually %d bytes" % fp['size'],
91
87
"store is probably damaged/corrupt"])
93
89
if ie.text_sha1 != fp['sha1']:
94
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
90
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
95
91
["inventory expects %s" % ie.text_sha1,
96
92
"file is actually %s" % fp['sha1'],
97
93
"store is probably damaged/corrupt"])
100
def print_file(self, fileid):
101
"""Print file with id `fileid` to stdout."""
96
def print_file(self, file_id):
97
"""Print file with id `file_id` to stdout."""
103
pumpfile(self.get_file(fileid), sys.stdout)
106
def export(self, dest):
107
"""Export this tree to a new directory.
109
`dest` should not exist, and will be created holding the
110
contents of this tree.
112
TODO: To handle subdirectories we need to create the
115
:note: If the export fails, the destination directory will be
116
left in a half-assed state.
119
mutter('export version %r' % self)
121
for dp, ie in inv.iter_entries():
123
fullpath = appendpath(dest, dp)
124
if kind == 'directory':
127
pumpfile(self.get_file(ie.file_id), file(fullpath, 'wb'))
129
bailout("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
130
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
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)
141
120
or at least passing a description to the constructor.
144
def __init__(self, store, inv):
123
def __init__(self, weave_store, inv, revision_id):
124
self._weave_store = weave_store
146
125
self._inventory = inv
126
self._revision_id = revision_id
128
def get_file_text(self, file_id):
129
ie = self._inventory[file_id]
130
weave = self._weave_store.get_weave(file_id)
131
idx = weave.lookup(self._revision_id)
132
content = weave.get_text(idx)
133
if len(content) != ie.text_size:
134
raise BzrCheckError('mismatched size on revision %s of file %s: '
136
% (self._revision_id, file_id, len(content),
148
140
def get_file(self, file_id):
149
ie = self._inventory[file_id]
150
f = self._store[ie.text_id]
151
mutter(" get fileid{%s} from %r" % (file_id, self))
152
self._check_retrieved(ie, f)
141
return StringIO(self.get_file_text(file_id))
155
143
def get_file_size(self, file_id):
156
144
return self._inventory[file_id].text_size
158
146
def get_file_sha1(self, file_id):
159
147
ie = self._inventory[file_id]
148
if ie.kind == "file":
162
151
def has_filename(self, filename):
163
152
return bool(self.inventory.path2id(filename))
245
242
if old_name != new_name:
246
243
yield (old_name, new_name)
247
######################################################################
250
def dir_exporter(tree, dest, root):
251
"""Export this tree to a new directory.
253
`dest` should not exist, and will be created holding the
254
contents of this tree.
256
TODO: To handle subdirectories we need to create the
259
:note: If the export fails, the destination directory will be
260
left in a half-assed state.
264
mutter('export version %r' % tree)
266
for dp, ie in inv.iter_entries():
268
fullpath = appendpath(dest, dp)
269
if kind == 'directory':
272
pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
274
raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
275
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
276
exporters['dir'] = dir_exporter
283
def get_root_name(dest):
284
"""Get just the root name for a tarball.
286
>>> get_root_name('mytar.tar')
288
>>> get_root_name('mytar.tar.bz2')
290
>>> get_root_name('tar.tar.tar.tgz')
292
>>> get_root_name('bzr-0.0.5.tar.gz')
294
>>> get_root_name('a/long/path/mytar.tgz')
296
>>> get_root_name('../parent/../dir/other.tbz2')
299
endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
300
dest = os.path.basename(dest)
302
if dest.endswith(end):
303
return dest[:-len(end)]
305
def tar_exporter(tree, dest, root, compression=None):
306
"""Export this tree to a new tar file.
308
`dest` will be created holding the contents of this tree; if it
309
already exists, it will be clobbered, like with "tar -c".
311
from time import time
313
compression = str(compression or '')
315
root = get_root_name(dest)
317
ball = tarfile.open(dest, 'w:' + compression)
318
except tarfile.CompressionError, e:
319
raise BzrError(str(e))
320
mutter('export version %r' % tree)
322
for dp, ie in inv.iter_entries():
323
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
324
item = tarfile.TarInfo(os.path.join(root, dp))
325
# TODO: would be cool to actually set it to the timestamp of the
326
# revision it was last changed
328
if ie.kind == 'directory':
329
item.type = tarfile.DIRTYPE
334
elif ie.kind == 'file':
335
item.type = tarfile.REGTYPE
336
fileobj = tree.get_file(ie.file_id)
337
item.size = _find_file_size(fileobj)
340
raise BzrError("don't know how to export {%s} of kind %r" %
341
(ie.file_id, ie.kind))
343
ball.addfile(item, fileobj)
345
exporters['tar'] = tar_exporter
347
def tgz_exporter(tree, dest, root):
348
tar_exporter(tree, dest, root, compression='gz')
349
exporters['tgz'] = tgz_exporter
351
def tbz_exporter(tree, dest, root):
352
tar_exporter(tree, dest, root, compression='bz2')
353
exporters['tbz2'] = tbz_exporter
356
def _find_file_size(fileobj):
357
offset = fileobj.tell()
360
size = fileobj.tell()
362
# gzip doesn't accept second argument to seek()
366
nread = len(fileobj.read())