Rev 5724: (jelmer) Fix some export bugs. (Jelmer Vernooij) in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Mar 15 00:38:30 UTC 2011
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 5724 [merge]
revision-id: pqm at pqm.ubuntu.com-20110315003823-636nub1z70i2sc7p
parent: pqm at pqm.ubuntu.com-20110314164900-8v7s3nzvigrhoydq
parent: jelmer at samba.org-20110314231848-1lckp5aeoeobuiq3
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2011-03-15 00:38:23 +0000
message:
(jelmer) Fix some export bugs. (Jelmer Vernooij)
modified:
bzrlib/export/__init__.py __init__.py-20051114235828-1ba62cb4062304e6
bzrlib/export/dir_exporter.py dir_exporter.py-20051114235828-b51397f56bc7b117
bzrlib/export/tar_exporter.py tar_exporter.py-20051114235828-1f6349a2f090a5d0
bzrlib/export/zip_exporter.py zip_exporter.py-20051114235828-8f57f954fba6497e
bzrlib/tests/__init__.py selftest.py-20050531073622-8d0e3c8845c97a64
bzrlib/tests/blackbox/test_export.py test_export.py-20051229024010-e6c26658e460fb1c
bzrlib/tests/features.py features.py-20090820042958-jglgza3wrn03ha9e-1
bzrlib/tests/test_export.py test_export.py-20090220201010-tpbxssdnezsvu9pk-1
doc/en/release-notes/bzr-2.4.txt bzr2.4.txt-20110114053217-k7ym9jfz243fddjm-1
=== modified file 'bzrlib/export/__init__.py'
--- a/bzrlib/export/__init__.py 2010-09-21 03:20:09 +0000
+++ b/bzrlib/export/__init__.py 2011-03-14 12:18:38 +0000
@@ -20,9 +20,11 @@
"""
import os
+import time
from bzrlib import (
errors,
pyutils,
+ trace,
)
# Maps format name => export function
@@ -57,10 +59,10 @@
When requesting a specific type of export, load the respective path.
"""
- def _loader(tree, dest, root, subdir, filtered, per_file_timestamps):
+ def _loader(tree, dest, root, subdir, filtered, force_mtime):
func = pyutils.get_named_object(module, funcname)
return func(tree, dest, root, subdir, filtered=filtered,
- per_file_timestamps=per_file_timestamps)
+ force_mtime=force_mtime)
register_exporter(scheme, extensions, _loader)
@@ -103,10 +105,18 @@
if format not in _exporters:
raise errors.NoSuchExportFormat(format)
+
+ if not per_file_timestamps:
+ force_mtime = time.time()
+ else:
+ force_mtime = None
+
+ trace.mutter('export version %r', tree)
+
tree.lock_read()
try:
return _exporters[format](tree, dest, root, subdir, filtered=filtered,
- per_file_timestamps=per_file_timestamps)
+ force_mtime=force_mtime)
finally:
tree.unlock()
@@ -114,26 +124,11 @@
def get_root_name(dest):
"""Get just the root name for an export.
- >>> get_root_name('../mytest.tar')
- 'mytest'
- >>> get_root_name('mytar.tar')
- 'mytar'
- >>> get_root_name('mytar.tar.bz2')
- 'mytar'
- >>> get_root_name('tar.tar.tar.tgz')
- 'tar.tar.tar'
- >>> get_root_name('bzr-0.0.5.tar.gz')
- 'bzr-0.0.5'
- >>> get_root_name('bzr-0.0.5.zip')
- 'bzr-0.0.5'
- >>> get_root_name('bzr-0.0.5')
- 'bzr-0.0.5'
- >>> get_root_name('a/long/path/mytar.tgz')
- 'mytar'
- >>> get_root_name('../parent/../dir/other.tbz2')
- 'other'
"""
global _exporter_extensions
+ if dest == '-':
+ # Exporting to -/foo doesn't make sense so use relative paths.
+ return ''
dest = os.path.basename(dest)
for ext in _exporter_extensions:
if dest.endswith(ext):
@@ -141,11 +136,12 @@
return dest
-def _export_iter_entries(tree, subdir):
+def _export_iter_entries(tree, subdir, skip_special=True):
"""Iter the entries for tree suitable for exporting.
:param tree: A tree object.
:param subdir: None or the path of an entry to start exporting from.
+ :param skip_special: Whether to skip .bzr files.
"""
inv = tree.inventory
if subdir is None:
@@ -167,7 +163,7 @@
for entry in entries:
# The .bzr* namespace is reserved for "magic" files like
# .bzrignore and .bzrrules - do not export these
- if entry[0].startswith(".bzr"):
+ if skip_special and entry[0].startswith(".bzr"):
continue
if subdir is None:
if not tree.has_filename(entry[0]):
@@ -180,8 +176,10 @@
register_lazy_exporter(None, [], 'bzrlib.export.dir_exporter', 'dir_exporter')
register_lazy_exporter('dir', [], 'bzrlib.export.dir_exporter', 'dir_exporter')
-register_lazy_exporter('tar', ['.tar'], 'bzrlib.export.tar_exporter', 'tar_exporter')
+register_lazy_exporter('tar', ['.tar'], 'bzrlib.export.tar_exporter', 'plain_tar_exporter')
register_lazy_exporter('tgz', ['.tar.gz', '.tgz'], 'bzrlib.export.tar_exporter', 'tgz_exporter')
register_lazy_exporter('tbz2', ['.tar.bz2', '.tbz2'], 'bzrlib.export.tar_exporter', 'tbz_exporter')
+register_lazy_exporter('tlzma', ['.tar.lzma'], 'bzrlib.export.tar_exporter', 'tar_lzma_exporter')
+register_lazy_exporter('txz', ['.tar.xz'], 'bzrlib.export.tar_exporter', 'tar_xz_exporter')
register_lazy_exporter('zip', ['.zip'], 'bzrlib.export.zip_exporter', 'zip_exporter')
=== modified file 'bzrlib/export/dir_exporter.py'
--- a/bzrlib/export/dir_exporter.py 2010-05-25 17:27:52 +0000
+++ b/bzrlib/export/dir_exporter.py 2011-03-13 21:30:33 +0000
@@ -18,7 +18,6 @@
import errno
import os
-import time
from bzrlib import errors, osutils
from bzrlib.export import _export_iter_entries
@@ -26,11 +25,9 @@
ContentFilterContext,
filtered_output_bytes,
)
-from bzrlib.trace import mutter
-
-
-def dir_exporter(tree, dest, root, subdir, filtered=False,
- per_file_timestamps=False):
+
+
+def dir_exporter(tree, dest, root, subdir=None, filtered=False, force_mtime=None):
"""Export this tree to a new directory.
`dest` should either not exist or should be empty. If it does not exist it
@@ -39,7 +36,6 @@
:note: If the export fails, the destination directory will be
left in an incompletely exported state: export is not transactional.
"""
- mutter('export version %r', tree)
try:
os.mkdir(dest)
except OSError, e:
@@ -76,7 +72,6 @@
# The data returned here can be in any order, but we've already created all
# the directories
flags = os.O_CREAT | os.O_TRUNC | os.O_WRONLY | getattr(os, 'O_BINARY', 0)
- now = time.time()
for (relpath, executable), chunks in tree.iter_files_bytes(to_fetch):
if filtered:
filters = tree._content_filter_stack(relpath)
@@ -92,8 +87,8 @@
out.writelines(chunks)
finally:
out.close()
- if per_file_timestamps:
+ if force_mtime is not None:
+ mtime = force_mtime
+ else:
mtime = tree.get_file_mtime(tree.path2id(relpath), relpath)
- else:
- mtime = now
os.utime(fullpath, (mtime, mtime))
=== modified file 'bzrlib/export/tar_exporter.py'
--- a/bzrlib/export/tar_exporter.py 2011-03-13 18:18:10 +0000
+++ b/bzrlib/export/tar_exporter.py 2011-03-14 14:11:06 +0000
@@ -17,14 +17,13 @@
"""Export a Tree to a non-versioned directory.
"""
+import os
import StringIO
import sys
import tarfile
-import time
from bzrlib import (
errors,
- export,
osutils,
)
from bzrlib.export import _export_iter_entries
@@ -32,41 +31,26 @@
ContentFilterContext,
filtered_output_bytes,
)
-from bzrlib.trace import mutter
-
-
-def tar_exporter(tree, dest, root, subdir, compression=None, filtered=False,
- per_file_timestamps=False):
- """Export this tree to a new tar file.
-
- `dest` will be created holding the contents of this tree; if it
- already exists, it will be clobbered, like with "tar -c".
+
+
+def export_tarball(tree, ball, root, subdir=None, filtered=False,
+ force_mtime=None):
+ """Export tree contents to a tarball.
+
+ :param tree: Tree to export
+ :param ball: Tarball to export to
+ :param filtered: Whether to apply filters
+ :param subdir: Sub directory to export
+ :param force_mtime: Option mtime to force, instead of using
+ tree timestamps.
"""
- mutter('export version %r', tree)
- now = time.time()
- compression = str(compression or '')
- if dest == '-':
- # XXX: If no root is given, the output tarball will contain files
- # named '-/foo'; perhaps this is the most reasonable thing.
- ball = tarfile.open(None, 'w|' + compression, sys.stdout)
- else:
- if root is None:
- root = export.get_root_name(dest)
-
- # tarfile.open goes on to do 'os.getcwd() + dest' for opening
- # the tar file. With dest being unicode, this throws UnicodeDecodeError
- # unless we encode dest before passing it on. This works around
- # upstream python bug http://bugs.python.org/issue8396
- # (fixed in Python 2.6.5 and 2.7b1)
- ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:' + compression)
-
for dp, ie in _export_iter_entries(tree, subdir):
filename = osutils.pathjoin(root, dp).encode('utf8')
item = tarfile.TarInfo(filename)
- if per_file_timestamps:
+ if force_mtime is not None:
+ item.mtime = force_mtime
+ else:
item.mtime = tree.get_file_mtime(ie.file_id, dp)
- else:
- item.mtime = now
if ie.kind == "file":
item.type = tarfile.REGTYPE
if tree.is_executable(ie.file_id):
@@ -82,7 +66,7 @@
item.size = len(content)
fileobj = StringIO.StringIO(content)
else:
- item.size = ie.text_size
+ item.size = tree.get_file_size(ie.file_id)
fileobj = tree.get_file(ie.file_id)
elif ie.kind == "directory":
item.type = tarfile.DIRTYPE
@@ -94,22 +78,113 @@
item.type = tarfile.SYMTYPE
item.size = 0
item.mode = 0755
- item.linkname = ie.symlink_target
+ item.linkname = tree.get_symlink_target(ie.file_id)
fileobj = None
else:
raise errors.BzrError("don't know how to export {%s} of kind %r" %
(ie.file_id, ie.kind))
ball.addfile(item, fileobj)
- ball.close()
-
-
-def tgz_exporter(tree, dest, root, subdir, filtered=False,
- per_file_timestamps=False):
- tar_exporter(tree, dest, root, subdir, compression='gz',
- filtered=filtered, per_file_timestamps=per_file_timestamps)
-
-
-def tbz_exporter(tree, dest, root, subdir, filtered=False,
- per_file_timestamps=False):
- tar_exporter(tree, dest, root, subdir, compression='bz2',
- filtered=filtered, per_file_timestamps=per_file_timestamps)
+
+
+def tgz_exporter(tree, dest, root, subdir, filtered=False, force_mtime=None):
+ """Export this tree to a new tar file.
+
+ `dest` will be created holding the contents of this tree; if it
+ already exists, it will be clobbered, like with "tar -c".
+ """
+ import gzip
+ if force_mtime is not None:
+ root_mtime = force_mtime
+ elif (getattr(tree, "repository", None) and
+ getattr(tree, "get_revision_id", None)):
+ # If this is a revision tree, use the revisions' timestamp
+ rev = tree.repository.get_revision(tree.get_revision_id())
+ root_mtime = rev.timestamp
+ elif tree.get_root_id() is not None:
+ root_mtime = tree.get_file_mtime(tree.get_root_id())
+ else:
+ root_mtime = None
+ if dest == '-':
+ basename = None
+ stream = sys.stdout
+ else:
+ stream = open(dest.encode(osutils._fs_enc), 'w')
+ # gzip file is used with an explicit fileobj so that
+ # the basename can be stored in the gzip file rather than
+ # dest. (bug 102234)
+ basename = os.path.basename(dest)
+ try:
+ stream = gzip.GzipFile(basename, 'w', fileobj=stream, mtime=root_mtime)
+ except TypeError:
+ # Python < 2.7 doesn't support the mtime argument
+ stream = gzip.GzipFile(basename, 'w', fileobj=stream)
+ ball = tarfile.open(None, 'w|', fileobj=stream)
+ export_tarball(tree, ball, root, subdir, filtered=filtered,
+ force_mtime=force_mtime)
+ ball.close()
+
+
+def tbz_exporter(tree, dest, root, subdir, filtered=False, force_mtime=None):
+ """Export this tree to a new tar file.
+
+ `dest` will be created holding the contents of this tree; if it
+ already exists, it will be clobbered, like with "tar -c".
+ """
+ if dest == '-':
+ ball = tarfile.open(None, 'w|bz2', sys.stdout)
+ else:
+ # tarfile.open goes on to do 'os.getcwd() + dest' for opening
+ # the tar file. With dest being unicode, this throws UnicodeDecodeError
+ # unless we encode dest before passing it on. This works around
+ # upstream python bug http://bugs.python.org/issue8396
+ # (fixed in Python 2.6.5 and 2.7b1)
+ ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:bz2')
+ export_tarball(tree, ball, root, subdir, filtered=filtered,
+ force_mtime=force_mtime)
+ ball.close()
+
+
+def plain_tar_exporter(tree, dest, root, subdir, compression=None,
+ filtered=False, force_mtime=None):
+ """Export this tree to a new tar file.
+
+ `dest` will be created holding the contents of this tree; if it
+ already exists, it will be clobbered, like with "tar -c".
+ """
+ if dest == '-':
+ stream = sys.stdout
+ else:
+ stream = open(dest.encode(osutils._fs_enc), 'w')
+ ball = tarfile.open(None, 'w|', stream)
+ export_tarball(tree, ball, root, subdir, filtered=filtered,
+ force_mtime=force_mtime)
+ ball.close()
+
+
+def tar_xz_exporter(tree, dest, root, subdir, filtered=False,
+ force_mtime=None):
+ return tar_lzma_exporter(tree, dest, root, subdir, filtered=filtered,
+ force_mtime=force_mtime, compression_format="xz")
+
+
+def tar_lzma_exporter(tree, dest, root, subdir, filtered=False, force_mtime=None, compression_format="alone"):
+ """Export this tree to a new .tar.lzma file.
+
+ `dest` will be created holding the contents of this tree; if it
+ already exists, it will be clobbered, like with "tar -c".
+ """
+ if dest == '-':
+ raise errors.BzrError("Writing to stdout not supported for .tar.lzma")
+
+ try:
+ import lzma
+ except ImportError, e:
+ raise errors.DependencyNotPresent('lzma', e)
+
+ stream = lzma.LZMAFile(dest.encode(osutils._fs_enc), 'w',
+ options={"format": compression_format})
+ ball = tarfile.open(None, 'w:', fileobj=stream)
+ export_tarball(tree, ball, root, subdir, filtered=filtered,
+ force_mtime=force_mtime)
+ ball.close()
+
=== modified file 'bzrlib/export/zip_exporter.py'
--- a/bzrlib/export/zip_exporter.py 2011-02-16 17:20:10 +0000
+++ b/bzrlib/export/zip_exporter.py 2011-03-13 21:30:33 +0000
@@ -19,6 +19,7 @@
import os
import stat
+import sys
import time
import zipfile
@@ -43,20 +44,17 @@
_DIR_ATTR = stat.S_IFDIR | ZIP_DIRECTORY_BIT | DIR_PERMISSIONS
-def zip_exporter(tree, dest, root, subdir, filtered=False,
- per_file_timestamps=False):
+def zip_exporter(tree, dest, root, subdir=None, filtered=False, force_mtime=None):
""" Export this tree to a new zip file.
`dest` will be created holding the contents of this tree; if it
already exists, it will be overwritten".
"""
- mutter('export version %r', tree)
-
- now = time.localtime()[:6]
compression = zipfile.ZIP_DEFLATED
+ if dest == "-":
+ dest = sys.stdout
zipf = zipfile.ZipFile(dest, "w", compression)
-
try:
for dp, ie in _export_iter_entries(tree, subdir):
file_id = ie.file_id
@@ -64,15 +62,16 @@
# zipfile.ZipFile switches all paths to forward
# slashes anyway, so just stick with that.
- if per_file_timestamps:
+ if force_mtime is not None:
+ mtime = force_mtime
+ else:
mtime = tree.get_file_mtime(ie.file_id, dp)
- else:
- mtime = now
+ date_time = time.localtime(mtime)[:6]
filename = osutils.pathjoin(root, dp).encode('utf8')
if ie.kind == "file":
zinfo = zipfile.ZipInfo(
filename=filename,
- date_time=mtime)
+ date_time=date_time)
zinfo.compress_type = compression
zinfo.external_attr = _FILE_ATTR
if filtered:
@@ -90,14 +89,14 @@
# not just empty files.
zinfo = zipfile.ZipInfo(
filename=filename + '/',
- date_time=mtime)
+ date_time=date_time)
zinfo.compress_type = compression
zinfo.external_attr = _DIR_ATTR
zipf.writestr(zinfo,'')
elif ie.kind == "symlink":
zinfo = zipfile.ZipInfo(
filename=(filename + '.lnk'),
- date_time=mtime)
+ date_time=date_time)
zinfo.compress_type = compression
zinfo.external_attr = _FILE_ATTR
zipf.writestr(zinfo, ie.symlink_target)
=== modified file 'bzrlib/tests/__init__.py'
--- a/bzrlib/tests/__init__.py 2011-03-10 13:29:54 +0000
+++ b/bzrlib/tests/__init__.py 2011-03-14 12:18:38 +0000
@@ -3903,7 +3903,6 @@
'bzrlib',
'bzrlib.branchbuilder',
'bzrlib.decorators',
- 'bzrlib.export',
'bzrlib.inventory',
'bzrlib.iterablefile',
'bzrlib.lockdir',
=== modified file 'bzrlib/tests/blackbox/test_export.py'
--- a/bzrlib/tests/blackbox/test_export.py 2011-02-16 17:20:10 +0000
+++ b/bzrlib/tests/blackbox/test_export.py 2011-03-14 23:18:48 +0000
@@ -23,6 +23,7 @@
import stat
import sys
import tarfile
+import time
import zipfile
@@ -117,6 +118,36 @@
# '.bzrignore'.
self.assertEqual(['test/a'], sorted(zfile.namelist()))
+ def test_zip_export_stdout(self):
+ tree = self.make_branch_and_tree('zip')
+ self.build_tree(['zip/a'])
+ tree.add('a')
+ tree.commit('1')
+ os.chdir('zip')
+ contents = self.run_bzr('export --format=zip -')[0]
+ zfile = zipfile.ZipFile(StringIO(contents))
+ self.assertEqual(['a'], sorted(zfile.namelist()))
+
+ def test_tgz_export_stdout(self):
+ tree = self.make_branch_and_tree('z')
+ self.build_tree(['z/a'])
+ tree.add('a')
+ tree.commit('1')
+ os.chdir('z')
+ contents = self.run_bzr('export --format=tgz -')[0]
+ ball = tarfile.open(mode='r|gz', fileobj=StringIO(contents))
+ self.assertEqual(['a'], ball.getnames())
+
+ def test_tbz2_export_stdout(self):
+ tree = self.make_branch_and_tree('z')
+ self.build_tree(['z/a'])
+ tree.add('a')
+ tree.commit('1')
+ os.chdir('z')
+ contents = self.run_bzr('export --format=tbz2 -')[0]
+ ball = tarfile.open(mode='r|bz2', fileobj=StringIO(contents))
+ self.assertEqual(['a'], ball.getnames())
+
def test_zip_export_unicode(self):
self.requireFeature(tests.UnicodeFilenameFeature)
tree = self.make_branch_and_tree('zip')
@@ -317,3 +348,15 @@
self.run_bzr(['export', '--directory=branch', 'latest'])
self.assertEqual(['goodbye', 'hello'], sorted(os.listdir('latest')))
self.check_file_contents('latest/goodbye', 'baz')
+
+ def test_zip_export_per_file_timestamps(self):
+ tree = self.example_branch()
+ self.build_tree_contents([('branch/har', 'foo')])
+ tree.add('har')
+ # Earliest allowable date on FAT32 filesystems is 1980-01-01
+ timestamp = 347151600
+ tree.commit('setup', timestamp=timestamp)
+ self.run_bzr('export --per-file-timestamps test.zip branch')
+ zfile = zipfile.ZipFile('test.zip')
+ info = zfile.getinfo("test/har")
+ self.assertEquals(time.localtime(timestamp)[:6], info.date_time)
=== modified file 'bzrlib/tests/features.py'
--- a/bzrlib/tests/features.py 2011-01-16 01:12:01 +0000
+++ b/bzrlib/tests/features.py 2011-03-14 12:43:19 +0000
@@ -42,6 +42,7 @@
not_running_as_root = _NotRunningAsRoot()
apport = tests.ModuleAvailableFeature('apport')
+lzma = tests.ModuleAvailableFeature('lzma')
meliae = tests.ModuleAvailableFeature('meliae')
paramiko = tests.ModuleAvailableFeature('paramiko')
pycurl = tests.ModuleAvailableFeature('pycurl')
=== modified file 'bzrlib/tests/test_export.py'
--- a/bzrlib/tests/test_export.py 2010-04-14 00:11:32 +0000
+++ b/bzrlib/tests/test_export.py 2011-03-14 23:18:48 +0000
@@ -14,19 +14,27 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+"""Tests for bzrlib.export."""
+
+from cStringIO import StringIO
import os
+import tarfile
import time
+import zipfile
from bzrlib import (
errors,
export,
tests,
)
-
-
-class TestExport(tests.TestCaseWithTransport):
-
- def test_dir_export_missing_file(self):
+from bzrlib.export import get_root_name
+from bzrlib.export.tar_exporter import export_tarball
+from bzrlib.tests import features
+
+
+class TestDirExport(tests.TestCaseWithTransport):
+
+ def test_missing_file(self):
self.build_tree(['a/', 'a/b', 'a/c'])
wt = self.make_branch_and_tree('.')
wt.add(['a', 'a/b', 'a/c'])
@@ -35,7 +43,12 @@
self.failUnlessExists('target/a/b')
self.failIfExists('target/a/c')
- def test_dir_export_symlink(self):
+ def test_empty(self):
+ wt = self.make_branch_and_tree('.')
+ export.export(wt, 'target', format="dir")
+ self.assertEquals([], os.listdir("target"))
+
+ def test_symlink(self):
self.requireFeature(tests.SymlinkFeature)
wt = self.make_branch_and_tree('.')
os.symlink('source', 'link')
@@ -43,7 +56,7 @@
export.export(wt, 'target', format="dir")
self.failUnlessExists('target/link')
- def test_dir_export_to_existing_empty_dir_success(self):
+ def test_to_existing_empty_dir_success(self):
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
wt = self.make_branch_and_tree('source')
wt.add(['a', 'b', 'b/c'])
@@ -54,7 +67,7 @@
self.failUnlessExists('target/b')
self.failUnlessExists('target/b/c')
- def test_dir_export_to_existing_nonempty_dir_fail(self):
+ def test_to_existing_nonempty_dir_fail(self):
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
wt = self.make_branch_and_tree('source')
wt.add(['a', 'b', 'b/c'])
@@ -62,7 +75,7 @@
self.build_tree(['target/', 'target/foo'])
self.assertRaises(errors.BzrError, export.export, wt, 'target', format="dir")
- def test_dir_export_existing_single_file(self):
+ def test_existing_single_file(self):
self.build_tree(['dir1/', 'dir1/dir2/', 'dir1/first', 'dir1/dir2/second'])
wtree = self.make_branch_and_tree('dir1')
wtree.add(['dir2', 'first', 'dir2/second'])
@@ -71,8 +84,8 @@
self.failUnlessExists('target1/first')
export.export(wtree, 'target2', format='dir', subdir='dir2/second')
self.failUnlessExists('target2/second')
-
- def test_dir_export_files_same_timestamp(self):
+
+ def test_files_same_timestamp(self):
builder = self.make_branch_builder('source')
builder.start_series()
builder.build_snapshot(None, None, [
@@ -99,7 +112,7 @@
# All files must be given the same mtime.
self.assertEqual(st_a.st_mtime, st_b.st_mtime)
- def test_dir_export_files_per_file_timestamps(self):
+ def test_files_per_file_timestamps(self):
builder = self.make_branch_builder('source')
builder.start_series()
# Earliest allowable date on FAT32 filesystems is 1980-01-01
@@ -121,3 +134,124 @@
t = self.get_transport('target')
self.assertEqual(a_time, t.stat('a').st_mtime)
self.assertEqual(b_time, t.stat('b').st_mtime)
+
+
+class TarExporterTests(tests.TestCaseWithTransport):
+
+ def test_xz(self):
+ self.requireFeature(features.lzma)
+ import lzma
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1")
+ export.export(wt, 'target.tar.xz', format="txz")
+ tf = tarfile.open(fileobj=lzma.LZMAFile('target.tar.xz'))
+ self.assertEquals(["target/a"], tf.getnames())
+
+ def test_lzma(self):
+ self.requireFeature(features.lzma)
+ import lzma
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1")
+ export.export(wt, 'target.tar.lzma', format="tlzma")
+ tf = tarfile.open(fileobj=lzma.LZMAFile('target.tar.lzma'))
+ self.assertEquals(["target/a"], tf.getnames())
+
+ def test_tgz(self):
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1")
+ export.export(wt, 'target.tar.gz', format="tgz")
+ tf = tarfile.open('target.tar.gz')
+ self.assertEquals(["target/a"], tf.getnames())
+
+ def test_tgz_ignores_dest_path(self):
+ # The target path should not be a part of the target file.
+ # (bug #102234)
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1")
+ os.mkdir("testdir1")
+ os.mkdir("testdir2")
+ export.export(wt, 'testdir1/target.tar.gz', format="tgz",
+ per_file_timestamps=True)
+ export.export(wt, 'testdir2/target.tar.gz', format="tgz",
+ per_file_timestamps=True)
+ file1 = open('testdir1/target.tar.gz', 'r')
+ self.addCleanup(file1.close)
+ file2 = open('testdir1/target.tar.gz', 'r')
+ self.addCleanup(file2.close)
+ content1 = file1.read()
+ content2 = file2.read()
+ self.assertEqualDiff(content1, content2)
+ # the gzip module doesn't have a way to read back to the original
+ # filename, but it's stored as-is in the tarfile.
+ self.assertFalse("testdir1" in content1)
+ self.assertFalse("target.tar.gz" in content1)
+ self.assertTrue("target.tar" in content1)
+
+ def test_tbz2(self):
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1")
+ export.export(wt, 'target.tar.bz2', format="tbz2")
+ tf = tarfile.open('target.tar.bz2')
+ self.assertEquals(["target/a"], tf.getnames())
+
+ def test_xz_stdout(self):
+ wt = self.make_branch_and_tree('.')
+ self.assertRaises(errors.BzrError, export.export, wt, '-',
+ format="txz")
+
+ def test_export_tarball(self):
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ wt.add(["a"])
+ wt.commit("1", timestamp=42)
+ target = StringIO()
+ ball = tarfile.open(None, "w|", target)
+ wt.lock_read()
+ try:
+ export_tarball(wt, ball, "bar")
+ finally:
+ wt.unlock()
+ self.assertEquals(["bar/a"], ball.getnames())
+ ball.close()
+
+
+class ZipExporterTests(tests.TestCaseWithTransport):
+
+ def test_per_file_timestamps(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree_contents([('har', 'foo')])
+ tree.add('har')
+ # Earliest allowable date on FAT32 filesystems is 1980-01-01
+ timestamp = 347151600
+ tree.commit('setup', timestamp=timestamp)
+ export.export(tree.basis_tree(), 'test.zip', format='zip',
+ per_file_timestamps=True)
+ zfile = zipfile.ZipFile('test.zip')
+ info = zfile.getinfo("test/har")
+ self.assertEquals(time.localtime(timestamp)[:6], info.date_time)
+
+
+class RootNameTests(tests.TestCase):
+
+ def test_root_name(self):
+ self.assertEquals('mytest', get_root_name('../mytest.tar'))
+ self.assertEquals('mytar', get_root_name('mytar.tar'))
+ self.assertEquals('mytar', get_root_name('mytar.tar.bz2'))
+ self.assertEquals('tar.tar.tar', get_root_name('tar.tar.tar.tgz'))
+ self.assertEquals('bzr-0.0.5', get_root_name('bzr-0.0.5.tar.gz'))
+ self.assertEquals('bzr-0.0.5', get_root_name('bzr-0.0.5.zip'))
+ self.assertEquals('bzr-0.0.5', get_root_name('bzr-0.0.5'))
+ self.assertEquals('mytar', get_root_name('a/long/path/mytar.tgz'))
+ self.assertEquals('other',
+ get_root_name('../parent/../dir/other.tbz2'))
+ self.assertEquals('', get_root_name('-'))
=== modified file 'doc/en/release-notes/bzr-2.4.txt'
--- a/doc/en/release-notes/bzr-2.4.txt 2011-03-11 15:36:12 +0000
+++ b/doc/en/release-notes/bzr-2.4.txt 2011-03-14 14:21:29 +0000
@@ -50,8 +50,22 @@
* Branching, merging and pulling a branch now copies revisions named in
tags, not just the tag metadata. (Andrew Bennetts, #309682)
-
-* ``bzr cat-revision`` no longer requires a working tree. (Jelmer Vernooij, #704405)
+
+* ``bzr cat-revision`` no longer requires a working tree.
+ (Jelmer Vernooij, #704405)
+
+* ``bzr export --per-file-timestamps`` for .tar.gz files will now
+ override the mtime for trees exported on Python 2.7 and later, which
+ expose the 'mtime' field in gzip files. This makes the output of
+ ``bzr export --per-file-timestamps`` for a particular tree
+ deterministic. (Jelmer Vernooij, #711226)
+
+* ``bzr export --format=zip`` can now export to standard output,
+ like the other exporters can. (Jelmer Vernooij, #513752)
+
+* ``bzr export`` can now create ``.tar.xz`` and ``.tar.lzma`` files.
+ (Jelmer Vernooij, #551714)
+
Bug Fixes
*********
@@ -69,6 +83,9 @@
* ``bzr export`` to zip files will now set a mode on directories.
(Jelmer Vernooij, #207253)
+* ``bzr export`` to tgz files will only write out the basename of the
+ tarfile to the gzip file. (Jelmer Vernooij, #102234)
+
* ``bzr push --overwrite`` with an older revision specified will now correctly
roll back the target branch. (Jelmer Vernooij, #386576)
More information about the bazaar-commits
mailing list