~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to upstream_import.py

  • Committer: Aaron Bentley
  • Date: 2013-08-20 03:02:43 UTC
  • Revision ID: aaron@aaronbentley.com-20130820030243-r8v1xfbcnd8f10p4
Fix zap command for 2.6/7

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
"""Import upstream source into a branch"""
2
2
 
3
 
from bz2 import BZ2File
4
3
import errno
5
4
import os
 
5
import re
6
6
from StringIO import StringIO
7
7
import stat
8
8
import tarfile
12
12
from bzrlib.bzrdir import BzrDir
13
13
from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError
14
14
from bzrlib.osutils import (pathjoin, isdir, file_iterator, basename,
15
 
                            file_kind)
 
15
                            file_kind, splitpath)
16
16
from bzrlib.trace import warning
17
17
from bzrlib.transform import TreeTransform, resolve_conflicts, cook_conflicts
18
18
from bzrlib.workingtree import WorkingTree
19
19
from bzrlib.plugins.bzrtools.bzrtools import open_from_url
 
20
from bzrlib.plugins.bzrtools import errors
20
21
 
21
22
class ZipFileWrapper(object):
22
23
 
117
118
            return False
118
119
 
119
120
 
120
 
def top_directory(path):
 
121
def top_path(path):
121
122
    """Return the top directory given in a path."""
122
 
    dirname = os.path.dirname(path)
123
 
    last_dirname = dirname
124
 
    while True:
125
 
        dirname = os.path.dirname(dirname)
126
 
        if dirname == '' or dirname == last_dirname:
127
 
            return last_dirname
128
 
        last_dirname = dirname
 
123
    components = splitpath(path)
 
124
    if len(components) > 0:
 
125
        return components[0]
 
126
    else:
 
127
        return ''
129
128
 
130
129
 
131
130
def common_directory(names):
132
131
    """Determine a single directory prefix from a list of names"""
133
132
    possible_prefix = None
134
133
    for name in names:
135
 
        name_top = top_directory(name)
 
134
        name_top = top_path(name)
136
135
        if name_top == '':
137
136
            return None
138
137
        if possible_prefix is None:
165
164
            yield member.name
166
165
 
167
166
 
 
167
def should_ignore(relative_path):
 
168
    return top_path(relative_path) == '.bzr'
 
169
 
 
170
 
