/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: Jelmer Vernooij
  • Date: 2018-06-04 20:14:52 UTC
  • mto: This revision was merged to the branch mainline in revision 6983.
  • Revision ID: jelmer@jelmer.uk-20180604201452-u3b7srs176yua5jw
Implement get_file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008, 2009, 2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008-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 a Tree to a non-versioned directory.
18
 
"""
19
 
 
20
 
import StringIO
 
17
"""Export a tree to a tarball."""
 
18
 
 
19
from __future__ import absolute_import
 
20
 
 
21
from contextlib import closing
 
22
import os
21
23
import sys
22
24
import tarfile
23
 
import time
24
 
 
25
 
from bzrlib import export, osutils
26
 
from bzrlib.export import _export_iter_entries
27
 
from bzrlib.filters import (
28
 
    ContentFilterContext,
29
 
    filtered_output_bytes,
30
 
    )
31
 
from bzrlib.trace import mutter
32
 
 
33
 
 
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.
37
 
 
38
 
    `dest` will be created holding the contents of this tree; if it
39
 
    already exists, it will be clobbered, like with "tar -c".
40
 
    """
41
 
    mutter('export version %r', tree)
42
 
    now = time.time()
43
 
    compression = str(compression or '')
44
 
    if dest == '-':
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)
48
 
    else:
49
 
        if root is None:
50
 
            root = export.get_root_name(dest)
51
 
 
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)
58
 
 
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)
64
 
        else:
65
 
            item.mtime = now
66
 
        if ie.kind == "file":
67
 
            item.type = tarfile.REGTYPE
68
 
            if tree.is_executable(ie.file_id):
69
 
                item.mode = 0755
70
 
            else:
71
 
                item.mode = 0644
72
 
            if filtered:
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)
80
 
            else:
81
 
                item.size = ie.text_size
82
 
                fileobj = tree.get_file(ie.file_id)
83
 
        elif ie.kind == "directory":
84
 
            item.type = tarfile.DIRTYPE
85
 
            item.name += '/'
86
 
            item.size = 0
87
 
            item.mode = 0755
88
 
            fileobj = None
89
 
        elif ie.kind == "symlink":
90
 
            item.type = tarfile.SYMTYPE
91
 
            item.size = 0
92
 
            item.mode = 0755
93
 
            item.linkname = ie.symlink_target
94
 
            fileobj = None
95
 
        else:
96
 
            raise BzrError("don't know how to export {%s} of kind %r" %
97
 
                           (ie.file_id, ie.kind))
98
 
        ball.addfile(item, fileobj)
99
 
    ball.close()
100
 
 
101
 
 
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)
106
 
 
107
 
 
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)
 
25
 
 
26
from .. import (
 
27
    errors,
 
28
    osutils,
 
29
    )
 
30
from ..export import _export_iter_entries
 
31
from ..sixish import (
 
32
    BytesIO,
 
33
    )
 
34
 
 
35
 
 
36
def prepare_tarball_item(tree, root, final_path, tree_path, entry, force_mtime=None):
 
37
    """Prepare a tarball item for exporting
 
38
 
 
39
    :param tree: Tree to export
 
40
    :param final_path: Final path to place item
 
41
    :param tree_path: Path for the entry in the tree
 
42
    :param entry: Entry to export
 
43
    :param force_mtime: Option mtime to force, instead of using tree
 
44
        timestamps.
 
45
 
 
46
    Returns a (tarinfo, fileobj) tuple
 
47
    """
 
48
    filename = osutils.pathjoin(root, final_path).encode('utf8')
 
49
    item = tarfile.TarInfo(filename)
 
50
    if force_mtime is not None:
 
51
        item.mtime = force_mtime
 
52
    else:
 
53
        item.mtime = tree.get_file_mtime(tree_path, entry.file_id)
 
54
    if entry.kind == "file":
 
55
        item.type = tarfile.REGTYPE
 
56
        if tree.is_executable(tree_path, entry.file_id):
 
57
            item.mode = 0o755
 
58
        else:
 
59
            item.mode = 0o644
 
60
        # This brings the whole file into memory, but that's almost needed for
 
61
        # the tarfile contract, which wants the size of the file up front.  We
 
62
        # want to make sure it doesn't change, and we need to read it in one
 
63
        # go for content filtering.
 
64
        content = tree.get_file_text(tree_path, entry.file_id)
 
65
        item.size = len(content)
 
66
        fileobj = BytesIO(content)
 
67
    elif entry.kind in ("directory", "tree-reference"):
 
68
        item.type = tarfile.DIRTYPE
 
69
        item.name += '/'
 
70
        item.size = 0
 
71
        item.mode = 0o755
 
72
        fileobj = None
 
73
    elif entry.kind == "symlink":
 
74
        item.type = tarfile.SYMTYPE
 
75
        item.size = 0
 
76
        item.mode = 0o755
 
77
        item.linkname = tree.get_symlink_target(tree_path, entry.file_id)
 
