/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
1
# Copyright (C) 2006-2012 Aaron Bentley
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
17
"""Import upstream source into a branch"""
18
19
import errno
7045.2.19 by Jelmer Vernooij
Fix some more tests.
20
from io import (
21
    BytesIO,
22
    )
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
23
import os
24
import re
25
import stat
26
import tarfile
27
import zipfile
28
7265.5.1 by Jelmer Vernooij
Move generate_ids to breezy.bzr.
29
from . import urlutils
30
from .bzr import generate_ids
7311.2.3 by Jelmer Vernooij
Drop find_trees.
31
from .controldir import ControlDir, is_control_filename
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
32
from .errors import (BzrError, NoSuchFile, CommandError, NotBranchError)
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
33
from .osutils import (pathjoin, isdir, file_iterator, basename,
34
                      file_kind, splitpath)
35
from .trace import warning
7490.129.1 by Jelmer Vernooij
Make cook_conflicts a member of Transform.
36
from .transform import resolve_conflicts
6637.1.2 by Jelmer Vernooij
Add tests.
37
from .transport import get_transport
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
38
from .workingtree import WorkingTree
39
40
41
# TODO(jelmer): Move this to transport.py ?
42
def open_from_url(location):
43
    location = urlutils.normalize_url(location)
44
    dirname, basename = urlutils.split(location)
45
    if location.endswith('/') and not basename.endswith('/'):
46
        basename += '/'
47
    return get_transport(dirname).get(basename)
48
49
50
class NotArchiveType(BzrError):
51
52
    _fmt = '%(path)s is not an archive.'
53
54
    def __init__(self, path):
55
        BzrError.__init__(self)
56
        self.path = path
57
58
59
class ZipFileWrapper(object):
60
61
    def __init__(self, fileobj, mode):
62
        self.zipfile = zipfile.ZipFile(fileobj, mode)
63
64
    def getmembers(self):
65
        for info in self.zipfile.infolist():
66
            yield ZipInfoWrapper(self.zipfile, info)
67
68
    def extractfile(self, infowrapper):
7045.2.5 by Jelmer Vernooij
Fix po_merge and upstream_import tests.
69
        return BytesIO(self.zipfile.read(infowrapper.name))
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
70
71
    def add(self, filename):
72
        if isdir(filename):
7143.15.2 by Jelmer Vernooij
Run autopep8.
73
            self.zipfile.writestr(filename + '/', '')
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
74
        else:
75
            self.zipfile.write(filename)
76
77
    def close(self):
78
        self.zipfile.close()
79
80
81
class ZipInfoWrapper(object):
82
83
    def __init__(self, zipfile, info):
84
        self.info = info
85
        self.type = None
86
        self.name = info.filename
87
        self.zipfile = zipfile
6791.2.3 by Jelmer Vernooij
Fix more imports.
88
        self.mode = 0o666
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
89
90
    def isdir(self):
91
        # Really? Eeeew!
92
        return bool(self.name.endswith('/'))
93
94
    def isreg(self):
95
        # Really? Eeeew!
96
        return not self.isdir()
97
98
99
class DirWrapper(object):
6637.1.4 by Jelmer Vernooij
Switch back to StringIO for now.
100
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
101
    def __init__(self, fileobj, mode='r'):
6637.1.4 by Jelmer Vernooij
Switch back to StringIO for now.
102
        if mode != 'r':
103
            raise AssertionError(
104
                'only readonly supported')
7058.4.26 by Jelmer Vernooij
Fix upstream_import test.
105
        self.root = os.path.realpath(fileobj.read().decode('utf-8'))
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
106
107
    def __repr__(self):
108
        return 'DirWrapper(%r)' % self.root
109
110
    def getmembers(self, subdir=None):
111
        if subdir is not None:
112
            mydir = pathjoin(self.root, subdir)
113
        else:
114
            mydir = self.root
115
        for child in os.listdir(mydir):
116
            if subdir is not None:
117
                child = pathjoin(subdir, child)
118
            fi = FileInfo(self.root, child)
119
            yield fi
120
            if fi.isdir():
121
                for v in self.getmembers(child):
122
                    yield v
123
124
    def extractfile(self, member):
7045.3.1 by Jelmer Vernooij
Fix another ~500 tests.
125
        return open(member.fullpath, 'rb')
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
126
127
128
class FileInfo(object):
129
130
    def __init__(self, root, filepath):
131
        self.fullpath = pathjoin(root, filepath)
132
        self.root = root
133
        if filepath != '':
134
            self.name = pathjoin(basename(root), filepath)
135
        else:
6791.2.3 by Jelmer Vernooij
Fix more imports.
136
            print('root %r' % root)
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
137
            self.name = basename(root)
138
        self.type = None
139
        stat = os.lstat(self.fullpath)
140
        self.mode = stat.st_mode
141
        if self.isdir():
142
            self.name += '/'
143
144
    def __repr__(self):
145
        return 'FileInfo(%r)' % self.name
