Added support for stripping leading path segments

closes #95
This commit is contained in:
Jonas Borgström 2014-08-02 22:15:21 +02:00
parent 8f4e0f7506
commit 193fb1fcd5
4 changed files with 36 additions and 3 deletions

View file

@ -7,6 +7,8 @@ Version 0.14
------------
(feature release, released on X)
- Added support for stripping leading path segments (#95)
"attic extract --strip-segments X"
- Add workaround for old Linux systems without acl_extended_file_no_follow (#96)
- Add MacPorts' path to the default openssl search path (#101)
- HashIndex improvements, eliminates unnecessary IO on low memory systems.

View file

@ -191,14 +191,21 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
archive = Archive(repository, key, manifest, args.archive.archive,
numeric_owner=args.numeric_owner)
patterns = adjust_patterns(args.paths, args.excludes)
dry_run = args.dry_run
strip_components = args.strip_components
dirs = []
for item in archive.iter_items(lambda item: not exclude_path(item[b'path'], patterns), preload=True):
orig_path = item[b'path']
if strip_components:
item[b'path'] = os.sep.join(orig_path.split(os.sep)[strip_components:])
if not item[b'path']:
continue
if not args.dry_run:
while dirs and not item[b'path'].startswith(dirs[-1][b'path']):
archive.extract_item(dirs.pop(-1))
self.print_verbose(remove_surrogates(item[b'path']))
self.print_verbose(remove_surrogates(orig_path))
try:
if args.dry_run:
if dry_run:
archive.extract_item(item, dry_run=True)
else:
if stat.S_ISDIR(item[b'mode']):
@ -207,7 +214,7 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
else:
archive.extract_item(item)
except IOError as e:
self.print_error('%s: %s', remove_surrogates(item[b'path']), e)
self.print_error('%s: %s', remove_surrogates(orig_path), e)
if not args.dry_run:
while dirs:
@ -572,6 +579,9 @@ Type "Yes I am sure" if you understand this and want to continue.\n""")
subparser.add_argument('--numeric-owner', dest='numeric_owner',
action='store_true', default=False,
help='only obey numeric user and group identifiers')
subparser.add_argument('--strip-components', dest='strip_components',
type=int, default=0, metavar='NUMBER',
help='Remove the specified number of leading path elements. Pathnames with fewer elements will be silently skipped.')
subparser.add_argument('archive', metavar='ARCHIVE',
type=location_validator(archive=True),
help='archive to extract')

View file

@ -1,3 +1,4 @@
from contextlib import contextmanager
import filecmp
import os
import posix
@ -41,6 +42,12 @@ class AtticTestCase(unittest.TestCase):
assert_raises = unittest.TestCase.assertRaises
assert_true = unittest.TestCase.assertTrue
@contextmanager
def assert_creates_file(self, path):
self.assert_true(not os.path.exists(path), '{} should not exist'.format(path))
yield
self.assert_true(os.path.exists(path), '{} should exist'.format(path))
def assert_dirs_equal(self, dir1, dir2):
diff = filecmp.dircmp(dir1, dir2)
self._assert_dirs_equal_cmp(diff)

View file

@ -165,6 +165,20 @@ class ArchiverTestCase(ArchiverTestCaseBase):
# end the same way as info_output
assert info_output2.endswith(info_output)
def test_strip_components(self):
self.attic('init', self.repository_location)
self.create_regular_file('dir/file')
self.attic('create', self.repository_location + '::test', 'input')
with changedir('output'):
self.attic('extract', self.repository_location + '::test', '--strip-components', '3')
self.assert_true(not os.path.exists('file'))
with self.assert_creates_file('file'):
self.attic('extract', self.repository_location + '::test', '--strip-components', '2')
with self.assert_creates_file('dir/file'):
self.attic('extract', self.repository_location + '::test', '--strip-components', '1')
with self.assert_creates_file('input/dir/file'):
self.attic('extract', self.repository_location + '::test', '--strip-components', '0')
def test_extract_include_exclude(self):
self.attic('init', self.repository_location)
self.create_regular_file('file1', size=1024 * 80)