]> arthur.barton.de Git - bup.git/commitdiff
prune-older-cmd: copy to bup.cmd.prune_older
authorRob Browning <rlb@defaultvalue.org>
Fri, 12 Feb 2021 20:19:20 +0000 (14:19 -0600)
committerRob Browning <rlb@defaultvalue.org>
Sat, 6 Mar 2021 18:29:39 +0000 (12:29 -0600)
Signed-off-by: Rob Browning <rlb@defaultvalue.org>
lib/bup/cmd/prune_older.py [new file with mode: 0755]
lib/cmd/prune-older-cmd.py [deleted file]

diff --git a/lib/bup/cmd/prune_older.py b/lib/bup/cmd/prune_older.py
new file mode 100755 (executable)
index 0000000..077ffb4
--- /dev/null
@@ -0,0 +1,183 @@
+#!/bin/sh
+"""": # -*-python-*-
+# https://sourceware.org/bugzilla/show_bug.cgi?id=26034
+export "BUP_ARGV_0"="$0"
+arg_i=1
+for arg in "$@"; do
+    export "BUP_ARGV_${arg_i}"="$arg"
+    shift
+    arg_i=$((arg_i + 1))
+done
+# Here to end of preamble replaced during install
+bup_python="$(dirname "$0")/../../../config/bin/python" || exit $?
+exec "$bup_python" "$0"
+"""
+# end of bup preamble
+
+from __future__ import absolute_import, print_function
+
+# Intentionally replace the dirname "$0" that python prepends
+import os, sys
+sys.path[0] = os.path.dirname(os.path.realpath(__file__)) + '/../..'
+
+from binascii import hexlify, unhexlify
+from collections import defaultdict
+from itertools import groupby
+from sys import stderr
+from time import localtime, strftime, time
+
+from bup import compat, git, options
+from bup.compat import argv_bytes, int_types
+from bup.gc import bup_gc
+from bup.helpers import die_if_errors, log, partition, period_as_secs
+from bup.io import byte_stream
+from bup.repo import LocalRepo
+from bup.rm import bup_rm
+
+
+def branches(refnames=tuple()):
+    return ((name[11:], hexlify(sha)) for (name,sha)
+            in git.list_refs(patterns=(b'refs/heads/' + n for n in refnames),
+                             limit_to_heads=True))
+
+def save_name(branch, utc):
+    return branch + b'/' \
+            + strftime('%Y-%m-%d-%H%M%S', localtime(utc)).encode('ascii')
+
+def classify_saves(saves, period_start):
+    """For each (utc, id) in saves, yield (True, (utc, id)) if the save
+    should be kept and (False, (utc, id)) if the save should be removed.
+    The ids are binary hashes.
+    """
+
+    def retain_newest_in_region(region):
+        for save in region[0:1]:
+            yield True, save
+        for save in region[1:]:
+            yield False, save
+
+    matches, rest = partition(lambda s: s[0] >= period_start['all'], saves)
+    for save in matches:
+        yield True, save
+
+    tm_ranges = ((period_start['dailies'], lambda s: localtime(s[0]).tm_yday),
+                 (period_start['monthlies'], lambda s: localtime(s[0]).tm_mon),
+                 (period_start['yearlies'], lambda s: localtime(s[0]).tm_year))
+
+    # Break the decreasing utc sorted saves up into the respective
+    # period ranges (dailies, monthlies, ...).  Within each range,
+    # group the saves by the period scale (days, months, ...), and
+    # then yield a "keep" action (True, utc) for the newest save in
+    # each group, and a "drop" action (False, utc) for the rest.
+    for pstart, time_region_id in tm_ranges:
+        matches, rest = partition(lambda s: s[0] >= pstart, rest)
+        for region_id, region_saves in groupby(matches, time_region_id):
+            for action in retain_newest_in_region(list(region_saves)):
+                yield action
+
+    # Finally, drop any saves older than the specified periods
+    for save in rest:
+        yield False, save
+
+
+optspec = """
+bup prune-older [options...] [BRANCH...]
+--
+keep-all-for=       retain all saves within the PERIOD
+keep-dailies-for=   retain the newest save per day within the PERIOD
+keep-monthlies-for= retain the newest save per month within the PERIOD
+keep-yearlies-for=  retain the newest save per year within the PERIOD
+wrt=                end all periods at this number of seconds since the epoch
+pretend       don't prune, just report intended actions to standard output
+gc            collect garbage after removals [1]
+gc-threshold= only rewrite a packfile if it's over this percent garbage [10]
+#,compress=   set compression level to # (0-9, 9 is highest) [1]
+v,verbose     increase log output (can be used more than once)
+unsafe        use the command even though it may be DANGEROUS
+"""
+
+o = options.Options(optspec)
+opt, flags, roots = o.parse(compat.argv[1:])
+roots = [argv_bytes(x) for x in roots]
+
+if not opt.unsafe:
+    o.fatal('refusing to run dangerous, experimental command without --unsafe')
+
+now = int(time()) if opt.wrt is None else opt.wrt
+if not isinstance(now, int_types):
+    o.fatal('--wrt value ' + str(now) + ' is not an integer')
+
+period_start = {}
+for period, extent in (('all', opt.keep_all_for),
+                       ('dailies', opt.keep_dailies_for),
+                       ('monthlies', opt.keep_monthlies_for),
+                       ('yearlies', opt.keep_yearlies_for)):
+    if extent:
+        secs = period_as_secs(extent.encode('ascii'))
+        if not secs:
+            o.fatal('%r is not a valid period' % extent)
+        period_start[period] = now - secs
+
+if not period_start:
+    o.fatal('at least one keep argument is required')
+
+period_start = defaultdict(lambda: float('inf'), period_start)
+
+if opt.verbose:
+    epoch_ymd = strftime('%Y-%m-%d-%H%M%S', localtime(0))
+    for kind in ['all', 'dailies', 'monthlies', 'yearlies']:
+        period_utc = period_start[kind]
+        if period_utc != float('inf'):
+            if not (period_utc > float('-inf')):
+                log('keeping all ' + kind)
+            else:
+                try:
+                    when = strftime('%Y-%m-%d-%H%M%S', localtime(period_utc))
+                    log('keeping ' + kind + ' since ' + when + '\n')
+                except ValueError as ex:
+                    if period_utc < 0:
+                        log('keeping %s since %d seconds before %s\n'
+                            %(kind, abs(period_utc), epoch_ymd))
+                    elif period_utc > 0:
+                        log('keeping %s since %d seconds after %s\n'
+                            %(kind, period_utc, epoch_ymd))
+                    else:
+                        log('keeping %s since %s\n' % (kind, epoch_ymd))
+
+git.check_repo_or_die()
+
+# This could be more efficient, but for now just build the whole list
+# in memory and let bup_rm() do some redundant work.
+
+def parse_info(f):
+    author_secs = f.readline().strip()
+    return int(author_secs)
+
+sys.stdout.flush()
+out = byte_stream(sys.stdout)
+
+removals = []
+for branch, branch_id in branches(roots):
+    die_if_errors()
+    saves = ((utc, unhexlify(oidx)) for (oidx, utc) in
+             git.rev_list(branch_id, format=b'%at', parse=parse_info))
+    for keep_save, (utc, id) in classify_saves(saves, period_start):
+        assert(keep_save in (False, True))
+        # FIXME: base removals on hashes
+        if opt.pretend:
+            out.write((b'+ ' if keep_save else b'- ')
+                      + save_name(branch, utc) + b'\n')
+        elif not keep_save:
+            removals.append(save_name(branch, utc))
+
+if not opt.pretend:
+    die_if_errors()
+    repo = LocalRepo()
+    bup_rm(repo, removals, compression=opt.compress, verbosity=opt.verbose)
+    if opt.gc:
+        die_if_errors()
+        bup_gc(threshold=opt.gc_threshold,
+               compression=opt.compress,
+               verbosity=opt.verbose)
+
+die_if_errors()
diff --git a/lib/cmd/prune-older-cmd.py b/lib/cmd/prune-older-cmd.py
deleted file mode 100755 (executable)
index 770e58a..0000000
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/bin/sh
-"""": # -*-python-*-
-# https://sourceware.org/bugzilla/show_bug.cgi?id=26034
-export "BUP_ARGV_0"="$0"
-arg_i=1
-for arg in "$@"; do
-    export "BUP_ARGV_${arg_i}"="$arg"
-    shift
-    arg_i=$((arg_i + 1))
-done
-# Here to end of preamble replaced during install
-bup_python="$(dirname "$0")/../../config/bin/python" || exit $?
-exec "$bup_python" "$0"
-"""
-# end of bup preamble
-
-from __future__ import absolute_import, print_function
-
-# Intentionally replace the dirname "$0" that python prepends
-import os, sys
-sys.path[0] = os.path.dirname(os.path.realpath(__file__)) + '/..'
-
-from binascii import hexlify, unhexlify
-from collections import defaultdict
-from itertools import groupby
-from sys import stderr
-from time import localtime, strftime, time
-import re
-
-from bup import compat, git, options
-from bup.compat import argv_bytes, int_types
-from bup.gc import bup_gc
-from bup.helpers import die_if_errors, log, partition, period_as_secs
-from bup.io import byte_stream
-from bup.repo import LocalRepo
-from bup.rm import bup_rm
-
-
-def branches(refnames=tuple()):
-    return ((name[11:], hexlify(sha)) for (name,sha)
-            in git.list_refs(patterns=(b'refs/heads/' + n for n in refnames),
-                             limit_to_heads=True))
-
-def save_name(branch, utc):
-    return branch + b'/' \
-            + strftime('%Y-%m-%d-%H%M%S', localtime(utc)).encode('ascii')
-
-def classify_saves(saves, period_start):
-    """For each (utc, id) in saves, yield (True, (utc, id)) if the save
-    should be kept and (False, (utc, id)) if the save should be removed.
-    The ids are binary hashes.
-    """
-
-    def retain_newest_in_region(region):
-        for save in region[0:1]:
-            yield True, save
-        for save in region[1:]:
-            yield False, save
-
-    matches, rest = partition(lambda s: s[0] >= period_start['all'], saves)
-    for save in matches:
-        yield True, save
-
-    tm_ranges = ((period_start['dailies'], lambda s: localtime(s[0]).tm_yday),
-                 (period_start['monthlies'], lambda s: localtime(s[0]).tm_mon),
-                 (period_start['yearlies'], lambda s: localtime(s[0]).tm_year))
-
-    # Break the decreasing utc sorted saves up into the respective
-    # period ranges (dailies, monthlies, ...).  Within each range,
-    # group the saves by the period scale (days, months, ...), and
-    # then yield a "keep" action (True, utc) for the newest save in
-    # each group, and a "drop" action (False, utc) for the rest.
-    for pstart, time_region_id in tm_ranges:
-        matches, rest = partition(lambda s: s[0] >= pstart, rest)
-        for region_id, region_saves in groupby(matches, time_region_id):
-            for action in retain_newest_in_region(list(region_saves)):
-                yield action
-
-    # Finally, drop any saves older than the specified periods
-    for save in rest:
-        yield False, save
-
-
-optspec = """
-bup prune-older [options...] [BRANCH...]
---
-keep-all-for=       retain all saves within the PERIOD
-keep-dailies-for=   retain the newest save per day within the PERIOD
-keep-monthlies-for= retain the newest save per month within the PERIOD
-keep-yearlies-for=  retain the newest save per year within the PERIOD
-wrt=                end all periods at this number of seconds since the epoch
-pretend       don't prune, just report intended actions to standard output
-gc            collect garbage after removals [1]
-gc-threshold= only rewrite a packfile if it's over this percent garbage [10]
-#,compress=   set compression level to # (0-9, 9 is highest) [1]
-v,verbose     increase log output (can be used more than once)
-unsafe        use the command even though it may be DANGEROUS
-"""
-
-o = options.Options(optspec)
-opt, flags, roots = o.parse(compat.argv[1:])
-roots = [argv_bytes(x) for x in roots]
-
-if not opt.unsafe:
-    o.fatal('refusing to run dangerous, experimental command without --unsafe')
-
-now = int(time()) if opt.wrt is None else opt.wrt
-if not isinstance(now, int_types):
-    o.fatal('--wrt value ' + str(now) + ' is not an integer')
-
-period_start = {}
-for period, extent in (('all', opt.keep_all_for),
-                       ('dailies', opt.keep_dailies_for),
-                       ('monthlies', opt.keep_monthlies_for),
-                       ('yearlies', opt.keep_yearlies_for)):
-    if extent:
-        secs = period_as_secs(extent.encode('ascii'))
-        if not secs:
-            o.fatal('%r is not a valid period' % extent)
-        period_start[period] = now - secs
-
-if not period_start:
-    o.fatal('at least one keep argument is required')
-
-period_start = defaultdict(lambda: float('inf'), period_start)
-
-if opt.verbose:
-    epoch_ymd = strftime('%Y-%m-%d-%H%M%S', localtime(0))
-    for kind in ['all', 'dailies', 'monthlies', 'yearlies']:
-        period_utc = period_start[kind]
-        if period_utc != float('inf'):
-            if not (period_utc > float('-inf')):
-                log('keeping all ' + kind)
-            else:
-                try:
-                    when = strftime('%Y-%m-%d-%H%M%S', localtime(period_utc))
-                    log('keeping ' + kind + ' since ' + when + '\n')
-                except ValueError as ex:
-                    if period_utc < 0:
-                        log('keeping %s since %d seconds before %s\n'
-                            %(kind, abs(period_utc), epoch_ymd))
-                    elif period_utc > 0:
-                        log('keeping %s since %d seconds after %s\n'
-                            %(kind, period_utc, epoch_ymd))
-                    else:
-                        log('keeping %s since %s\n' % (kind, epoch_ymd))
-
-git.check_repo_or_die()
-
-# This could be more efficient, but for now just build the whole list
-# in memory and let bup_rm() do some redundant work.
-
-def parse_info(f):
-    author_secs = f.readline().strip()
-    return int(author_secs)
-
-sys.stdout.flush()
-out = byte_stream(sys.stdout)
-
-removals = []
-for branch, branch_id in branches(roots):
-    die_if_errors()
-    saves = ((utc, unhexlify(oidx)) for (oidx, utc) in
-             git.rev_list(branch_id, format=b'%at', parse=parse_info))
-    for keep_save, (utc, id) in classify_saves(saves, period_start):
-        assert(keep_save in (False, True))
-        # FIXME: base removals on hashes
-        if opt.pretend:
-            out.write((b'+ ' if keep_save else b'- ')
-                      + save_name(branch, utc) + b'\n')
-        elif not keep_save:
-            removals.append(save_name(branch, utc))
-
-if not opt.pretend:
-    die_if_errors()
-    repo = LocalRepo()
-    bup_rm(repo, removals, compression=opt.compress, verbosity=opt.verbose)
-    if opt.gc:
-        die_if_errors()
-        bup_gc(threshold=opt.gc_threshold,
-               compression=opt.compress,
-               verbosity=opt.verbose)
-
-die_if_errors()