146
147
    def isreg(self):
148
        return stat.S_ISREG(self.mode)
149
150
    def isdir(self):
151
        return stat.S_ISDIR(self.mode)
152
153
    def issym(self):
154
        if stat.S_ISLNK(self.mode):
155
            self.linkname = os.readlink(self.fullpath)
156
            return True
157
        else:
158
            return False
159
160
161
def top_path(path):
162
    """Return the top directory given in a path."""
163
    components = splitpath(path)
164
    if len(components) > 0:
165
        return components[0]
166
    else:
167
        return ''
168
169
170
def common_directory(names):
171
    """Determine a single directory prefix from a list of names"""
172
    possible_prefix = None
173
    for name in names:
174
        name_top = top_path(name)
175
        if name_top == '':
176
            return None
177
        if possible_prefix is None:
178
            possible_prefix = name_top
179
        else:
180
            if name_top != possible_prefix:
181
                return None
182
    return possible_prefix
183
184
185
def do_directory(tt, trans_id, tree, relative_path, path):
6852.3.1 by Jelmer Vernooij
add Tree.is_versioned.
186
    if isdir(path) and tree.is_versioned(relative_path):
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
187
        tt.cancel_deletion(trans_id)
188
    else:
189
        tt.create_directory(trans_id)
190
191
192
def add_implied_parents(implied_parents, path):
193
    """Update the set of implied parents from a path"""
194
    parent = os.path.dirname(path)
195
    if parent in implied_parents:
196
        return
197
    implied_parents.add(parent)
198
    add_implied_parents(implied_parents, parent)
199
200
201
def names_of_files(tar_file):
202
    for member in tar_file.getmembers():
203
        if member.type != "g":
204
            yield member.name
205
206
207
def should_ignore(relative_path):
7311.2.3 by Jelmer Vernooij
Drop find_trees.
208
    return is_control_filename(top_path(relative_path))
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
209
210
211
def import_tar(tree, tar_input):
212
    """Replace the contents of a working directory with tarfile contents.
213
    The tarfile may be a gzipped stream.  File ids will be updated.
214
    """
215
    tar_file = tarfile.open('lala', 'r', tar_input)
216
    import_archive(tree, tar_file)
217
7143.15.2 by Jelmer Vernooij
Run autopep8.
218
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
219
def import_zip(tree, zip_input):
220
    zip_file = ZipFileWrapper(zip_input, 'r')
221
    import_archive(tree, zip_file)
222
7045.2.19 by Jelmer Vernooij
Fix some more tests.
223
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
224
def import_dir(tree, dir_input):
225
    dir_file = DirWrapper(dir_input)
226
    import_archive(tree, dir_file)
227
228
229
def import_archive(tree, archive_file):
7490.77.2 by Jelmer Vernooij
Split out git and bzr-specific transforms.
230
    with tree.transform() as tt:
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
231
        import_archive_to_transform(tree, archive_file, tt)
232
        tt.apply()
233
234
235
def import_archive_to_transform(tree, archive_file, tt):
236
    prefix = common_directory(names_of_files(archive_file))
237
    removed = set()
238
    for path, entry in tree.iter_entries_by_dir():
239
        if entry.parent_id is None:
240
            continue
241
        trans_id = tt.trans_id_tree_path(path)
242
        tt.delete_contents(trans_id)
243
        removed.add(path)
244
245
    added = set()
246
    implied_parents = set()
247
    seen = set()
248
    for member in archive_file.getmembers():
249
        if member.type == 'g':
250
            # type 'g' is a header
251
            continue
252
        # Inverse functionality in bzr uses utf-8.  We could also
253
        # interpret relative to fs encoding, which would match native
254
        # behaviour better.
7045.2.4 by Jelmer Vernooij
Fix some more.
255
        relative_path = member.name
7479.2.1 by Jelmer Vernooij
Drop python2 support.
256
        if not isinstance(relative_path, str):
7045.2.4 by Jelmer Vernooij
Fix some more.
257
            relative_path = relative_path.decode('utf-8')
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
258
        if prefix is not None:
7143.15.2 by Jelmer Vernooij
Run autopep8.
259
            relative_path = relative_path[len(prefix) + 1:]
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
260
            relative_path = relative_path.rstrip('/')
261
        if relative_path == '':
262
            continue
263
        if should_ignore(relative_path):
264
            continue
265
        add_implied_parents(implied_parents, relative_path)
266
        trans_id = tt.trans_id_tree_path(relative_path)
267
        added.add(relative_path.rstrip('/'))
268
        path = tree.abspath(relative_path)
269
        if member.name in seen:
270
            if tt.final_kind(trans_id) == 'file':
271
                tt.set_executability(None, trans_id)
272
            tt.cancel_creation(trans_id)
273
        seen.add(member.name)
274
        if member.isreg():
275
            tt.create_file(file_iterator(archive_file.extractfile(member)),
276
                           trans_id)
