|
@@ -20,9 +20,9 @@ import collections
|
|
|
|
|
|
from . import __version__
|
|
|
from .helpers import Error, location_validator, archivename_validator, format_line, format_time, format_file_size, \
|
|
|
- parse_pattern, parse_exclude_pattern, ArgparsePatternAction, ArgparsePatternFileAction, ArgparseExcludeFileAction, \
|
|
|
- PathPrefixPattern, to_localtime, timestamp, safe_timestamp, bin_to_hex, get_cache_dir, prune_within, prune_split, \
|
|
|
- Manifest, NoManifestError, remove_surrogates, format_archive, check_extension_modules, Statistics, \
|
|
|
+ parse_pattern, PathPrefixPattern, to_localtime, timestamp, safe_timestamp, bin_to_hex, \
|
|
|
+ get_cache_dir, prune_within, prune_split, \
|
|
|
+ Manifest, NoManifestError, remove_surrogates, update_excludes, format_archive, check_extension_modules, Statistics, \
|
|
|
dir_is_tagged, bigint_to_int, ChunkerParams, CompressionSpec, PrefixSpec, is_slow_msgpack, yes, sysinfo, \
|
|
|
EXIT_SUCCESS, EXIT_WARNING, EXIT_ERROR, log_multi, PatternMatcher, ErrorIgnoringTextIOWrapper, set_ec, \
|
|
|
replace_placeholders
|
|
@@ -124,18 +124,6 @@ class Archiver:
|
|
|
if self.output_list and (self.output_filter is None or status in self.output_filter):
|
|
|
logger.info("%1s %s", status, remove_surrogates(path))
|
|
|
|
|
|
- @staticmethod
|
|
|
- def build_matcher(inclexcl_patterns, paths):
|
|
|
- matcher = PatternMatcher()
|
|
|
- if inclexcl_patterns:
|
|
|
- matcher.add_inclexcl(inclexcl_patterns)
|
|
|
- include_patterns = []
|
|
|
- if paths:
|
|
|
- include_patterns.extend(parse_pattern(i, PathPrefixPattern) for i in paths)
|
|
|
- matcher.add(include_patterns, True)
|
|
|
- matcher.fallback = not include_patterns
|
|
|
- return matcher, include_patterns
|
|
|
-
|
|
|
def do_serve(self, args):
|
|
|
"""Start in server mode. This command is usually not used manually.
|
|
|
"""
|
|
@@ -257,7 +245,8 @@ class Archiver:
|
|
|
def do_create(self, args, repository, manifest=None, key=None):
|
|
|
"""Create new archive"""
|
|
|
matcher = PatternMatcher(fallback=True)
|
|
|
- matcher.add_inclexcl(args.patterns)
|
|
|
+ if args.excludes:
|
|
|
+ matcher.add(args.excludes, False)
|
|
|
|
|
|
def create_inner(archive, cache):
|
|
|
# Add cache dir to inode_skip list
|
|
@@ -445,7 +434,17 @@ class Archiver:
|
|
|
if sys.platform.startswith(('linux', 'freebsd', 'netbsd', 'openbsd', 'darwin', )):
|
|
|
logger.warning('Hint: You likely need to fix your locale setup. E.g. install locales and use: LANG=en_US.UTF-8')
|
|
|
|
|
|
- matcher, include_patterns = self.build_matcher(args.patterns, args.paths)
|
|
|
+ matcher = PatternMatcher()
|
|
|
+ if args.excludes:
|
|
|
+ matcher.add(args.excludes, False)
|
|
|
+
|
|
|
+ include_patterns = []
|
|
|
+
|
|
|
+ if args.paths:
|
|
|
+ include_patterns.extend(parse_pattern(i, PathPrefixPattern) for i in args.paths)
|
|
|
+ matcher.add(include_patterns, True)
|
|
|
+
|
|
|
+ matcher.fallback = not include_patterns
|
|
|
|
|
|
output_list = args.output_list
|
|
|
dry_run = args.dry_run
|
|
@@ -923,9 +922,8 @@ class Archiver:
|
|
|
|
|
|
helptext = collections.OrderedDict()
|
|
|
helptext['patterns'] = textwrap.dedent('''
|
|
|
- File patterns support four separate styles: fnmatch, shell, regular
|
|
|
- expressions and path prefixes. By default, fnmatch is used for
|
|
|
- `--exclude` patterns and shell-style is used for `--pattern`. If followed
|
|
|
+ Exclusion patterns support four separate styles, fnmatch, shell, regular
|
|
|
+ expressions and path prefixes. By default, fnmatch is used. If followed
|
|
|
by a colon (':') the first two characters of a pattern are used as a
|
|
|
style selector. Explicit style selection is necessary when a
|
|
|
non-default style is desired or when the desired pattern starts with
|
|
@@ -933,12 +931,12 @@ class Archiver:
|
|
|
|
|
|
`Fnmatch <https://docs.python.org/3/library/fnmatch.html>`_, selector `fm:`
|
|
|
|
|
|
- This is the default style for --exclude and --exclude-from.
|
|
|
- These patterns use a variant of shell pattern syntax, with '*' matching
|
|
|
- any number of characters, '?' matching any single character, '[...]'
|
|
|
- matching any single character specified, including ranges, and '[!...]'
|
|
|
- matching any character not specified. For the purpose of these patterns,
|
|
|
- the path separator ('\\' for Windows and '/' on other systems) is not
|
|
|
+ This is the default style. These patterns use a variant of shell
|
|
|
+ pattern syntax, with '*' matching any number of characters, '?'
|
|
|
+ matching any single character, '[...]' matching any single
|
|
|
+ character specified, including ranges, and '[!...]' matching any
|
|
|
+ character not specified. For the purpose of these patterns, the
|
|
|
+ path separator ('\\' for Windows and '/' on other systems) is not
|
|
|
treated specially. Wrap meta-characters in brackets for a literal
|
|
|
match (i.e. `[?]` to match the literal character `?`). For a path
|
|
|
to match a pattern, it must completely match from start to end, or
|
|
@@ -949,7 +947,6 @@ class Archiver:
|
|
|
|
|
|
Shell-style patterns, selector `sh:`
|
|
|
|
|
|
- This is the default style for --pattern and --patterns-from.
|
|
|
Like fnmatch patterns these are similar to shell patterns. The difference
|
|
|
is that the pattern may include `**/` for matching zero or more directory
|
|
|
levels, `*` for matching zero or more arbitrary characters with the
|
|
@@ -1010,41 +1007,7 @@ class Archiver:
|
|
|
re:^/home/[^/]\.tmp/
|
|
|
sh:/home/*/.thumbnails
|
|
|
EOF
|
|
|
- $ borg create --exclude-from exclude.txt backup /
|
|
|
-
|
|
|
-
|
|
|
- A more general and easier to use way to define filename matching patterns exists
|
|
|
- with the `--pattern` and `--patterns-from` options. Using these, you may specify
|
|
|
- the backup roots (starting points) and patterns for inclusion/exclusion. A
|
|
|
- root path starts with the prefix `R`, followed by a path (a plain path, not a
|
|
|
- file pattern). An include rule starts with the prefix +, an exclude rule starts
|
|
|
- with the prefix -, both followed by a pattern.
|
|
|
- Inclusion patterns are useful to include pathes that are contained in an excluded
|
|
|
- path. The first matching pattern is used so if an include pattern matches before
|
|
|
- an exclude pattern, the file is backed up.
|
|
|
-
|
|
|
- Note that the default pattern style for `--pattern` and `--patterns-from` is
|
|
|
- shell style (`sh:`), so those patterns behave similar to rsync include/exclude
|
|
|
- patterns. The pattern style can be set via the `P` prefix.
|
|
|
-
|
|
|
- Patterns (`--pattern`) and excludes (`--exclude`) from the command line are
|
|
|
- considered first (in the order of appearance). Then patterns from `--patterns-from`
|
|
|
- are added. Exclusion patterns from `--exclude-from` files are appended last.
|
|
|
-
|
|
|
- An example `--patterns-from` file could look like that::
|
|
|
-
|
|
|
- # "sh:" pattern style is the default, so the following line is not needed:
|
|
|
- P sh
|
|
|
- R /
|
|
|
- # can be rebuild
|
|
|
- - /home/*/.cache
|
|
|
- # they're downloads for a reason
|
|
|
- - /home/*/Downloads
|
|
|
- # susan is a nice person
|
|
|
- # include susans home
|
|
|
- + /home/susan
|
|
|
- # don't backup the other home directories
|
|
|
- - /home/*\n\n''')
|
|
|
+ $ borg create --exclude-from exclude.txt backup /\n\n''')
|
|
|
helptext['placeholders'] = textwrap.dedent('''
|
|
|
Repository (or Archive) URLs, --prefix and --remote-path values support these
|
|
|
placeholders:
|
|
@@ -1161,9 +1124,6 @@ class Archiver:
|
|
|
help='show version number and exit')
|
|
|
subparsers = parser.add_subparsers(title='required arguments', metavar='<command>')
|
|
|
|
|
|
- # some empty defaults for all subparsers
|
|
|
- common_parser.set_defaults(paths=[], patterns=[])
|
|
|
-
|
|
|
serve_epilog = textwrap.dedent("""
|
|
|
This command starts a repository server process. This command is usually not used manually.
|
|
|
""")
|
|
@@ -1422,10 +1382,11 @@ class Archiver:
|
|
|
help='output verbose list of items (files, dirs, ...)')
|
|
|
subparser.add_argument('--filter', dest='output_filter', metavar='STATUSCHARS',
|
|
|
help='only display items with the given status characters')
|
|
|
- subparser.add_argument('-e', '--exclude', dest='patterns',
|
|
|
- type=parse_exclude_pattern, action='append',
|
|
|
+ subparser.add_argument('-e', '--exclude', dest='excludes',
|
|
|
+ type=parse_pattern, action='append',
|
|
|
metavar="PATTERN", help='exclude paths matching PATTERN')
|
|
|
- subparser.add_argument('--exclude-from', action=ArgparseExcludeFileAction,
|
|
|
+ subparser.add_argument('--exclude-from', dest='exclude_files',
|
|
|
+ type=argparse.FileType('r'), action='append',
|
|
|
metavar='EXCLUDEFILE', help='read exclude patterns from EXCLUDEFILE, one per line')
|
|
|
subparser.add_argument('--exclude-caches', dest='exclude_caches',
|
|
|
action='store_true', default=False,
|
|
@@ -1436,10 +1397,6 @@ class Archiver:
|
|
|
subparser.add_argument('--keep-tag-files', dest='keep_tag_files',
|
|
|
action='store_true', default=False,
|
|
|
help='keep tag files of excluded caches/directories')
|
|
|
- subparser.add_argument('--pattern', action=ArgparsePatternAction,
|
|
|
- metavar="PATTERN", help='include/exclude paths matching PATTERN')
|
|
|
- subparser.add_argument('--patterns-from', action=ArgparsePatternFileAction,
|
|
|
- metavar='PATTERNFILE', help='read include/exclude patterns from PATTERNFILE, one per line')
|
|
|
subparser.add_argument('-c', '--checkpoint-interval', dest='checkpoint_interval',
|
|
|
type=int, default=300, metavar='SECONDS',
|
|
|
help='write checkpoint every SECONDS seconds (Default: 300)')
|
|
@@ -1486,7 +1443,7 @@ class Archiver:
|
|
|
subparser.add_argument('location', metavar='ARCHIVE',
|
|
|
type=location_validator(archive=True),
|
|
|
help='name of archive to create (must be also a valid directory name)')
|
|
|
- subparser.add_argument('paths', metavar='PATH', nargs='*', type=str,
|
|
|
+ subparser.add_argument('paths', metavar='PATH', nargs='+', type=str,
|
|
|
help='paths to archive')
|
|
|
|
|
|
extract_epilog = textwrap.dedent("""
|
|
@@ -1509,15 +1466,12 @@ class Archiver:
|
|
|
subparser.add_argument('-n', '--dry-run', dest='dry_run',
|
|
|
default=False, action='store_true',
|
|
|
help='do not actually change any files')
|
|
|
- subparser.add_argument('-e', '--exclude', dest='patterns',
|
|
|
- type=parse_exclude_pattern, action='append',
|
|
|
+ subparser.add_argument('-e', '--exclude', dest='excludes',
|
|
|
+ type=parse_pattern, action='append',
|
|
|
metavar="PATTERN", help='exclude paths matching PATTERN')
|
|
|
- subparser.add_argument('--exclude-from', action=ArgparseExcludeFileAction,
|
|
|
+ subparser.add_argument('--exclude-from', dest='exclude_files',
|
|
|
+ type=argparse.FileType('r'), action='append',
|
|
|
metavar='EXCLUDEFILE', help='read exclude patterns from EXCLUDEFILE, one per line')
|
|
|
- subparser.add_argument('--pattern', action=ArgparsePatternAction,
|
|
|
- metavar="PATTERN", help='include/exclude paths matching PATTERN')
|
|
|
- subparser.add_argument('--patterns-from', action=ArgparsePatternFileAction,
|
|
|
- metavar='PATTERNFILE', help='read include/exclude patterns from PATTERNFILE, one per line')
|
|
|
subparser.add_argument('--numeric-owner', dest='numeric_owner',
|
|
|
action='store_true', default=False,
|
|
|
help='only obey numeric user and group identifiers')
|
|
@@ -2101,11 +2055,7 @@ class Archiver:
|
|
|
args = self.preprocess_args(args)
|
|
|
parser = self.build_parser(args)
|
|
|
args = parser.parse_args(args or ['-h'])
|
|
|
- # This works around http://bugs.python.org/issue9351
|
|
|
- func = getattr(args, 'func', None) or getattr(args, 'fallback_func')
|
|
|
- if func == self.do_create and not args.paths:
|
|
|
- # need at least 1 path but args.paths may also be populated from patterns
|
|
|
- parser.error('Need at least one PATH argument.')
|
|
|
+ update_excludes(args)
|
|
|
return args
|
|
|
|
|
|
def run(self, args):
|