/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/archive/tar.py

  • Committer: Breezy landing bot
  • Author(s): Colin Watson
  • Date: 2020-11-16 21:47:08 UTC
  • mfrom: (7521.1.1 remove-lp-workaround)
  • Revision ID: breezy.the.bot@gmail.com-20201116214708-jos209mgxi41oy15
Remove breezy.git workaround for bazaar.launchpad.net.

Merged from https://code.launchpad.net/~cjwatson/brz/remove-lp-workaround/+merge/393710

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Export a tree to a tarball."""
18
18
 
19
 
from __future__ import absolute_import
20
 
 
 
19
from contextlib import closing
 
20
from io import BytesIO
21
21
import os
22
 
import StringIO
23
22
import sys
24
23
import tarfile
25
24
 
26
 
from bzrlib import (
 
25
from .. import (
27
26
    errors,
28
27
    osutils,
29
28
    )
30
 
from bzrlib.export import _export_iter_entries
 
29
from ..export import _export_iter_entries
31
30
 
32
31
 
33
32
def prepare_tarball_item(tree, root, final_path, tree_path, entry, force_mtime=None):
42
41
 
43
42
    Returns a (tarinfo, fileobj) tuple
44
43
    """
45
 
    filename = osutils.pathjoin(root, final_path).encode('utf8')
 
44
    file_id = getattr(entry, 'file_id', None)
 
45
    filename = osutils.pathjoin(root, final_path)
46
46
    item = tarfile.TarInfo(filename)
47
47
    if force_mtime is not None:
48
48
        item.mtime = force_mtime
49
49
    else:
50
 
        item.mtime = tree.get_file_mtime(entry.file_id, tree_path)
 
50
        item.mtime = tree.get_file_mtime(tree_path)
51
51
    if entry.kind == "file":
52
52
        item.type = tarfile.REGTYPE
53
 
        if tree.is_executable(entry.file_id, tree_path):
54
 
            item.mode = 0755
 
53
        if tree.is_executable(tree_path):
 
54
            item.mode = 0o755
55
55
        else:
56
 
            item.mode = 0644
 
56
            item.mode = 0o644
57
57
        # This brings the whole file into memory, but that's almost needed for
58
58
        # the tarfile contract, which wants the size of the file up front.  We
59
59
        # want to make sure it doesn't change, and we need to read it in one
60
60
        # go for content filtering.
61
 
        content = tree.get_file_text(entry.file_id, tree_path)
 
61
        content = tree.get_file_text(tree_path)
62
62
        item.size = len(content)
63
 
        fileobj = StringIO.StringIO(content)
64
 
    elif entry.kind == "directory":
 
63
        fileobj = BytesIO(content)
 
64
    elif entry.kind in ("directory", "tree-reference"):
65
65
        item.type = tarfile.DIRTYPE
66
66
        item.name += '/'
67
67
        item.size = 0
68
 
        item.mode = 0755
 
68
        item.mode = 0o755
69
69
        fileobj = None
70
70
    elif entry.kind == "symlink":
71
71
        item.type = tarfile.SYMTYPE
72
72
        item.size = 0
73
 
        item.mode = 0755
74
 
        item.linkname = tree.get_symlink_target(entry.file_id, tree_path)
 
73
        item.mode = 0o755
 
74
        item.linkname = tree.get_symlink_target(tree_path)
75
75
        fileobj = None
76
76
    else:
77
77
        raise errors.BzrError("don't know how to export {%s} of kind %r"
78
 
                              % (entry.file_id, entry.kind))
 
78
                              % (file_id, entry.kind))
79
79
    return (item, fileobj)
80
80
 
81
81
 
82
 
def export_tarball_generator(tree, ball, root, subdir=None, force_mtime=None):
 
82
def tarball_generator(tree, root, subdir=None, force_mtime=None, format=''):
83
83
    """Export tree contents to a tarball.
84
84
 
85
 
    :returns: A generator that will repeatedly produce None as each file is
86
 
        emitted.  The entire generator must be consumed to complete writing
87
 
        the file.
 
85
    :returns: A generator that will produce file content chunks.
88
86
 
89
87
    :param tree: Tree to export
90
88
 
91
 
    :param ball: Tarball to export to; it will be closed when writing is
92
 
        complete.
93
 
 
94
89
    :param subdir: Sub directory to export
95
90
 
96
91
    :param force_mtime: Option mtime to force, instead of using tree
97
92
        timestamps.
98
93
    """
99
 
    try:
 
94
    buf = BytesIO()
 
95
    with closing(tarfile.open(None, "w:%s" % format, buf)) as ball, tree.lock_read():
