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
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 appendpath, fingerprint_file
36
33
"""Abstract file tree.
38
35
There are several subclasses:
76
69
def _get_inventory(self):
77
70
return self._inventory
72
def get_file_by_path(self, path):
73
return self.get_file(self._inventory.path2id(path))
79
75
inventory = property(_get_inventory,
80
76
doc="Inventory of this Tree")
82
78
def _check_retrieved(self, ie, f):
83
81
fp = fingerprint_file(f)
86
84
if ie.text_size != None:
87
85
if ie.text_size != fp['size']:
88
bailout("mismatched size for file %r in %r" % (ie.file_id, self._store),
86
raise BzrError("mismatched size for file %r in %r" % (ie.file_id, self._store),
89
87
["inventory expects %d bytes" % ie.text_size,
90
88
"file is actually %d bytes" % fp['size'],
91
89
"store is probably damaged/corrupt"])
93
91
if ie.text_sha1 != fp['sha1']:
94
bailout("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
92
raise BzrError("wrong SHA-1 for file %r in %r" % (ie.file_id, self._store),
95
93
["inventory expects %s" % ie.text_sha1,
96
94
"file is actually %s" % fp['sha1'],
97
95
"store is probably damaged/corrupt"])
100
def print_file(self, fileid):
101
"""Print file with id `fileid` to stdout."""
98
def print_file(self, file_id):
99
"""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))
101
sys.stdout.write(self.get_file_text(file_id))
104
def export(self, dest, format='dir', root=None):
105
"""Export this tree."""
107
exporter = exporters[format]
109
from bzrlib.errors import BzrCommandError
110
raise BzrCommandError("export format %r not supported" % format)
111
exporter(self, dest, root)
141
122
or at least passing a description to the constructor.
144
def __init__(self, store, inv):
125
def __init__(self, weave_store, inv, revision_id):
126
self._weave_store = weave_store
146
127
self._inventory = inv
128
self._revision_id = revision_id
130
def get_weave(self, file_id):
131
return self._weave_store.get_weave(file_id)
134
def get_file_lines(self, file_id):
135
ie = self._inventory[file_id]
136
weave = self.get_weave(file_id)
137
return weave.get(ie.revision)
140
def get_file_text(self, file_id):
141
return ''.join(self.get_file_lines(file_id))
148
144
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)
145
return StringIO(self.get_file_text(file_id))
155
147
def get_file_size(self, file_id):
156
148
return self._inventory[file_id].text_size
158
150
def get_file_sha1(self, file_id):
159
151
ie = self._inventory[file_id]
152
if ie.kind == "file":
155
def is_executable(self, file_id):
156
return self._inventory[file_id].executable
162
158
def has_filename(self, filename):
163
159
return bool(self.inventory.path2id(filename))
165
161
def list_files(self):
166
162
# The only files returned by this are those from the version
167
163
for path, entry in self.inventory.iter_entries():
168
yield path, 'V', entry.kind, entry.file_id
164
yield path, 'V', entry.kind, entry.file_id, entry
166
def get_symlink_target(self, file_id):
167
ie = self._inventory[file_id]
168
return ie.symlink_target;
171
171
class EmptyTree(Tree):
172
172
def __init__(self):
173
173
self._inventory = Inventory()
175
def get_symlink_target(self, file_id):
175
178
def has_filename(self, filename):
178
181
def list_files(self):
179
if False: # just to make it a generator
184
def __contains__(self, file_id):
185
return file_id in self._inventory
187
def get_file_sha1(self, file_id):
188
assert self._inventory[file_id].kind == "root_directory"
184
192
######################################################################
245
253
if old_name != new_name:
246
254
yield (old_name, new_name)
258
######################################################################
261
def dir_exporter(tree, dest, root):
262
"""Export this tree to a new directory.
264
`dest` should not exist, and will be created holding the
265
contents of this tree.
267
TODO: To handle subdirectories we need to create the
270
:note: If the export fails, the destination directory will be
271
left in a half-assed state.
275
mutter('export version %r' % tree)
277
for dp, ie in inv.iter_entries():
278
ie.put_on_disk(dest, dp, tree)
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, fileobj = ie.get_tar_item(root, dp, now, tree)
329
ball.addfile(item, fileobj)
332
exporters['tar'] = tar_exporter
334
def tgz_exporter(tree, dest, root):
335
tar_exporter(tree, dest, root, compression='gz')
336
exporters['tgz'] = tgz_exporter
338
def tbz_exporter(tree, dest, root):
339
tar_exporter(tree, dest, root, compression='bz2')
340
exporters['tbz2'] = tbz_exporter