78
        fileobj = None
 
79
    else:
 
80
        raise errors.BzrError("don't know how to export {%s} of kind %r"
 
81
                              % (entry.file_id, entry.kind))
 
82
    return (item, fileobj)
 
83
 
 
84
 
 
85
def tarball_generator(tree, root, subdir=None, force_mtime=None, format=''):
 
86
    """Export tree contents to a tarball.
 
87
 
 
88
    :returns: A generator that will produce file content chunks.
 
89
 
 
90
    :param tree: Tree to export
 
91
 
 
92
    :param subdir: Sub directory to export
 
93
 
 
94
    :param force_mtime: Option mtime to force, instead of using tree
 
95
        timestamps.
 
96
    """
 
97
    buf = BytesIO()
 
98
    with closing(tarfile.open(None, "w:%s" % format, buf)) as ball, tree.lock_read():
 
99
        for final_path, tree_path, entry in _export_iter_entries(tree, subdir):
 
100
            (item, fileobj) = prepare_tarball_item(
 
101
                tree, root, final_path, tree_path, entry, force_mtime)
 
102
            ball.addfile(item, fileobj)
 
103
            # Yield the data that was written so far, rinse, repeat.
 
104
            yield buf.getvalue()
 
105
            buf.truncate(0)
 
106
            buf.seek(0)
 
107
    yield buf.getvalue()
 
108
 
 
109
 
 
110
def tgz_generator(tree, dest, root, subdir, force_mtime=None):
 
111
    """Export this tree to a new tar file.
 
112
 
 
113
    `dest` will be created holding the contents of this tree; if it
 
114
    already exists, it will be clobbered, like with "tar -c".
 
115
    """
 
116
    with tree.lock_read():
 
117
        import gzip
 
118
        if force_mtime is not None:
 
119
            root_mtime = force_mtime
 
120
        elif (getattr(tree, "repository", None) and
 
121
              getattr(tree, "get_revision_id", None)):
 
122
            # If this is a revision tree, use the revisions' timestamp
 
123
            rev = tree.repository.get_revision(tree.get_revision_id())
 
124
            root_mtime = rev.timestamp
 
125
        elif tree.get_root_id() is not None:
 
126
            root_mtime = tree.get_file_mtime('', tree.get_root_id())
 
127
        else:
 
128
            root_mtime = None
 
129
 
 
130
        is_stdout = False
 
131
        basename = None
 
132
        # gzip file is used with an explicit fileobj so that
 
133
        # the basename can be stored in the gzip file rather than
 
134
        # dest. (bug 102234)
 
135
        basename = os.path.basename(dest)
 
136
        buf = BytesIO()
 
137
        zipstream = gzip.GzipFile(basename, 'w', fileobj=buf,
 
138
                                  mtime=root_mtime)
 
139
        for chunk in tarball_generator(
 
140
            tree, root, subdir, force_mtime):
 
141
            zipstream.write(chunk)
 
142
            # Yield the data that was written so far, rinse, repeat.
 
143
            yield buf.getvalue()
 
144
            buf.truncate(0)
 
145
            buf.seek(0)
 
146
        # Closing zipstream may trigger writes to stream
 
147
        zipstream.close()
 
148
        yield buf.getvalue()
 
149
 
 
150
 
 
151
def tbz_generator(tree, dest, root, subdir, force_mtime=None):
 
152
    """Export this tree to a new tar file.
 
153
 
 
154
    `dest` will be created holding the contents of this tree; if it
 
155
    already exists, it will be clobbered, like with "tar -c".
 
156
    """
 
157
    return tarball_generator(
 
158
        tree, root, subdir, force_mtime, format='bz2')
 
159
 
 
160
 
 
161
def plain_tar_generator(tree, dest, root, subdir,
 
162
    force_mtime=None):
 
163
    """Export this tree to a new tar file.
 
164
 
 
165
    `dest` will be created holding the contents of this tree; if it
 
166
    already exists, it will be clobbered, like with "tar -c".
 
167
    """
 
168
    return tarball_generator(
 
169
        tree, root, subdir, force_mtime, format='')
 
170
 
 
171
 
 
172
def tar_xz_generator(tree, dest, root, subdir, force_mtime=None):
 
173
    return tar_lzma_generator(tree, dest, root, subdir, force_mtime, "xz")
 
174
 
 
175
 
 
176
def tar_lzma_generator(tree, dest, root, subdir, force_mtime=None,
 
177
                       compression_format="alone"):
 
178
    """Export this tree to a new .tar.lzma file.
 
179
 
 
180
    `dest` will be created holding the contents of this tree; if it
 
181
    already exists, it will be clobbered, like with "tar -c".
 
182
    """
 
183
    try:
 
184
        import lzma
 
185
    except ImportError as e:
 
186
        raise errors.DependencyNotPresent('lzma', e)
 
187
 
 
188
    compressor = lzma.LZMACompressor(
 
189
            options={"format": 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()