1
1
"""Import upstream source into a branch"""
3
from bz2 import BZ2File
6
from shutil import rmtree
7
6
from StringIO import StringIO
9
from unittest import makeSuite
11
from bzrlib import generate_ids
12
12
from bzrlib.bzrdir import BzrDir
13
13
from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError
14
from bzrlib.osutils import pathjoin, isdir, file_iterator
15
from bzrlib.tests import TestCaseInTempDir
14
from bzrlib.osutils import (pathjoin, isdir, file_iterator, basename,
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
from bzrlib.plugins.bzrtools.bzrtools import open_from_url
20
from bzrlib.plugins.bzrtools import errors
20
22
class ZipFileWrapper(object):
22
def __init__(self, zipfile):
23
self.zipfile = zipfile
24
def __init__(self, fileobj, mode):
25
self.zipfile = zipfile.ZipFile(fileobj, mode)
25
27
def getmembers(self):
26
28
for info in self.zipfile.infolist():
57
59
return not self.isdir()
60
def top_directory(path):
62
class DirWrapper(object):
63
def __init__(self, fileobj, mode='r'):
64
assert mode == 'r', mode
65
self.root = os.path.realpath(fileobj.read())
68
return 'DirWrapper(%r)' % self.root
70
def getmembers(self, subdir=None):
71
if subdir is not None:
72
mydir = pathjoin(self.root, subdir)
75
for child in os.listdir(mydir):
76
if subdir is not None:
77
child = pathjoin(subdir, child)
78
fi = FileInfo(self.root, child)
81
for v in self.getmembers(child):
84
def extractfile(self, member):
85
return open(member.fullpath)
88
class FileInfo(object):
90
def __init__(self, root, filepath):
91
self.fullpath = pathjoin(root, filepath)
94
self.name = pathjoin(basename(root), filepath)
96
print 'root %r' % root
97
self.name = basename(root)
99
stat = os.lstat(self.fullpath)
100
self.mode = stat.st_mode
105
return 'FileInfo(%r)' % self.name
108
return stat.S_ISREG(self.mode)
111
return stat.S_ISDIR(self.mode)
114
if stat.S_ISLNK(self.mode):
115
self.linkname = os.readlink(self.fullpath)
61
122
"""Return the top directory given in a path."""
62
dirname = os.path.dirname(path)
63
last_dirname = dirname
65
dirname = os.path.dirname(dirname)
66
if dirname == '' or dirname == last_dirname:
68
last_dirname = dirname
123
components = splitpath(path)
124
if len(components) > 0:
71
130
def common_directory(names):
72
131
"""Determine a single directory prefix from a list of names"""
73
132
possible_prefix = None
74
133
for name in names:
75
name_top = top_directory(name)
134
name_top = top_path(name)
76
137
if possible_prefix is None:
77
138
possible_prefix = name_top
111
176
import_archive(tree, tar_file)
113
178
def import_zip(tree, zip_input):
114
zip_file = ZipFileWrapper(zipfile.ZipFile(zip_input))
179
zip_file = ZipFileWrapper(zip_input, 'r')
115
180
import_archive(tree, zip_file)
182
def import_dir(tree, dir_input):
183
dir_file = DirWrapper(dir_input)
184
import_archive(tree, dir_file)
117
187
def import_archive(tree, archive_file):
188
tt = TreeTransform(tree)
190
import_archive_to_transform(tree, archive_file, tt)
196
def import_archive_to_transform(tree, archive_file, tt):
118
197
prefix = common_directory(names_of_files(archive_file))
119
tt = TreeTransform(tree)
122
for path, entry in tree.inventory.iter_entries():
199
for path, entry in tree.iter_entries_by_dir():
123
200
if entry.parent_id is None:
125
202
trans_id = tt.trans_id_tree_path(path)
126
203
tt.delete_contents(trans_id)
127
204
removed.add(path)
130
207
implied_parents = set()
132
209
for member in archive_file.getmembers():
133
210
if member.type == 'g':
134
211
# type 'g' is a header
136
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
216
relative_path = member.name.decode('utf-8')
137
217
if prefix is not None:
138
218
relative_path = relative_path[len(prefix)+1:]
219
relative_path = relative_path.rstrip('/')
139
220
if relative_path == '':
222
if should_ignore(relative_path):
141
224
add_implied_parents(implied_parents, relative_path)
142
225
trans_id = tt.trans_id_tree_path(relative_path)
143
226
added.add(relative_path.rstrip('/'))
144
227
path = tree.abspath(relative_path)
145
228
if member.name in seen:
229
if tt.final_kind(trans_id) == 'file':
230
tt.set_executability(None, trans_id)
146
231
tt.cancel_creation(trans_id)
147
232
seen.add(member.name)
148
233
if member.isreg():
149
tt.create_file(file_iterator(archive_file.extractfile(member)),
234
tt.create_file(file_iterator(archive_file.extractfile(member)),
236
executable = (member.mode & 0111) != 0
237
tt.set_executability(executable, trans_id)
151
238
elif member.isdir():
152
239
do_directory(tt, trans_id, tree, relative_path, path)
153
240
elif member.issym():
154
241
tt.create_symlink(member.linkname, trans_id)
244
if tt.tree_file_id(trans_id) is None:
245
name = basename(member.name.rstrip('/'))
246
file_id = generate_ids.gen_file_id(name)
247
tt.version_file(file_id, trans_id)
156
249
for relative_path in implied_parents.difference(added):
157
250
if relative_path == "":
194
280
if tree.changes_from(tree.basis_tree()).has_changed():
195
281
raise BzrCommandError("Working tree has uncommitted changes.")
197
if (source.endswith('.tar') or source.endswith('.tar.gz') or
198
source.endswith('.tar.bz2')) or source.endswith('.tgz'):
200
if source.endswith('.bz2'):
201
tar_input = BZ2File(source, 'r')
202
tar_input = StringIO(tar_input.read())
204
tar_input = file(source, 'rb')
206
if e.errno == errno.ENOENT:
207
raise NoSuchFile(source)
209
import_tar(tree, tar_input)
212
elif source.endswith('.zip'):
213
import_zip(tree, open(source, 'rb'))
284
archive, external_compressor = get_archive_type(source)
285
except errors.NotArchiveType:
286
if file_kind(source) == 'directory':
291
raise BzrCommandError('Unhandled import source')
215
raise BzrCommandError('Unhandled import source')
294
import_zip(tree, open_from_url(source))
295
elif archive == 'tar':
297
tar_input = open_from_url(source)
298
if external_compressor == 'bz2':
300
tar_input = StringIO(bz2.decompress(tar_input.read()))
301
elif external_compressor == 'lzma':
303
tar_input = StringIO(lzma.decompress(tar_input.read()))
305
if e.errno == errno.ENOENT:
306
raise NoSuchFile(source)
308
import_tar(tree, tar_input)
219
class TestImport(TestCaseInTempDir):
221
def make_tar(self, mode='w'):
223
return tarfile.open('project-0.1.tar', mode, fileobj)
224
return self.make_archive(maker)
226
def make_archive(self, maker):
228
archive_file = maker(result)
229
os.mkdir('project-0.1')
230
archive_file.add('project-0.1')
231
os.mkdir('project-0.1/junk')
232
archive_file.add('project-0.1/junk')
234
f = file('project-0.1/README', 'wb')
237
archive_file.add('project-0.1/README')
239
f = file('project-0.1/FEEDME', 'wb')
242
archive_file.add('project-0.1/FEEDME')
245
rmtree('project-0.1')
251
tar_file = tarfile.open('project-0.2.tar', 'w', result)
252
os.mkdir('project-0.2')
253
tar_file.add('project-0.2')
255
os.mkdir('project-0.2/junk')
256
tar_file.add('project-0.2/junk')
258
f = file('project-0.2/README', 'wb')
261
tar_file.add('project-0.2/README')
264
tar_file = tarfile.open('project-0.2.tar', 'a', result)
265
tar_file.add('project-0.2/README')
267
rmtree('project-0.2')
270
def make_messed_tar(self):
272
tar_file = tarfile.open('project-0.1.tar', 'w', result)
273
os.mkdir('project-0.1')
274
tar_file.add('project-0.1')
276
os.mkdir('project-0.2')
277
tar_file.add('project-0.2')
279
f = file('project-0.1/README', 'wb')
282
tar_file.add('project-0.1/README')
284
rmtree('project-0.1')
290
return ZipFileWrapper(zipfile.ZipFile(fileobj, 'w'))
291
return self.make_archive(maker)
293
def test_top_directory(self):
294
self.assertEqual(top_directory('ab/b/c'), 'ab')
295
self.assertEqual(top_directory('/etc'), '/')
297
def test_common_directory(self):
298
self.assertEqual(common_directory(['ab/c/d', 'ab/c/e']), 'ab')
299
self.assertIs(common_directory(['ab/c/d', 'ac/c/e']), None)
301
def test_untar(self):
302
tar_file = self.make_tar()
303
tree = BzrDir.create_standalone_workingtree('tree')
304
import_tar(tree, tar_file)
305
self.assertTrue(tree.path2id('README') is not None)
306
self.assertTrue(tree.path2id('FEEDME') is not None)
307
self.assertTrue(os.path.isfile(tree.abspath('README')))
308
self.assertEqual(tree.inventory[tree.path2id('README')].kind, 'file')
309
self.assertEqual(tree.inventory[tree.path2id('FEEDME')].kind, 'file')
311
f = file(tree.abspath('junk/food'), 'wb')
312
f.write('I like food\n')
315
tar_file = self.make_tar2()
316
import_tar(tree, tar_file)
317
self.assertTrue(tree.path2id('README') is not None)
318
self.assertTrue(not os.path.exists(tree.abspath('FEEDME')))
321
def test_untar2(self):
322
tar_file = self.make_messed_tar()
323
tree = BzrDir.create_standalone_workingtree('tree')
324
import_tar(tree, tar_file)
325
self.assertTrue(tree.path2id('project-0.1/README') is not None)
327
def test_untar_gzip(self):
328
tar_file = self.make_tar(mode='w:gz')
329
tree = BzrDir.create_standalone_workingtree('tree')
330
import_tar(tree, tar_file)
331
self.assertTrue(tree.path2id('README') is not None)
333
def test_unzip(self):
334
zip_file = self.make_zip()
335
tree = BzrDir.create_standalone_workingtree('tree')
336
import_zip(tree, zip_file)
337
self.assertTrue(tree.path2id('README') is not None)
338
self.assertTrue(tree.path2id('FEEDME') is not None)
339
self.assertTrue(os.path.isfile(tree.abspath('README')))
340
self.assertEqual(tree.inventory[tree.path2id('README')].kind, 'file')
341
self.assertEqual(tree.inventory[tree.path2id('FEEDME')].kind, 'file')
343
f = file(tree.abspath('junk/food'), 'wb')
344
f.write('I like food\n')
349
return makeSuite(TestImport)
315
def get_archive_type(path):
316
"""Return the type of archive and compressor indicated by path name.
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
322
matches = re.match(r'.*\.(zip|tgz|tar(.(gz|bz2|lzma|xz))?)$', path)
324
raise errors.NotArchiveType(path)
325
external_compressor = None
326
if matches.group(3) is not None:
328
external_compressor = matches.group(3)
329
if external_compressor == 'xz':
330
external_compressor = 'lzma'
331
elif matches.group(1) == 'tgz':
334
archive = matches.group(1)
335
return archive, external_compressor