~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/export/__init__.py

  • Committer: Vincent Ladeuil
  • Date: 2011-07-06 09:22:00 UTC
  • mfrom: (6008 +trunk)
  • mto: (6012.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6013.
  • Revision ID: v.ladeuil+lp@free.fr-20110706092200-7iai2mwzc0sqdsvf
MergingĀ inĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
16
16
 
17
 
"""Export functionality, which can take a Tree and create a different representation.
18
 
 
19
 
Such as non-controlled directories, tarfiles, zipfiles, etc.
 
17
"""Export trees to tarballs, non-controlled directories, zipfiles, etc.
20
18
"""
21
19
 
22
20
import os
 
21
import time
23
22
from bzrlib import (
24
23
    errors,
25
24
    pyutils,
 
25
    trace,
26
26
    )
27
27
 
28
28
# Maps format name => export function
30
30
# Maps filename extensions => export format name
31
31
_exporter_extensions = {}
32
32
 
 
33
 
33
34
def register_exporter(format, extensions, func, override=False):
34
35
    """Register an exporter.
35
36
 
57
58
 
58
59
    When requesting a specific type of export, load the respective path.
59
60
    """
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)
 
65
 
64
66
    register_exporter(scheme, extensions, _loader)
65
67
 
66
68
 
 
69
def get_export_generator(tree, dest=None, format=None, root=None, subdir=None,
 
70
                         filtered=False, per_file_timestamps=False,
 
71
                         fileobj=None):
 
72
    """Returns a generator that exports the given tree.
 
73
 
 
74
    The generator is expected to yield None while exporting the tree while the
 
75
    actual export is written to ``fileobj``.
 
76
 
 
77
    :param tree: A Tree (such as RevisionTree) to export
 
78
 
 
79
    :param dest: The destination where the files, etc should be put
 
80
 
 
81
    :param format: The format (dir, zip, etc), if None, it will check the
 
82
        extension on dest, looking for a match
 
83
 
 
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.
 
88
 
 
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.
 
92
 
 
93
    :param filtered: If True, content filtering is applied to the exported
 
94
        files.
 
95
 
 
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.
 
99
 
 
100
    :param fileobj: Optional file object to use
 
101
    """
 
102
    global _exporters, _exporter_extensions
 
103
 
 
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]
 
108
                break
 
109
 
 
110
    # Most of the exporters will just have to call
 
111
    # this function anyway, so why not do it for them
 
112
    if root is None:
 
113
        root = get_root_name(dest)
 
114
 
 
115
    if format not in _exporters:
 
116
        raise errors.NoSuchExportFormat(format)
 
117
 
 
118
    if not per_file_timestamps:
 
119
        force_mtime = time.time()
 
120
    else:
 
121
        force_mtime = None
 
122
 
 
123
    trace.mutter('export version %r', tree)
 
124
 
 
125
    try:
 
126
        tree.lock_read()
 
127
 
 
128
        for _ in _exporters[format](tree, dest, root, subdir,
 
129
                                    filtered=filtered,
 
130
                                    force_mtime=force_mtime, fileobj=fileobj):
 
131
 
 
132
            yield
 
133
    finally:
 
134
        tree.unlock()
 
135
 
 
136
 
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.
70
140
 
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
86
156
                     files exported.
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
90
161
    """
91
 
    global _exporters, _exporter_extensions
92
 
 
93
 
    if format is None:
94
 
        for ext in _exporter_extensions:
95
 
            if dest.endswith(ext):
96
 
                format = _exporter_extensions[ext]
97
 
                break
98
 
 
99
 
    # Most of the exporters will just have to call
100
 
    # this function anyway, so why not do it for them
101
 
    if root is None:
102
 
        root = get_root_name(dest)
103
 
 
104
 
    if format not in _exporters:
105
 
        raise errors.NoSuchExportFormat(format)
106
 
    tree.lock_read()
107
 
    try:
108
 
        return _exporters[format](tree, dest, root, subdir, filtered=filtered,
109
 
                                  per_file_timestamps=per_file_timestamps)
110
 
    finally:
111
 
        tree.unlock()
 
162
    for _ in get_export_generator(tree, dest, format, root, subdir, filtered,
 
163
                                  per_file_timestamps, fileobj):
 
164
        pass
112
165
 
113
166
 
114
167
def get_root_name(dest):
115
168
    """Get just the root name for an export.
116
169
 
117
 
    >>> get_root_name('../mytest.tar')
118
 
    'mytest'
119
 
    >>> get_root_name('mytar.tar')
120
 
    'mytar'
121
 
    >>> get_root_name('mytar.tar.bz2')
122
 
    'mytar'
123
 
    >>> get_root_name('tar.tar.tar.tgz')
124
 
    'tar.tar.tar'
125
 
    >>> get_root_name('bzr-0.0.5.tar.gz')
126
 
    'bzr-0.0.5'
127
 
    >>> get_root_name('bzr-0.0.5.zip')
128
 
    'bzr-0.0.5'
129
 
    >>> get_root_name('bzr-0.0.5')
130
 
    'bzr-0.0.5'
131
 
    >>> get_root_name('a/long/path/mytar.tgz')
132
 
    'mytar'
133
 
    >>> get_root_name('../parent/../dir/other.tbz2')
134
 
    'other'
135
170
    """
136
171
    global _exporter_extensions
 
172
    if dest == '-':
 
173
        # Exporting to -/foo doesn't make sense so use relative paths.
 
174
        return ''
137
175
    dest = os.path.basename(dest)
138
176
    for ext in _exporter_extensions:
139
177
        if dest.endswith(ext):
141
179
    return dest
142
180
 
143
181
 
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.
146
184
 
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.
149
188
    """
150
 
    inv = tree.inventory
151
 
    if subdir is None:
152
 
        subdir_object = None
153
 
    else:
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
158
 
        else:
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
162
 
        return
163
 
    else:
164
 
        entries = inv.iter_entries(subdir_object)
165
 
    if subdir is None:
166
 
        entries.next() # skip root
167
 
    for entry in entries:
 
189
    if subdir == '':
 
190
        subdir = None
 
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"):
171
199
            continue
172
 
        if subdir is None:
173
 
            if not tree.has_filename(entry[0]):
 
200
        if path == subdir:
 
201
            if entry.kind == 'directory':
 
202
                continue
 
203
            final_path = entry.name
 
204
        elif subdir is not None:
 
205
            if path.startswith(subdir + '/'):
 
206
                final_path = path[len(subdir) + 1:]
 
207
            else:
174
208
                continue
175
209
        else:
176
 
            if not tree.has_filename(os.path.join(subdir, entry[0])):
177
 
                continue
178
 
        yield entry
179
 
 
180
 
 
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')
187
 
 
 
210
            final_path = path
 
211
        if not tree.has_filename(path):
 
212
            continue
 
213
 
 
214
        yield final_path, entry
 
215
 
 
216
 
 
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')