/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: Gustav Hartvigsson
  • Date: 2021-01-09 21:36:27 UTC
  • Revision ID: gustav.hartvigsson@gmail.com-20210109213627-h1xwcutzy9m7a99b
Added 'Case Preserving Working Tree Use Cases' from Canonical Wiki

* Addod a page from the Canonical Bazaar wiki
  with information on the scmeatics of case
  perserving filesystems an a case insensitive
  filesystem works.
  
  * Needs re-work, but this will do as it is the
    same inforamoton as what was on the linked
    page in the currint documentation.

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
 
"""
 
17
"""Export a tree to a tarball."""
19
18
 
20
 
import StringIO
 
19
from contextlib import closing
 
20
from io import BytesIO
 
21
import os
21
22
import sys
22
23
import tarfile
23
 
import time
24
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,
 
25
from .. import (
 
26
    errors,
 
27
    osutils,
30
28
    )
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)
 
29
from ..export import _export_iter_entries
 
30
 
 
31
 
 
32
def prepare_tarball_item(tree, root, final_path, tree_path, entry, force_mtime=None):
 
33
    """Prepare a tarball item for exporting
 
34
 
 
35
    :param tree: Tree to export
 
36
    :param final_path: Final path to place item
 
37
    :param tree_path: Path for the entry in the tree
 
38
    :param entry: Entry to export
 
39
    :param force_mtime: Option mtime to force, instead of using tree
 
40
        timestamps.
 
41
 
 
42
    Returns a (tarinfo, fileobj) tuple
 
43
    """
 
44
    file_id = getattr(entry, 'file_id', None)
 
45
    filename = osutils.pathjoin(root, final_path)
 
46
    item = tarfile.TarInfo(filename)
 
47
    if force_mtime is not None:
 
48
        item.mtime = force_mtime
 
49
    else:
 
50
        item.mtime = tree.get_file_mtime(tree_path)
 
51
    if entry.kind == "file":
 
52
        item.type = tarfile.REGTYPE
 
53
        if tree.is_executable(tree_path):
 
54
            item.mode = 0o755
 
55
        else:
 
56
            item.mode = 0o644
 
57
        # This brings the whole file into memory, but that's almost needed for
 
58
        # the tarfile contract, which wants the size of the file up front.  We
 
59
        # want to make sure it doesn't change, and we need to read it in one
 
60
        # go for content filtering.
 
61
        content = tree.get_file_text(tree_path)
 
62
        item.size = len(content)
 
63
        fileobj = BytesIO(content)
 
64
    elif entry.kind in ("directory", "tree-reference"):
 
65
        item.type = tarfile.DIRTYPE
 
66
        item.name += '/'
 
67
        item.size = 0
 
68
        item.mode = 0o755
 
69
        fileobj = None
 
70
    elif entry.kind == "symlink":
 
71
        item.type = tarfile.SYMTYPE
 
72
        item.size = 0
 
73
        item.mode = 0o755
 
74
        item.linkname = tree.get_symlink_target(tree_path)
 
75
        fileobj = None
 
76
    else:
 
77
        raise errors.BzrError("don't know how to export {%s} of kind %r"
 
78
                              % (file_id, entry.kind))
 
79
    return (item, fileobj)
 
80
 
 
81
 
 
82
def tarball_generator(tree, root, subdir=None, force_mtime=None, format=''):
 
83
    """Export tree contents to a tarball.
 
84
 
 
85
    :returns: A generator that will produce file content chunks.
 
86
 
 
87
    :param tree: Tree to export
 
88
 
 
89
    :param subdir: Sub directory to export
 
90
 
 
91
    :param force_mtime: Option mtime to force, instead of using tree
 
92
        timestamps.
 
93
    """
 
94
    buf = BytesIO()
 
95
    with closing(tarfile.open(None, "w:%s" % format, buf)) as ball, tree.lock_read():
 
96
        for final_path, tree_path, entry in _export_iter_entries(tree, subdir):
 
97
            (item, fileobj) = prepare_tarball_item(
 
98
                tree, root, final_path, tree_path, entry, force_mtime)
 
99
            ball.addfile(item, fileobj)
 
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):
 
108
    """Export this tree to a new tar file.
 
109
 
 
110
    `dest` will be created holding the contents of this tree; if it
 
111
    already exists, it will be clobbered, like with "tar -c".
 
112
    """
 
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
 
126
 
 
127
        is_stdout = False
 
128
        basename = None
 
129
        # gzip file is used with an explicit fileobj so that
 
130
        # the basename can be stored in the gzip file rather than
 
131
        # dest. (bug 102234)
 
132
        basename = os.path.basename(dest)
 
133
        buf = BytesIO()
 
134
        zipstream = gzip.GzipFile(basename, 'w', fileobj=buf,
 
135
                                  mtime=root_mtime)
 
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"):
 
174
    """Export this tree to a new .tar.lzma file.
 
175
 
 
176
    `dest` will be created holding the contents of this tree; if it
 
177
    already exists, it will be clobbered, like with "tar -c".
 
178
    """
 
179
    try:
 
180
        import lzma
 
181
    except ImportError as e:
 
182
        raise errors.DependencyNotPresent('lzma', e)
 
183
 
 
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()