17
17
"""Tree classes, representing directory at point in time.
21
from cStringIO import StringIO
20
from osutils import pumpfile, appendpath, fingerprint_file
22
from bzrlib.trace import mutter, note
23
from bzrlib.errors import BzrError
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
29
29
class Tree(object):
30
30
"""Abstract file tree.
68
63
def id2path(self, file_id):
69
64
return self.inventory.id2path(file_id)
71
def kind(self, file_id):
72
raise NotImplementedError("subclasses must implement kind")
74
66
def _get_inventory(self):
75
67
return self._inventory
77
def get_file_by_path(self, path):
78
return self.get_file(self._inventory.path2id(path))
80
69
inventory = property(_get_inventory,
81
70
doc="Inventory of this Tree")
83
72
def _check_retrieved(self, ie, f):
86
73
fp = fingerprint_file(f)
100
87
"store is probably damaged/corrupt"])
103
def print_file(self, file_id):
104
"""Print file with id `file_id` to stdout."""
90
def print_file(self, fileid):
91
"""Print file with id `fileid` to stdout."""
106
sys.stdout.write(self.get_file_text(file_id))
93
pumpfile(self.get_file(fileid), sys.stdout)
96
def export(self, dest, format='dir'):
97
"""Export this tree."""
99
exporter = exporters[format]
101
raise BzrCommandError("export format %r not supported" % format)
109
106
class RevisionTree(Tree):
110
107
"""Tree viewing a previous revision.
116
113
or at least passing a description to the constructor.
119
def __init__(self, branch, inv, revision_id):
120
self._branch = branch
121
self._weave_store = branch.weave_store
116
def __init__(self, store, inv):
122
118
self._inventory = inv
123
self._revision_id = revision_id
125
def get_weave(self, file_id):
126
import bzrlib.transactions as transactions
127
return self._weave_store.get_weave(file_id,
128
self._branch.get_transaction())
130
def get_weave_prelude(self, file_id):
131
import bzrlib.transactions as transactions
132
return self._weave_store.get_weave_prelude(file_id,
133
self._branch.get_transaction())
135
def get_file_lines(self, file_id):
136
ie = self._inventory[file_id]
137
weave = self.get_weave(file_id)
138
return weave.get(ie.revision)
140
def get_file_text(self, file_id):
141
return ''.join(self.get_file_lines(file_id))
143
120
def get_file(self, file_id):
144
return StringIO(self.get_file_text(file_id))
121
ie = self._inventory[file_id]
122
f = self._store[ie.text_id]
123
mutter(" get fileid{%s} from %r" % (file_id, self))
124
self._check_retrieved(ie, f)
146
127
def get_file_size(self, file_id):
147
128
return self._inventory[file_id].text_size
149
130
def get_file_sha1(self, file_id):
150
131
ie = self._inventory[file_id]
151
if ie.kind == "file":
154
def is_executable(self, file_id):
155
ie = self._inventory[file_id]
156
if ie.kind != "file":
158
return self._inventory[file_id].executable
160
134
def has_filename(self, filename):
161
135
return bool(self.inventory.path2id(filename))
163
137
def list_files(self):
164
138
# The only files returned by this are those from the version
165
139
for path, entry in self.inventory.iter_entries():
166
yield path, 'V', entry.kind, entry.file_id, entry
168
def get_symlink_target(self, file_id):
169
ie = self._inventory[file_id]
170
return ie.symlink_target;
172
def kind(self, file_id):
173
return self._inventory[file_id].kind
140
yield path, 'V', entry.kind, entry.file_id
176
143
class EmptyTree(Tree):
177
144
def __init__(self):
145
from bzrlib.inventory import Inventory
178
146
self._inventory = Inventory()
180
def get_symlink_target(self, file_id):
183
148
def has_filename(self, filename):
186
def kind(self, file_id):
187
assert self._inventory[file_id].kind == "root_directory"
188
return "root_directory"
190
151
def list_files(self):
152
if False: # just to make it a generator
193
def __contains__(self, file_id):
194
return file_id in self._inventory
196
def get_file_sha1(self, file_id):
197
assert self._inventory[file_id].kind == "root_directory"
201
157
######################################################################
223
######################################################################
226
def dir_exporter(tree, dest):
227
"""Export this tree to a new directory.
229
`dest` should not exist, and will be created holding the
230
contents of this tree.
232
TODO: To handle subdirectories we need to create the
235
:note: If the export fails, the destination directory will be
236
left in a half-assed state.
240
mutter('export version %r' % tree)
242
for dp, ie in inv.iter_entries():
244
fullpath = appendpath(dest, dp)
245
if kind == 'directory':
248
pumpfile(tree.get_file(ie.file_id), file(fullpath, 'wb'))
250
raise BzrError("don't know how to export {%s} of kind %r" % (ie.file_id, kind))
251
mutter(" export {%s} kind %s to %s" % (ie.file_id, kind, fullpath))
252
exporters['dir'] = dir_exporter
259
def tar_exporter(tree, dest, compression=None):
260
"""Export this tree to a new tar file.
262
`dest` will be created holding the contents of this tree; if it
263
already exists, it will be clobbered, like with "tar -c".
265
from time import time
267
compression = str(compression or '')
269
ball = tarfile.open(dest, 'w:' + compression)
270
except tarfile.CompressionError, e:
271
raise BzrError(str(e))
272
mutter('export version %r' % tree)
274
for dp, ie in inv.iter_entries():
275
mutter(" export {%s} kind %s to %s" % (ie.file_id, ie.kind, dest))
276
item = tarfile.TarInfo(dp)
277
# TODO: would be cool to actually set it to the timestamp of the
278
# revision it was last changed
280
if ie.kind == 'directory':
281
item.type = tarfile.DIRTYPE
286
elif ie.kind == 'file':
287
item.type = tarfile.REGTYPE
288
fileobj = tree.get_file(ie.file_id)
289
item.size = _find_file_size(fileobj)
292
raise BzrError("don't know how to export {%s} of kind %r" %
293
(ie.file_id, ie.kind))
295
ball.addfile(item, fileobj)
297
exporters['tar'] = tar_exporter
299
def tgz_exporter(tree, dest):
300
tar_exporter(tree, dest, compression='gz')
301
exporters['tgz'] = tgz_exporter
303
def tbz_exporter(tree, dest):
304
tar_exporter(tree, dest, compression='bz2')
305
exporters['tbz2'] = tbz_exporter
308
def _find_file_size(fileobj):
309
offset = fileobj.tell()
312
size = fileobj.tell()
314
# gzip doesn't accept second argument to seek()
318
nread = len(fileobj.read())