6791.2.3 by Jelmer Vernooij
Fix more imports.
277
            executable = (member.mode & 0o111) != 0
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
278
            tt.set_executability(executable, trans_id)
279
        elif member.isdir():
280
            do_directory(tt, trans_id, tree, relative_path, path)
281
        elif member.issym():
282
            tt.create_symlink(member.linkname, trans_id)
283
        else:
284
            continue
285
        if tt.tree_file_id(trans_id) is None:
286
            name = basename(member.name.rstrip('/'))
287
            file_id = generate_ids.gen_file_id(name)
7490.77.8 by Jelmer Vernooij
Change argument order for Transform.version_file.
288
            tt.version_file(trans_id, file_id=file_id)
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
289
290
    for relative_path in implied_parents.difference(added):
291
        if relative_path == "":
292
            continue
293
        trans_id = tt.trans_id_tree_path(relative_path)
294
        path = tree.abspath(relative_path)
295
        do_directory(tt, trans_id, tree, relative_path, path)
296
        if tt.tree_file_id(trans_id) is None:
7490.77.8 by Jelmer Vernooij
Change argument order for Transform.version_file.
297
            tt.version_file(trans_id, file_id=trans_id)
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
298
        added.add(relative_path)
299
300
    for path in removed.difference(added):
301
        tt.unversion_file(tt.trans_id_tree_path(path))
302
7490.129.1 by Jelmer Vernooij
Make cook_conflicts a member of Transform.
303
    for conflict in tt.cook_conflicts(resolve_conflicts(tt)):
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
304
        warning(conflict)
305
306
307
def do_import(source, tree_directory=None):
308
    """Implementation of import command.  Intended for UI only"""
309
    if tree_directory is not None:
310
        try:
311
            tree = WorkingTree.open(tree_directory)
312
        except NotBranchError:
313
            if not os.path.exists(tree_directory):
314
                os.mkdir(tree_directory)
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
315
            branch = ControlDir.create_branch_convenience(tree_directory)
6653.6.1 by Jelmer Vernooij
Rename a number of attributes from bzrdir to controldir.
316
            tree = branch.controldir.open_workingtree()
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
317
    else:
318
        tree = WorkingTree.open_containing('.')[0]
6969.3.2 by Jelmer Vernooij
Use context managers for locking.
319
    with tree.lock_write():
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
320
        if tree.changes_from(tree.basis_tree()).has_changed():
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
321
            raise CommandError("Working tree has uncommitted changes.")
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
322
323
        try:
324
            archive, external_compressor = get_archive_type(source)
6637.1.2 by Jelmer Vernooij
Add tests.
325
        except NotArchiveType:
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
326
            if file_kind(source) == 'directory':
7058.4.29 by Jelmer Vernooij
Fix remaining test.
327
                s = BytesIO(source.encode('utf-8'))
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
328
                s.seek(0)
329
                import_dir(tree, s)
330
            else:
7490.61.1 by Jelmer Vernooij
Rename BzrCommandError to CommandError.
331
                raise CommandError('Unhandled import source')
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
332
        else:
333
            if archive == 'zip':
334
                import_zip(tree, open_from_url(source))
335
            elif archive == 'tar':
336
                try:
337
                    tar_input = open_from_url(source)
338
                    if external_compressor == 'bz2':
339
                        import bz2
7045.2.18 by Jelmer Vernooij
Some fixes.
340
                        tar_input = BytesIO(bz2.decompress(tar_input.read()))
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
341
                    elif external_compressor == 'lzma':
342
                        import lzma
7045.2.18 by Jelmer Vernooij
Some fixes.
343
                        tar_input = BytesIO(lzma.decompress(tar_input.read()))
6791.2.3 by Jelmer Vernooij
Fix more imports.
344
                except IOError as e:
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
345
                    if e.errno == errno.ENOENT:
346
                        raise NoSuchFile(source)
347
                try:
348
                    import_tar(tree, tar_input)
349
                finally:
350
                    tar_input.close()
351
352
353
def get_archive_type(path):
354
    """Return the type of archive and compressor indicated by path name.
355
356
    Only external compressors are returned, so zip files are only
357
    ('zip', None).  .tgz is treated as ('tar', 'gz') and '.tar.xz' is treated
358
    as ('tar', 'lzma').
359
    """
360
    matches = re.match(r'.*\.(zip|tgz|tar(.(gz|bz2|lzma|xz))?)$', path)
361
    if not matches:
6637.1.2 by Jelmer Vernooij
Add tests.
362
        raise NotArchiveType(path)
6637.1.1 by Jelmer Vernooij
Bundle upstream_import.
363
    external_compressor = None
364
    if matches.group(3) is not None:
365
        archive = 'tar'
366
        external_compressor = matches.group(3)
367
        if external_compressor == 'xz':
368
            external_compressor = 'lzma'
369
    elif matches.group(1) == 'tgz':
370
        return 'tar', 'gz'
371
    else:
372
        archive = matches.group(1)
373
    return archive, external_compressor