瀏覽代碼

Merge pull request #2784 from ThomasWaldmann/prune-enhancements

prune enhancements / fixes
TW 8 年之前
父節點
當前提交
6b20f2af93
共有 4 個文件被更改,包括 25 次插入11 次删除
  1. 5 5
      src/borg/archiver.py
  2. 4 4
      src/borg/helpers.py
  3. 5 2
      src/borg/shellpattern.py
  4. 11 0
      src/borg/testsuite/shellpattern.py

+ 5 - 5
src/borg/archiver.py

@@ -1474,13 +1474,13 @@ class Archiver:
                              '"keep-secondly", "keep-minutely", "keep-hourly", "keep-daily", '
                              '"keep-secondly", "keep-minutely", "keep-hourly", "keep-daily", '
                              '"keep-weekly", "keep-monthly" or "keep-yearly" settings must be specified.')
                              '"keep-weekly", "keep-monthly" or "keep-yearly" settings must be specified.')
             return self.exit_code
             return self.exit_code
-        archives_checkpoints = manifest.archives.list(sort_by=['ts'], reverse=True)  # just a ArchiveInfo list
         if args.prefix:
         if args.prefix:
             args.glob_archives = 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)]
         checkpoints = [arch for arch in archives_checkpoints if is_checkpoint(arch.name)]
         # keep the latest checkpoint, if there is no later non-checkpoint archive
         # keep the latest checkpoint, if there is no later non-checkpoint archive
         if archives_checkpoints and checkpoints and archives_checkpoints[0] is checkpoints[0]:
         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)
         name = safe_encode(name)
         del self._archives[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.
         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.
         *sort_by* is a list of sort keys applied in reverse order.
         """
         """
         if isinstance(sort_by, (str, bytes)):
         if isinstance(sort_by, (str, bytes)):
             raise TypeError('sort_by must be a sequence of str')
             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]
         archives = [x for x in self.values() if regex.match(x.name) is not None]
         for sortkey in reversed(sort_by):
         for sortkey in reversed(sort_by):
             archives.sort(key=attrgetter(sortkey))
             archives.sort(key=attrgetter(sortkey))

+ 5 - 2
src/borg/shellpattern.py

@@ -2,7 +2,7 @@ import os
 import re
 import re
 
 
 
 
-def translate(pat):
+def translate(pat, match_end=r"\Z"):
     """Translate a shell-style pattern to a regular expression.
     """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
     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
     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.
     This function is derived from the "fnmatch" module distributed with the Python standard library.
 
 
     Copyright (C) 2001-2017 Python Software Foundation. All rights reserved.
     Copyright (C) 2001-2017 Python Software Foundation. All rights reserved.
@@ -59,4 +62,4 @@ def translate(pat):
         else:
         else:
             res += re.escape(c)
             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):
 def test_mismatch(path, patterns):
     for p in patterns:
     for p in patterns:
         assert not check(path, p)
         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')