diff --git a/src/borg/helpers.py b/src/borg/helpers.py index 43ae2d26a..6252a5e7c 100644 --- a/src/borg/helpers.py +++ b/src/borg/helpers.py @@ -518,6 +518,17 @@ class PatternBase: raise NotImplementedError +class PathFullPattern(PatternBase): + """Full match of a path.""" + PREFIX = "pf" + + def _prepare(self, pattern): + self.pattern = os.path.normpath(pattern) + + def _match(self, path): + return path == self.pattern + + # For PathPrefixPattern, FnmatchPattern and ShellPattern, 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. @@ -600,6 +611,7 @@ class RegexPattern(PatternBase): _PATTERN_STYLES = set([ FnmatchPattern, + PathFullPattern, PathPrefixPattern, RegexPattern, ShellPattern, diff --git a/src/borg/testsuite/helpers.py b/src/borg/testsuite/helpers.py index 727f1628a..19c5e9c51 100644 --- a/src/borg/testsuite/helpers.py +++ b/src/borg/testsuite/helpers.py @@ -25,7 +25,8 @@ from ..helpers import parse_timestamp, ChunkIteratorFileWrapper, ChunkerParams, from ..helpers import ProgressIndicatorPercent, ProgressIndicatorEndless from ..helpers import load_exclude_file, load_pattern_file from ..helpers import CompressionSpec, ComprSpec, CompressionDecider1, CompressionDecider2 -from ..helpers import parse_pattern, PatternMatcher, RegexPattern, PathPrefixPattern, FnmatchPattern, ShellPattern +from ..helpers import parse_pattern, PatternMatcher +from ..helpers import PathFullPattern, PathPrefixPattern, FnmatchPattern, ShellPattern, RegexPattern from ..helpers import swidth_slice from ..helpers import chunkit from ..helpers import safe_ns, safe_s @@ -254,6 +255,35 @@ def check_patterns(files, pattern, expected): assert matched == (files if expected is None else expected) +@pytest.mark.parametrize("pattern, expected", [ + # "None" means all files, i.e. all match the given pattern + ("/", []), + ("/home", ["/home"]), + ("/home///", ["/home"]), + ("/./home", ["/home"]), + ("/home/user", ["/home/user"]), + ("/home/user2", ["/home/user2"]), + ("/home/user/.bashrc", ["/home/user/.bashrc"]), + ]) +def test_patterns_full(pattern, expected): + files = ["/home", "/home/user", "/home/user2", "/home/user/.bashrc", ] + + check_patterns(files, PathFullPattern(pattern), expected) + + +@pytest.mark.parametrize("pattern, expected", [ + # "None" means all files, i.e. all match the given pattern + ("", []), + ("relative", []), + ("relative/path/", ["relative/path"]), + ("relative/path", ["relative/path"]), + ]) +def test_patterns_full_relative(pattern, expected): + files = ["relative/path", "relative/path2", ] + + check_patterns(files, PathFullPattern(pattern), expected) + + @pytest.mark.parametrize("pattern, expected", [ # "None" means all files, i.e. all match the given pattern ("/", None),