100
96
        for final_path, tree_path, entry in _export_iter_entries(tree, subdir):
101
97
            (item, fileobj) = prepare_tarball_item(
102
98
                tree, root, final_path, tree_path, entry, force_mtime)
103
99
            ball.addfile(item, fileobj)
104
 
            yield
105
 
    finally:
106
 
        ball.close()
107
 
 
108
 
 
109
 
def tgz_exporter_generator(tree, dest, root, subdir, force_mtime=None,
110
 
    fileobj=None):
 
100
            # Yield the data that was written so far, rinse, repeat.
 
101
            yield buf.getvalue()
 
102
            buf.truncate(0)
 
103
            buf.seek(0)
 
104
    yield buf.getvalue()
 
105
 
 
106
 
 
107
def tgz_generator(tree, dest, root, subdir, force_mtime=None):
111
108
    """Export this tree to a new tar file.
112
109
 
113
110
    `dest` will be created holding the contents of this tree; if it
114
111
    already exists, it will be clobbered, like with "tar -c".
115
112
    """
116
 
    import gzip
117
 
    if force_mtime is not None:
118
 
        root_mtime = force_mtime
119
 
    elif (getattr(tree, "repository", None) and
120
 
          getattr(tree, "get_revision_id", None)):
121
 
        # If this is a revision tree, use the revisions' timestamp
122
 
        rev = tree.repository.get_revision(tree.get_revision_id())
123
 
        root_mtime = rev.timestamp
124
 
    elif tree.get_root_id() is not None:
125
 
        root_mtime = tree.get_file_mtime(tree.get_root_id())
126
 
    else:
127
 
        root_mtime = None
 
113
    with tree.lock_read():
 
114
        import gzip
 
115
        if force_mtime is not None:
 
116
            root_mtime = force_mtime
 
117
        elif (getattr(tree, "repository", None) and
 
118
              getattr(tree, "get_revision_id", None)):
 
119
            # If this is a revision tree, use the revisions' timestamp
 
120
            rev = tree.repository.get_revision(tree.get_revision_id())
 
121
            root_mtime = rev.timestamp
 
122
        elif tree.is_versioned(u''):
 
123
            root_mtime = tree.get_file_mtime('')
 
124
        else:
 
125
            root_mtime = None
128
126
 
129
 
    is_stdout = False
130
 
    basename = None
131
 
    if fileobj is not None:
132
 
        stream = fileobj
133
 
    elif dest == '-':
134
 
        stream = sys.stdout
135
 
        is_stdout = True
136
 
    else:
137
 
        stream = open(dest, 'wb')
 
127
        is_stdout = False
 
128
        basename = None
138
129
        # gzip file is used with an explicit fileobj so that
139
130
        # the basename can be stored in the gzip file rather than
140
131
        # dest. (bug 102234)
141
132
        basename = os.path.basename(dest)
142
 
    try:
