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
# FIXME: RevisionTree should be given a branch
132
# not a store, or the store should know the branch.
133
import bzrlib.transactions as transactions
134
return self._weave_store.get_weave(file_id,
135
transactions.PassThroughTransaction())
138
def get_file_lines(self, file_id):
139
ie = self._inventory[file_id]
140
weave = self.get_weave(file_id)
141
return weave.get(ie.revision)
144
def get_file_text(self, file_id):
145
return ''.join(self.get_file_lines(file_id))
148
148
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)
149
return StringIO(self.get_file_text(file_id))
155
151
def get_file_size(self, file_id):
156
152
return self._inventory[file_id].text_size
158
154
def get_file_sha1(self, file_id):
159
155
ie = self._inventory[file_id]
156
if ie.kind == "file":
159
def is_executable(self, file_id):
160
return self._inventory[file_id].executable
162
162
def has_filename(self, filename):
163
163
return bool(self.inventory.path2id(filename))
165
165
def list_files(self):
166
166
# The only files returned by this are those from the version
167
167
for path, entry in self.inventory.iter_entries():
168
yield path, 'V', entry.kind, entry.file_id
168
yield path, 'V', entry.kind, entry.file_id, entry
170
def get_symlink_target(self, file_id):
171
ie = self._inventory[file_id]
172
return ie.symlink_target;
171
175
class EmptyTree(Tree):
172
176
def __init__(self):
173
177
self._inventory = Inventory()
179
def get_symlink_target(self, file_id):
175
182
def has_filename(self, filename):
178
185
def list_files(self):
179
if False: # just to make it a generator
188
def __contains__(self, file_id):
189
return file_id in self._inventory
191
def get_file_sha1(self, file_id):
192
assert self._inventory[file_id].kind == "root_directory"
184
196
######################################################################
245
257
if old_name != new_name:
246
258
yield (old_name, new_name)
262
######################################################################
265
def dir_exporter(tree, dest, root):
266
"""Export this tree to a new directory.
268
`dest` should not exist, and will be created holding the
269
contents of this tree.
271
TODO: To handle subdirectories we need to create the
274
:note: If the export fails, the destination directory will be
275
left in a half-assed state.
279
mutter('export version %r' % tree)
281
for dp, ie in inv.iter_entries():
282
ie.put_on_disk(dest, dp, tree)
284
exporters['dir'] = dir_exporter
291
def get_root_name(dest):
292
"""Get just the root name for a tarball.
294
>>> get_root_name('mytar.tar')
296
>>> get_root_name('mytar.tar.bz2')
298
>>> get_root_name('tar.tar.tar.tgz')
300
>>> get_root_name('bzr-0.0.5.tar.gz')
302
>>> get_root_name('a/long/path/mytar.tgz')
304
>>> get_root_name('../parent/../dir/other.tbz2')
307
endings = ['.tar', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2']
308
dest = os.path.basename(dest)
310
if dest.endswith(end):
311
return dest[:-len(end)]
313
def tar_exporter(tree, dest, root, compression=None):
314
"""Export this tree to a new tar file.
316
`dest` will be created holding the contents of this tree; if it
317
already exists, it will be clobbered, like with "tar -c".
319
from time import time
321
compression = str(compression or '')
323
root = get_root_name(dest)
325
ball = tarfile.open(dest, 'w:' + compression)
326
except tarfile.CompressionError, e:
327
raise BzrError(str(e))
328
mutter('export version %r' % tree)
330
for dp, ie in inv.iter_entries():
331
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
332
item, fileobj = ie.get_tar_item(root, dp, now, tree)
333
ball.addfile(item, fileobj)
336
exporters['tar'] = tar_exporter
338
def tgz_exporter(tree, dest, root):
339
tar_exporter(tree, dest, root, compression='gz')
340
exporters['tgz'] = tgz_exporter
342
def tbz_exporter(tree, dest, root):
343
tar_exporter(tree, dest, root, compression='bz2')
344
exporters['tbz2'] = tbz_exporter