168
171
def import_tar(tree, tar_input):
169
172
    """Replace the contents of a working directory with tarfile contents.
170
173
    The tarfile may be a gzipped stream.  File ids will be updated.
180
183
    dir_file = DirWrapper(dir_input)
181
184
    import_archive(tree, dir_file)
182
185
 
 
186
 
183
187
def import_archive(tree, archive_file):
 
188
    tt = TreeTransform(tree)
 
189
    try:
 
190
        import_archive_to_transform(tree, archive_file, tt)
 
191
        tt.apply()
 
192
    finally:
 
193
        tt.finalize()
 
194
 
 
195
 
 
196
def import_archive_to_transform(tree, archive_file, tt):
184
197
    prefix = common_directory(names_of_files(archive_file))
185
 
    tt = TreeTransform(tree)
186
 
 
187
198
    removed = set()
188
 
    for path, entry in tree.inventory.iter_entries():
 
199
    for path, entry in tree.iter_entries_by_dir():
189
200
        if entry.parent_id is None:
190
201
            continue
191
202
        trans_id = tt.trans_id_tree_path(path)
199
210
        if member.type == 'g':
200
211
            # type 'g' is a header
201
212
            continue
202
 
        relative_path = member.name
 
213
        # Inverse functionality in bzr uses utf-8.  We could also
 
214
        # interpret relative to fs encoding, which would match native
 
215
        # behaviour better.
 
216
        relative_path = member.name.decode('utf-8')
203
217
        if prefix is not None:
204
218
            relative_path = relative_path[len(prefix)+1:]
205
219
            relative_path = relative_path.rstrip('/')
206
220
        if relative_path == '':
207
221
            continue
 
222
        if should_ignore(relative_path):
 
223
            continue
208
224
        add_implied_parents(implied_parents, relative_path)
209
225
        trans_id = tt.trans_id_tree_path(relative_path)
210
226
        added.add(relative_path.rstrip('/'))
245
261
 
246
262
    for conflict in cook_conflicts(resolve_conflicts(tt), tt):
247
263
        warning(conflict)
248
 
    tt.apply()
249
264
 
250
265
 
251
266
def do_import(source, tree_directory=None):
265
280
        if tree.changes_from(tree.basis_tree()).has_changed():
266
281
            raise BzrCommandError("Working tree has uncommitted changes.")
267
282
 
268
 
        if (source.endswith('.tar') or source.endswith('.tar.gz') or
269
 
            source.endswith('.tar.bz2')) or source.endswith('.tgz'):
270
 
            try:
271
 
                tar_input = open_from_url(source)
272
 
                if source.endswith('.bz2'):
273
 
                    tar_input = StringIO(tar_input.read().decode('bz2'))
274
 
            except IOError, e:
275
 
                if e.errno == errno.ENOENT:
276
 
                    raise NoSuchFile(source)
277
 
            try:
278
 
                import_tar(tree, tar_input)
279
 
            finally:
280
 
                tar_input.close()
281
 
        elif source.endswith('.zip'):
282
 
            import_zip(tree, open_from_url(source))
283
 
        elif file_kind(source) == 'directory':
284
 
            s = StringIO(source)
285
 
            s.seek(0)
286
 
            import_dir(tree, s)
 
283
        try:
 
284
            archive, external_compressor = get_archive_type(source)
 
285
        except errors.NotArchiveType:
 
286
            if file_kind(source) == 'directory':
 
287
                s = StringIO(source)
 
288
                s.seek(0)
 
289
                import_dir(tree, s)
 
290
            else:
 
291
                raise BzrCommandError('Unhandled import source')
287
292
        else:
288
 
            raise BzrCommandError('Unhandled import source')
 
293
            if archive == 'zip':
 
294
                import_zip(tree, open_from_url(source))
 
295
            elif archive == 'tar':
 
296
                try:
 
297
                    tar_input = open_from_url(source)
 
298
                    if external_compressor == 'bz2':
 
299
                        import bz2
 
300
                        tar_input = StringIO(bz2.decompress(tar_input.read()))
 
301
                    elif external_compressor == 'lzma':
 
302
                        import lzma
 
303
                        tar_input = StringIO(lzma.decompress(tar_input.read()))
 
304
                except IOError, e:
 
305
                    if e.errno == errno.ENOENT:
 
306
                        raise NoSuchFile(source)
 
307
                try:
 
308
                    import_tar(tree, tar_input)
 
309
                finally:
 
310
                    tar_input.close()
289
311
    finally:
290
312
        tree.unlock()
 
313
 
 
314
 
 
315
def get_archive_type(path):
 
316
    """Return the type of archive and compressor indicated by path name.
 
317
 
 
318
    Only external compressors are returned, so zip files are only
 
319
    ('zip', None).  .tgz is treated as ('tar', 'gz') and '.tar.xz' is treated
 
320
    as ('tar', 'lzma').
 
321
    """
 
322
    matches = re.match(r'.*\.(zip|tgz|tar(.(gz|bz2|lzma|xz))?)$', path)
 
323
    if not matches:
 
324
        raise errors.NotArchiveType(path)
 
325
    external_compressor = None
 
326
    if matches.group(3) is not None:
 
327
        archive = 'tar'
 
328
        external_compressor = matches.group(3)
 
329
        if external_compressor == 'xz':
 
330
            external_compressor = 'lzma'
 
331
    elif matches.group(1) == 'tgz':
 
332
        return 'tar', 'gz'
 
333
    else:
 
334
        archive = matches.group(1)
 
335
    return archive, external_compressor