Sfoglia il codice sorgente

Merge pull request #2784 from ThomasWaldmann/prune-enhancements

prune enhancements / fixes
TW 8 anni fa
parent
commit
6b20f2af93

+ 5 - 5
src/borg/archiver.py

@@ -1474,13 +1474,13 @@ class Archiver:
                              '"keep-secondly", "keep-minutely", "keep-hourly", "keep-daily", '
                              '"keep-weekly", "keep-monthly" or "keep-yearly" settings must be specified.')
             return self.exit_code
-        archives_checkpoints = manifest.archives.list(sort_by=['ts'], reverse=True)  # just a ArchiveInfo list
         if args.prefix:
             args.glob_archives = args.prefix + '*'
-        if args.glob_archives:
-            regex = re.compile(shellpattern.translate(args.glob_archives))
-            archives_checkpoints = [arch for arch in archives_checkpoints if regex.match(arch.name) is not None]
-        is_checkpoint = re.compile(r'\.checkpoint(\.\d+)?$').search
+        checkpoint_re = r'\.checkpoint(\.\d+)?'
+        archives_checkpoints = manifest.archives.list(glob=args.glob_archives,
+                                                      match_end=r'(%s)?\Z' % checkpoint_re,
+                                                      sort_by=['ts'], reverse=True)
+        is_checkpoint = re.compile(r'(%s)\Z' % checkpoint_re).search
         checkpoints = [arch for arch in archives_checkpoints if is_checkpoint(arch.name)]
         # keep the latest checkpoint, if there is no later non-checkpoint archive
         if archives_checkpoints and checkpoints and archives_checkpoints[0] is checkpoints[0]:

+ 4 - 4
src/borg/helpers.py

@@ -219,18 +219,18 @@ class Archives(abc.MutableMapping):
         name = safe_encode(name)
         del self._archives[name]
 
-    def list(self, *, glob=None, sort_by=(), first=None, last=None, reverse=False):
+    def list(self, *, glob=None, match_end=r'\Z', sort_by=(), first=None, last=None, reverse=False):
         """
         Return list of ArchiveInfo instances according to the parameters.
 
-        First match *glob*, then *sort_by*. Apply *first* and *last* filters,
-        and possibly *reverse* the list.
+        First match *glob* (considering *match_end*), then *sort_by*.
+        Apply *first* and *last* filters, and then possibly *reverse* the list.
 
         *sort_by* is a list of sort keys applied in reverse order.
         """
         if isinstance(sort_by, (str, bytes)):
             raise TypeError('sort_by must be a sequence of str')
-        regex = re.compile(shellpattern.translate(glob or '*'))
+        regex = re.compile(shellpattern.translate(glob or '*', match_end=match_end))
         archives = [x for x in self.values() if regex.match(x.name) is not None]
         for sortkey in reversed(sort_by):
             archives.sort(key=attrgetter(sortkey))

+ 5 - 2
src/borg/shellpattern.py

@@ -2,7 +2,7 @@ import os
 import re
 
 
-def translate(pat):
+def translate(pat, match_end=r"\Z"):
     """Translate a shell-style pattern to a regular expression.
 
     The pattern may include ``**<sep>`` (<sep> stands for the platform-specific path separator; "/" on POSIX systems) for
@@ -10,6 +10,9 @@ def translate(pat):
     any path separator. Wrap meta-characters in brackets for a literal match (i.e. "[?]" to match the literal character
     "?").
 
+    Using match_end=regex one can give a regular expression that is used to match after the regex that is generated from
+    the pattern. The default is to match the end of the string.
+
     This function is derived from the "fnmatch" module distributed with the Python standard library.
 
     Copyright (C) 2001-2017 Python Software Foundation. All rights reserved.
@@ -59,4 +62,4 @@ def translate(pat):
         else:
             res += re.escape(c)
 
-    return res + r"\Z(?ms)"
+    return res + match_end + "(?ms)"

+ 11 - 0
src/borg/testsuite/shellpattern.py

@@ -111,3 +111,14 @@ def test_match(path, patterns):
 def test_mismatch(path, patterns):
     for p in patterns:
         assert not check(path, p)
+
+
+def test_match_end():
+    regex = shellpattern.translate("*-home")  # default is match_end == string end
+    assert re.match(regex, '2017-07-03-home')
+    assert not re.match(regex, '2017-07-03-home.checkpoint')
+
+    match_end = r'(%s)?\Z' % r'\.checkpoint(\.\d+)?'  # with/without checkpoint ending
+    regex = shellpattern.translate("*-home", match_end=match_end)
+    assert re.match(regex, '2017-07-03-home')
+    assert re.match(regex, '2017-07-03-home.checkpoint')