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