143
 
        zipstream = gzip.GzipFile(basename, 'w', fileobj=stream,
 
133
        buf = BytesIO()
 
134
        zipstream = gzip.GzipFile(basename, 'w', fileobj=buf,
144
135
                                  mtime=root_mtime)
145
 
    except TypeError:
146
 
        # Python < 2.7 doesn't support the mtime argument
147
 
        zipstream = gzip.GzipFile(basename, 'w', fileobj=stream)
148
 
    ball = tarfile.open(None, 'w|', fileobj=zipstream)
149
 
    for _ in export_tarball_generator(
150
 
        tree, ball, root, subdir, force_mtime):
151
 
        yield
152
 
    # Closing zipstream may trigger writes to stream
153
 
    zipstream.close()
154
 
    if not is_stdout:
155
 
        # Now we can safely close the stream
156
 
        stream.close()
157
 
 
158
 
 
159
 
def tbz_exporter_generator(tree, dest, root, subdir,
160
 
                           force_mtime=None, fileobj=None):
161
 
    """Export this tree to a new tar file.
162
 
 
163
 
    `dest` will be created holding the contents of this tree; if it
164
 
    already exists, it will be clobbered, like with "tar -c".
165
 
    """
166
 
    if fileobj is not None:
167
 
        ball = tarfile.open(None, 'w|bz2', fileobj)
168
 
    elif dest == '-':
169
 
        ball = tarfile.open(None, 'w|bz2', sys.stdout)
170
 
    else:
171
 
        # tarfile.open goes on to do 'os.getcwd() + dest' for opening the
172
 
        # tar file. With dest being unicode, this throws UnicodeDecodeError
173
 
        # unless we encode dest before passing it on. This works around
174
 
        # upstream python bug http://bugs.python.org/issue8396 (fixed in
175
 
        # Python 2.6.5 and 2.7b1)
176
 
        ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:bz2')
177
 
    return export_tarball_generator(
178
 
        tree, ball, root, subdir, force_mtime)
179
 
 
180
 
 
181
 
def plain_tar_exporter_generator(tree, dest, root, subdir, compression=None,
182
 
    force_mtime=None, fileobj=None):
183
 
    """Export this tree to a new tar file.
184
 
 
185
 
    `dest` will be created holding the contents of this tree; if it
186
 
    already exists, it will be clobbered, like with "tar -c".
187
 
    """
188
 
    if fileobj is not None:
189
 
        stream = fileobj
190
 
    elif dest == '-':
191
 
        stream = sys.stdout
192
 
    else:
193
 
        stream = open(dest, 'wb')
194
 
    ball = tarfile.open(None, 'w|', stream)
195
 
    return export_tarball_generator(
196
 
        tree, ball, root, subdir, force_mtime)
197
 
 
198
 
 
199
 
def tar_xz_exporter_generator(tree, dest, root, subdir,
200
 
                              force_mtime=None, fileobj=None):
201
 
    return tar_lzma_exporter_generator(tree, dest, root, subdir,
202
 
                                       force_mtime, fileobj, "xz")
203
 
 
204
 
 
205
 
def tar_lzma_exporter_generator(tree, dest, root, subdir,
206
 
                      force_mtime=None, fileobj=None,
207
 
                                compression_format="alone"):
 
136
        for chunk in tarball_generator(tree, root, subdir, force_mtime):
 
137
            zipstream.write(chunk)
 
138
            # Yield the data that was written so far, rinse, repeat.
 
139
            yield buf.getvalue()
 
140
            buf.truncate(0)
 
141
            buf.seek(0)
 
142
        # Closing zipstream may trigger writes to stream
 
143
        zipstream.close()
 
144
        yield buf.getvalue()
 
145
 
 
146
 
 
147
def tbz_generator(tree, dest, root, subdir, force_mtime=None):
 
148
    """Export this tree to a new tar file.
 
149
 
 
150
    `dest` will be created holding the contents of this tree; if it
 
151
    already exists, it will be clobbered, like with "tar -c".
 
152
    """
 
153
    return tarball_generator(
 
154
        tree, root, subdir, force_mtime, format='bz2')
 
155
 
 
156
 
 
157
def plain_tar_generator(tree, dest, root, subdir,
 
158
                        force_mtime=None):
 
159
    """Export this tree to a new tar file.
 
160
 
 
161
    `dest` will be created holding the contents of this tree; if it
 
162
    already exists, it will be clobbered, like with "tar -c".
 
163
    """
 
164
    return tarball_generator(
 
165
        tree, root, subdir, force_mtime, format='')
 
166
 
 
167
 
 
168
def tar_xz_generator(tree, dest, root, subdir, force_mtime=None):
 
169
    return tar_lzma_generator(tree, dest, root, subdir, force_mtime, "xz")
 
170
 
 
171
 
 
172
def tar_lzma_generator(tree, dest, root, subdir, force_mtime=None,
 
173
                       compression_format="alone"):
208
174
    """Export this tree to a new .tar.lzma file.
209
175
 
210
176
    `dest` will be created holding the contents of this tree; if it
211
177
    already exists, it will be clobbered, like with "tar -c".
212
178
    """
213
 
    if dest == '-':
214
 
        raise errors.BzrError("Writing to stdout not supported for .tar.lzma")
215
 
 
216
 
    if fileobj is not None:
217
 
        raise errors.BzrError(
218
 
            "Writing to fileobject not supported for .tar.lzma")
219
179
    try:
220
180
        import lzma
221
 
    except ImportError, e:
 
181
    except ImportError as e:
222
182
        raise errors.DependencyNotPresent('lzma', e)
223
183
 
224
 
    stream = lzma.LZMAFile(dest.encode(osutils._fs_enc), 'w',
225
 
        options={"format": compression_format})
226
 
    ball = tarfile.open(None, 'w:', fileobj=stream)
227
 
    return export_tarball_generator(
228
 
        tree, ball, root, subdir, force_mtime=force_mtime)
 
184
    compressor = lzma.LZMACompressor(
 
185
        format={
 
186
            'xz': lzma.FORMAT_XZ,
 
187
            'raw': lzma.FORMAT_RAW,
 
188
            'alone': lzma.FORMAT_ALONE,
 
189
            }[compression_format])
 
190
 
 
191
    for chunk in tarball_generator(
 
192
            tree, root, subdir, force_mtime=force_mtime):
 
193
        yield compressor.compress(chunk)
 
194
 
 
195
    yield compressor.flush()