14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Export a Tree to a non-versioned directory.
17
"""Export a Tree to a non-versioned directory."""
25
from bzrlib import export, osutils
26
28
from bzrlib.export import _export_iter_entries
27
29
from bzrlib.filters import (
28
30
ContentFilterContext,
29
31
filtered_output_bytes,
31
from bzrlib.trace import mutter
34
def tar_exporter(tree, dest, root, subdir, compression=None, filtered=False,
35
per_file_timestamps=False):
36
"""Export this tree to a new tar file.
38
`dest` will be created holding the contents of this tree; if it
39
already exists, it will be clobbered, like with "tar -c".
41
mutter('export version %r', tree)
43
compression = str(compression or '')
35
def prepare_tarball_item(tree, root, final_path, entry, filtered=False,
37
"""Prepare a tarball item for exporting
39
:param tree: Tree to export
41
:param final_path: Final path to place item
43
:param entry: Entry to export
45
:param filtered: Whether to apply filters
47
:param force_mtime: Option mtime to force, instead of using tree
50
Returns a (tarinfo, fileobj) tuple
52
filename = osutils.pathjoin(root, final_path).encode('utf8')
53
item = tarfile.TarInfo(filename)
54
if force_mtime is not None:
55
item.mtime = force_mtime
57
item.mtime = tree.get_file_mtime(entry.file_id, final_path)
58
if entry.kind == "file":
59
item.type = tarfile.REGTYPE
60
if tree.is_executable(entry.file_id):
65
chunks = tree.get_file_lines(entry.file_id)
66
filters = tree._content_filter_stack(final_path)
67
context = ContentFilterContext(final_path, tree, entry)
68
contents = filtered_output_bytes(chunks, filters, context)
69
content = ''.join(contents)
70
item.size = len(content)
71
fileobj = StringIO.StringIO(content)
73
item.size = tree.get_file_size(entry.file_id)
74
fileobj = tree.get_file(entry.file_id)
75
elif entry.kind == "directory":
76
item.type = tarfile.DIRTYPE
81
elif entry.kind == "symlink":
82
item.type = tarfile.SYMTYPE
85
item.linkname = tree.get_symlink_target(entry.file_id)
88
raise errors.BzrError("don't know how to export {%s} of kind %r"
89
% (entry.file_id, entry.kind))
90
return (item, fileobj)
93
def export_tarball_generator(tree, ball, root, subdir=None, filtered=False,
95
"""Export tree contents to a tarball.
97
:returns: A generator that will repeatedly produce None as each file is
98
emitted. The entire generator must be consumed to complete writing
101
:param tree: Tree to export
103
:param ball: Tarball to export to; it will be closed when writing is
106
:param filtered: Whether to apply filters
108
:param subdir: Sub directory to export
110
:param force_mtime: Option mtime to force, instead of using tree
114
for final_path, entry in _export_iter_entries(tree, subdir):
115
(item, fileobj) = prepare_tarball_item(
116
tree, root, final_path, entry, filtered, force_mtime)
117
ball.addfile(item, fileobj)
123
def tgz_exporter_generator(tree, dest, root, subdir, filtered=False,
124
force_mtime=None, fileobj=None):
125
"""Export this tree to a new tar file.
127
`dest` will be created holding the contents of this tree; if it
128
already exists, it will be clobbered, like with "tar -c".
131
if force_mtime is not None:
132
root_mtime = force_mtime
133
elif (getattr(tree, "repository", None) and
134
getattr(tree, "get_revision_id", None)):
135
# If this is a revision tree, use the revisions' timestamp
136
rev = tree.repository.get_revision(tree.get_revision_id())
137
root_mtime = rev.timestamp
138
elif tree.get_root_id() is not None:
139
root_mtime = tree.get_file_mtime(tree.get_root_id())
144
if fileobj is not None:
151
stream = open(dest, 'wb')
152
# gzip file is used with an explicit fileobj so that
153
# the basename can be stored in the gzip file rather than
155
basename = os.path.basename(dest)
157
zipstream = gzip.GzipFile(basename, 'w', fileobj=stream,
160
# Python < 2.7 doesn't support the mtime argument
161
zipstream = gzip.GzipFile(basename, 'w', fileobj=stream)
162
ball = tarfile.open(None, 'w|', fileobj=zipstream)
163
for _ in export_tarball_generator(
164
tree, ball, root, subdir, filtered, force_mtime):
166
# Closing zipstream may trigger writes to stream
169
# Now we can safely close the stream
173
def tbz_exporter_generator(tree, dest, root, subdir, filtered=False,
174
force_mtime=None, fileobj=None):
175
"""Export this tree to a new tar file.
177
`dest` will be created holding the contents of this tree; if it
178
already exists, it will be clobbered, like with "tar -c".
180
if fileobj is not None:
181
ball = tarfile.open(None, 'w|bz2', fileobj)
183
ball = tarfile.open(None, 'w|bz2', sys.stdout)
185
# tarfile.open goes on to do 'os.getcwd() + dest' for opening the
186
# tar file. With dest being unicode, this throws UnicodeDecodeError
187
# unless we encode dest before passing it on. This works around
188
# upstream python bug http://bugs.python.org/issue8396 (fixed in
189
# Python 2.6.5 and 2.7b1)
190
ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:bz2')
191
return export_tarball_generator(
192
tree, ball, root, subdir, filtered, force_mtime)
195
def plain_tar_exporter_generator(tree, dest, root, subdir, compression=None,
196
filtered=False, force_mtime=None,
198
"""Export this tree to a new tar file.
200
`dest` will be created holding the contents of this tree; if it
201
already exists, it will be clobbered, like with "tar -c".
203
if fileobj is not None:
208
stream = open(dest, 'wb')
209
ball = tarfile.open(None, 'w|', stream)
210
return export_tarball_generator(
211
tree, ball, root, subdir, filtered, force_mtime)
214
def tar_xz_exporter_generator(tree, dest, root, subdir, filtered=False,
215
force_mtime=None, fileobj=None):
216
return tar_lzma_exporter_generator(tree, dest, root, subdir, filtered,
217
force_mtime, fileobj, "xz")
220
def tar_lzma_exporter_generator(tree, dest, root, subdir, filtered=False,
221
force_mtime=None, fileobj=None,
222
compression_format="alone"):
223
"""Export this tree to a new .tar.lzma file.
225
`dest` will be created holding the contents of this tree; if it
226
already exists, it will be clobbered, like with "tar -c".
45
# XXX: If no root is given, the output tarball will contain files
46
# named '-/foo'; perhaps this is the most reasonable thing.
47
ball = tarfile.open(None, 'w|' + compression, sys.stdout)
50
root = export.get_root_name(dest)
52
# tarfile.open goes on to do 'os.getcwd() + dest' for opening
53
# the tar file. With dest being unicode, this throws UnicodeDecodeError
54
# unless we encode dest before passing it on. This works around
55
# upstream python bug http://bugs.python.org/issue8396
56
# (fixed in Python 2.6.5 and 2.7b1)
57
ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:' + compression)
59
for dp, ie in _export_iter_entries(tree, subdir):
60
filename = osutils.pathjoin(root, dp).encode('utf8')
61
item = tarfile.TarInfo(filename)
62
if per_file_timestamps:
63
item.mtime = tree.get_file_mtime(ie.file_id, dp)
67
item.type = tarfile.REGTYPE
68
if tree.is_executable(ie.file_id):
73
chunks = tree.get_file_lines(ie.file_id)
74
filters = tree._content_filter_stack(dp)
75
context = ContentFilterContext(dp, tree, ie)
76
contents = filtered_output_bytes(chunks, filters, context)
77
content = ''.join(contents)
78
item.size = len(content)
79
fileobj = StringIO.StringIO(content)
81
item.size = ie.text_size
82
fileobj = tree.get_file(ie.file_id)
83
elif ie.kind == "directory":
84
item.type = tarfile.DIRTYPE
89
elif ie.kind == "symlink":
90
item.type = tarfile.SYMTYPE
93
item.linkname = ie.symlink_target
96
raise BzrError("don't know how to export {%s} of kind %r" %
97
(ie.file_id, ie.kind))
98
ball.addfile(item, fileobj)
102
def tgz_exporter(tree, dest, root, subdir, filtered=False,
103
per_file_timestamps=False):
104
tar_exporter(tree, dest, root, subdir, compression='gz',
105
filtered=filtered, per_file_timestamps=per_file_timestamps)
108
def tbz_exporter(tree, dest, root, subdir, filtered=False,
109
per_file_timestamps=False):
110
tar_exporter(tree, dest, root, subdir, compression='bz2',
111
filtered=filtered, per_file_timestamps=per_file_timestamps)
229
raise errors.BzrError("Writing to stdout not supported for .tar.lzma")
231
if fileobj is not None:
232
raise errors.BzrError(
233
"Writing to fileobject not supported for .tar.lzma")
236
except ImportError, e:
237
raise errors.DependencyNotPresent('lzma', e)
239
stream = lzma.LZMAFile(dest.encode(osutils._fs_enc), 'w',
240
options={"format": compression_format})
241
ball = tarfile.open(None, 'w:', fileobj=stream)
242
return export_tarball_generator(
243
tree, ball, root, subdir, filtered=filtered, force_mtime=force_mtime)