diff --git a/attic/archiver.py b/attic/archiver.py index 47650c2d..b2be0035 100644 --- a/attic/archiver.py +++ b/attic/archiver.py @@ -15,8 +15,8 @@ from attic.cache import Cache from attic.key import key_creator from attic.helpers import Error, location_validator, format_time, \ - format_file_mode, ExcludePattern, exclude_path, adjust_patterns, to_localtime, \ - get_cache_dir, get_keys_dir, format_timedelta, prune_within, prune_split, \ + format_file_mode, ExcludePattern, exclude_path, adjust_include_patterns, adjust_exclude_patterns, \ + to_localtime, get_cache_dir, get_keys_dir, format_timedelta, prune_within, prune_split, \ Manifest, remove_surrogates, update_excludes, format_archive, check_extension_modules, Statistics, \ is_cachedir, bigint_to_int from attic.remote import RepositoryServer, RemoteRepository @@ -125,7 +125,8 @@ def do_create(self, args): continue else: restrict_dev = None - self._process(archive, cache, args.excludes, args.exclude_caches, skip_inodes, path, restrict_dev) + excludes = adjust_exclude_patterns(path, args.excludes) + self._process(archive, cache, excludes, args.exclude_caches, skip_inodes, path, restrict_dev) archive.save() if args.stats: t = datetime.now() @@ -192,7 +193,7 @@ def do_extract(self, args): manifest, key = Manifest.load(repository) archive = Archive(repository, key, manifest, args.archive.archive, numeric_owner=args.numeric_owner) - patterns = adjust_patterns(args.paths, args.excludes) + patterns = adjust_include_patterns(args.paths, args.excludes) dry_run = args.dry_run strip_components = args.strip_components dirs = [] diff --git a/attic/helpers.py b/attic/helpers.py index ac526698..2cf46542 100644 --- a/attic/helpers.py +++ b/attic/helpers.py @@ -198,13 +198,20 @@ def update_excludes(args): file.close() -def adjust_patterns(paths, excludes): +def adjust_include_patterns(paths, excludes): if paths: return (excludes or []) + [IncludePattern(path) for path in paths] + [ExcludePattern('*')] else: return excludes +def adjust_exclude_patterns(path, excludes): + if excludes: + return [pattern for pattern in excludes if not pattern.match(path)] + else: + return [] + + def exclude_path(path, patterns): """Used by create and extract sub-commands to determine whether or not an item should be processed. @@ -566,4 +573,3 @@ def int_to_bigint(value): if value.bit_length() > 63: return value.to_bytes((value.bit_length() + 9) // 8, 'little', signed=True) return value - diff --git a/attic/testsuite/archiver.py b/attic/testsuite/archiver.py index 382fcc85..db62c1af 100644 --- a/attic/testsuite/archiver.py +++ b/attic/testsuite/archiver.py @@ -197,6 +197,17 @@ def test_extract_include_exclude(self): self.attic('extract', '--exclude-from=' + self.exclude_file_path, self.repository_location + '::test') self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'file3']) + def test_subtree_include_exclude(self): + self.attic('init', self.repository_location) + self.create_regular_file('file1', size=1024 * 80) + self.create_regular_file('unwanted/file2', size=1024 * 80) + self.create_regular_file('unwanted/wanted/file3', size=1024 * 80) + self.attic('create', '--exclude=input/unwanted', self.repository_location + '::test', 'input', 'input/unwanted/wanted') + with changedir('output'): + self.attic('extract', self.repository_location + '::test', 'input') + self.assert_equal(sorted(os.listdir('output/input')), ['file1', 'unwanted']) + self.assert_equal(sorted(os.listdir('output/input/unwanted')), ['wanted']) + def test_exclude_caches(self): self.attic('init', self.repository_location) self.create_regular_file('file1', size=1024 * 80) diff --git a/attic/testsuite/helpers.py b/attic/testsuite/helpers.py index e01b652c..b4fb304a 100644 --- a/attic/testsuite/helpers.py +++ b/attic/testsuite/helpers.py @@ -4,7 +4,7 @@ import os import tempfile import unittest -from attic.helpers import adjust_patterns, exclude_path, Location, format_timedelta, IncludePattern, ExcludePattern, make_path_safe, UpgradableLock, prune_within, prune_split, to_localtime, \ +from attic.helpers import adjust_include_patterns, exclude_path, Location, format_timedelta, IncludePattern, ExcludePattern, make_path_safe, UpgradableLock, prune_within, prune_split, to_localtime, \ StableDict, int_to_bigint, bigint_to_int from attic.testsuite import AtticTestCase import msgpack @@ -73,7 +73,7 @@ class PatternTestCase(AtticTestCase): ] def evaluate(self, paths, excludes): - patterns = adjust_patterns(paths, [ExcludePattern(p) for p in excludes]) + patterns = adjust_include_patterns(paths, [ExcludePattern(p) for p in excludes]) return [path for path in self.files if not exclude_path(path, patterns)] def test(self):