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 functionality, which can take a Tree and create a different representation.
19
Such as non-controlled directories, tarfiles, zipfiles, etc.
17
"""Export trees to tarballs, non-controlled directories, zipfiles, etc.
23
22
from bzrlib import (
28
28
# Maps format name => export function
58
59
When requesting a specific type of export, load the respective path.
60
def _loader(tree, dest, root, subdir, filtered, per_file_timestamps):
61
def _loader(tree, dest, root, subdir, filtered, force_mtime, fileobj):
61
62
func = pyutils.get_named_object(module, funcname)
62
63
return func(tree, dest, root, subdir, filtered=filtered,
63
per_file_timestamps=per_file_timestamps)
64
force_mtime=force_mtime, fileobj=fileobj)
64
66
register_exporter(scheme, extensions, _loader)
69
def get_export_generator(tree, dest=None, format=None, root=None, subdir=None,
70
filtered=False, per_file_timestamps=False,
72
"""Returns a generator that exports the given tree.
74
The generator is expected to yield None while exporting the tree while the
75
actual export is written to ``fileobj``.
77
:param tree: A Tree (such as RevisionTree) to export
79
:param dest: The destination where the files, etc should be put
81
:param format: The format (dir, zip, etc), if None, it will check the
82
extension on dest, looking for a match
84
:param root: The root location inside the format. It is common practise to
85
have zipfiles and tarballs extract into a subdirectory, rather than
86
into the current working directory. If root is None, the default root
87
will be selected as the destination without its extension.
89
:param subdir: A starting directory within the tree. None means to export
90
the entire tree, and anything else should specify the relative path to
91
a directory to start exporting from.
93
:param filtered: If True, content filtering is applied to the exported
96
:param per_file_timestamps: Whether to use the timestamp stored in the tree
97
rather than now(). This will do a revision lookup for every file so
98
will be significantly slower.
100
:param fileobj: Optional file object to use
102
global _exporters, _exporter_extensions
104
if format is None and dest is not None:
105
for ext in _exporter_extensions:
106
if dest.endswith(ext):
107
format = _exporter_extensions[ext]
110
# Most of the exporters will just have to call
111
# this function anyway, so why not do it for them
113
root = get_root_name(dest)
115
if format not in _exporters:
116
raise errors.NoSuchExportFormat(format)
118
if not per_file_timestamps:
119
force_mtime = time.time()
123
trace.mutter('export version %r', tree)
128
for _ in _exporters[format](tree, dest, root, subdir,
130
force_mtime=force_mtime, fileobj=fileobj):
67
137
def export(tree, dest, format=None, root=None, subdir=None, filtered=False,
68
per_file_timestamps=False):
138
per_file_timestamps=False, fileobj=None):
69
139
"""Export the given Tree to the specific destination.
71
141
:param tree: A Tree (such as RevisionTree) to export
84
154
a directory to start exporting from.
85
155
:param filtered: If True, content filtering is applied to the
87
:param per_file_timestamps: Whether to use the timestamp stored in the
88
tree rather than now(). This will do a revision lookup
157
:param per_file_timestamps: Whether to use the timestamp stored in the
158
tree rather than now(). This will do a revision lookup
89
159
for every file so will be significantly slower.
160
:param fileobj: Optional file object to use
91
global _exporters, _exporter_extensions
94
for ext in _exporter_extensions:
95
if dest.endswith(ext):
96
format = _exporter_extensions[ext]
99
# Most of the exporters will just have to call
100
# this function anyway, so why not do it for them
102
root = get_root_name(dest)
104
if format not in _exporters:
105
raise errors.NoSuchExportFormat(format)
108
return _exporters[format](tree, dest, root, subdir, filtered=filtered,
109
per_file_timestamps=per_file_timestamps)
162
for _ in get_export_generator(tree, dest, format, root, subdir, filtered,
163
per_file_timestamps, fileobj):
114
167
def get_root_name(dest):
115
168
"""Get just the root name for an export.
117
>>> get_root_name('../mytest.tar')
119
>>> get_root_name('mytar.tar')
121
>>> get_root_name('mytar.tar.bz2')
123
>>> get_root_name('tar.tar.tar.tgz')
125
>>> get_root_name('bzr-0.0.5.tar.gz')
127
>>> get_root_name('bzr-0.0.5.zip')
129
>>> get_root_name('bzr-0.0.5')
131
>>> get_root_name('a/long/path/mytar.tgz')
133
>>> get_root_name('../parent/../dir/other.tbz2')
136
171
global _exporter_extensions
173
# Exporting to -/foo doesn't make sense so use relative paths.
137
175
dest = os.path.basename(dest)
138
176
for ext in _exporter_extensions:
139
177
if dest.endswith(ext):
144
def _export_iter_entries(tree, subdir):
182
def _export_iter_entries(tree, subdir, skip_special=True):
145
183
"""Iter the entries for tree suitable for exporting.
147
185
:param tree: A tree object.
148
186
:param subdir: None or the path of an entry to start exporting from.
187
:param skip_special: Whether to skip .bzr files.
154
subdir_id = inv.path2id(subdir)
155
if subdir_id is not None:
156
subdir_object = inv[subdir_id]
157
# XXX: subdir is path not an id, so NoSuchId isn't proper error
159
raise errors.NoSuchId(tree, subdir)
160
if subdir_object is not None and subdir_object.kind != 'directory':
161
yield subdir_object.name, subdir_object
164
entries = inv.iter_entries(subdir_object)
166
entries.next() # skip root
167
for entry in entries:
191
if subdir is not None:
192
subdir = subdir.rstrip('/')
193
entries = tree.iter_entries_by_dir()
194
entries.next() # skip root
195
for path, entry in entries:
168
196
# The .bzr* namespace is reserved for "magic" files like
169
197
# .bzrignore and .bzrrules - do not export these
170
if entry[0].startswith(".bzr"):
198
if skip_special and path.startswith(".bzr"):
173
if not tree.has_filename(entry[0]):
201
if entry.kind == 'directory':
203
final_path = entry.name
204
elif subdir is not None:
205
if path.startswith(subdir + '/'):
206
final_path = path[len(subdir) + 1:]
176
if not tree.has_filename(os.path.join(subdir, entry[0])):
181
register_lazy_exporter(None, [], 'bzrlib.export.dir_exporter', 'dir_exporter')
182
register_lazy_exporter('dir', [], 'bzrlib.export.dir_exporter', 'dir_exporter')
183
register_lazy_exporter('tar', ['.tar'], 'bzrlib.export.tar_exporter', 'tar_exporter')
184
register_lazy_exporter('tgz', ['.tar.gz', '.tgz'], 'bzrlib.export.tar_exporter', 'tgz_exporter')
185
register_lazy_exporter('tbz2', ['.tar.bz2', '.tbz2'], 'bzrlib.export.tar_exporter', 'tbz_exporter')
186
register_lazy_exporter('zip', ['.zip'], 'bzrlib.export.zip_exporter', 'zip_exporter')
211
if not tree.has_filename(path):
214
yield final_path, entry
217
register_lazy_exporter(None, [], 'bzrlib.export.dir_exporter',
218
'dir_exporter_generator')
219
register_lazy_exporter('dir', [], 'bzrlib.export.dir_exporter',
220
'dir_exporter_generator')
221
register_lazy_exporter('tar', ['.tar'], 'bzrlib.export.tar_exporter',
222
'plain_tar_exporter_generator')
223
register_lazy_exporter('tgz', ['.tar.gz', '.tgz'],
224
'bzrlib.export.tar_exporter',
225
'tgz_exporter_generator')
226
register_lazy_exporter('tbz2', ['.tar.bz2', '.tbz2'],
227
'bzrlib.export.tar_exporter', 'tbz_exporter_generator')
228
register_lazy_exporter('tlzma', ['.tar.lzma'], 'bzrlib.export.tar_exporter',
229
'tar_lzma_exporter_generator')
230
register_lazy_exporter('txz', ['.tar.xz'], 'bzrlib.export.tar_exporter',
231
'tar_xz_exporter_generator')
232
register_lazy_exporter('zip', ['.zip'], 'bzrlib.export.zip_exporter',
233
'zip_exporter_generator')