1
0
Fork 0
mirror of https://github.com/borgbackup/borg.git synced 2025-03-15 00:21:56 +00:00

Merge pull request #33 from jdchristensen/exclude

fix include/exclude bugs and add more tests
This commit is contained in:
Jonas Borgström 2014-02-08 13:04:17 +01:00
commit c22bc30a06
2 changed files with 37 additions and 17 deletions

View file

@ -9,7 +9,7 @@ import stat
import sys import sys
import time import time
from datetime import datetime, timezone from datetime import datetime, timezone
from fnmatch import fnmatchcase from fnmatch import translate
from operator import attrgetter from operator import attrgetter
import fcntl import fcntl
@ -150,7 +150,7 @@ def adjust_patterns(paths, excludes):
def exclude_path(path, patterns): def exclude_path(path, patterns):
"""Used by create and extract sub-commands to determine """Used by create and extract sub-commands to determine
if an item should be processed or not whether or not an item should be processed.
""" """
for pattern in (patterns or []): for pattern in (patterns or []):
if pattern.match(path): if pattern.match(path):
@ -158,32 +158,43 @@ def exclude_path(path, patterns):
return False return False
# For both IncludePattern and ExcludePattern, we require that
# the pattern either match the whole path or an initial segment
# of the path up to but not including a path separator. To
# unify the two cases, we add a path separator to the end of
# the path before matching.
class IncludePattern: class IncludePattern:
"""--include PATTERN """Literal files or directories listed on the command line
for some operations (e.g. extract, but not create).
If a directory is specified, all paths that start with that
path match as well. A trailing slash makes no difference.
""" """
def __init__(self, pattern): def __init__(self, pattern):
self.pattern = pattern self.pattern = pattern.rstrip(os.path.sep)+os.path.sep
def match(self, path): def match(self, path):
dir, name = os.path.split(path) return (path+os.path.sep).startswith(self.pattern)
return (path == self.pattern
or (dir + os.path.sep).startswith(self.pattern))
def __repr__(self): def __repr__(self):
return '%s(%s)' % (type(self), self.pattern) return '%s(%s)' % (type(self), self.pattern)
class ExcludePattern(IncludePattern): class ExcludePattern(IncludePattern):
""" """Shell glob patterns to exclude. A trailing slash means to
exclude the contents of a directory, but not the directory itself.
""" """
def __init__(self, pattern): def __init__(self, pattern):
self.pattern = self.dirpattern = pattern if pattern.endswith(os.path.sep):
if not pattern.endswith('/'): self.pattern = pattern+'*'+os.path.sep
self.dirpattern += '/*' else:
self.pattern = pattern+os.path.sep+'*'
# fnmatch and re.match both cache compiled regular expressions.
# Nevertheless, this is about 10 times faster.
self.regex = re.compile(translate(self.pattern))
def match(self, path): def match(self, path):
dir, name = os.path.split(path) return self.regex.match(path+os.path.sep) is not None
return fnmatchcase(path, self.pattern) or fnmatchcase(dir + '/', self.dirpattern)
def __repr__(self): def __repr__(self):
return '%s(%s)' % (type(self), self.pattern) return '%s(%s)' % (type(self), self.pattern)

View file

@ -50,7 +50,7 @@ class FormatTimedeltaTestCase(AtticTestCase):
class PatternTestCase(AtticTestCase): class PatternTestCase(AtticTestCase):
files = [ files = [
'/etc/passwd', '/etc/hosts', '/etc/passwd', '/etc/hosts', '/home',
'/home/user/.profile', '/home/user/.bashrc', '/home/user/.profile', '/home/user/.bashrc',
'/home/user2/.profile', '/home/user2/public_html/index.html', '/home/user2/.profile', '/home/user2/public_html/index.html',
'/var/log/messages', '/var/log/dmesg', '/var/log/messages', '/var/log/dmesg',
@ -61,13 +61,22 @@ class PatternTestCase(AtticTestCase):
return [path for path in self.files if not exclude_path(path, patterns)] return [path for path in self.files if not exclude_path(path, patterns)]
def test(self): def test(self):
self.assert_equal(self.evaluate(['/'], []), self.files)
self.assert_equal(self.evaluate([], []), self.files)
self.assert_equal(self.evaluate(['/'], ['/h']), self.files)
self.assert_equal(self.evaluate(['/'], ['/home']), self.assert_equal(self.evaluate(['/'], ['/home']),
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg']) ['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])
self.assert_equal(self.evaluate(['/'], ['/home/']),
['/etc/passwd', '/etc/hosts', '/home', '/var/log/messages', '/var/log/dmesg'])
self.assert_equal(self.evaluate(['/home/u'], []), [])
self.assert_equal(self.evaluate(['/', '/home', '/etc/hosts'], ['/']), [])
self.assert_equal(self.evaluate(['/home/'], ['/home/user2']),
['/home', '/home/user/.profile', '/home/user/.bashrc'])
self.assert_equal(self.evaluate(['/'], ['*.profile', '/var/log']), self.assert_equal(self.evaluate(['/'], ['*.profile', '/var/log']),
['/etc/passwd', '/etc/hosts', '/home/user/.bashrc', '/home/user2/public_html/index.html']) ['/etc/passwd', '/etc/hosts', '/home', '/home/user/.bashrc', '/home/user2/public_html/index.html'])
self.assert_equal(self.evaluate(['/'], ['/home/*/public_html', '*.profile', '*/log/*']), self.assert_equal(self.evaluate(['/'], ['/home/*/public_html', '*.profile', '*/log/*']),
['/etc/passwd', '/etc/hosts', '/home/user/.bashrc']) ['/etc/passwd', '/etc/hosts', '/home', '/home/user/.bashrc'])
self.assert_equal(self.evaluate(['/etc', '/var'], ['dmesg']), self.assert_equal(self.evaluate(['/etc/', '/var'], ['dmesg']),
['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg']) ['/etc/passwd', '/etc/hosts', '/var/log/messages', '/var/log/dmesg'])