mirror of https://github.com/borgbackup/borg.git
163 lines
6.9 KiB
Python
163 lines
6.9 KiB
Python
import argparse
|
|
import logging
|
|
|
|
from ._common import with_repository, Highlander
|
|
from ..archive import Archive, Statistics
|
|
from ..cache import Cache
|
|
from ..constants import * # NOQA
|
|
from ..helpers import log_multi, format_archive, sig_int
|
|
from ..manifest import Manifest
|
|
|
|
from ..logger import create_logger
|
|
|
|
logger = create_logger()
|
|
|
|
|
|
class DeleteMixIn:
|
|
@with_repository(exclusive=True, manifest=False)
|
|
def do_delete(self, args, repository):
|
|
"""Delete archives"""
|
|
self.output_list = args.output_list
|
|
dry_run = args.dry_run
|
|
manifest = Manifest.load(repository, (Manifest.Operation.DELETE,))
|
|
archive_names = tuple(x.name for x in manifest.archives.list_considering(args))
|
|
if not archive_names:
|
|
return self.exit_code
|
|
if args.match_archives is None and args.first == 0 and args.last == 0:
|
|
self.print_error(
|
|
"Aborting: if you really want to delete all archives, please use -a 'sh:*' "
|
|
"or just delete the whole repository (might be much faster)."
|
|
)
|
|
return EXIT_ERROR
|
|
|
|
if args.forced == 2:
|
|
deleted = False
|
|
logger_list = logging.getLogger("borg.output.list")
|
|
for i, archive_name in enumerate(archive_names, 1):
|
|
try:
|
|
current_archive = manifest.archives.pop(archive_name)
|
|
except KeyError:
|
|
self.exit_code = EXIT_WARNING
|
|
logger.warning(f"Archive {archive_name} not found ({i}/{len(archive_names)}).")
|
|
else:
|
|
deleted = True
|
|
if self.output_list:
|
|
msg = "Would delete: {} ({}/{})" if dry_run else "Deleted archive: {} ({}/{})"
|
|
logger_list.info(msg.format(format_archive(current_archive), i, len(archive_names)))
|
|
if dry_run:
|
|
logger.info("Finished dry-run.")
|
|
elif deleted:
|
|
manifest.write()
|
|
# note: might crash in compact() after committing the repo
|
|
repository.commit(compact=False)
|
|
logger.warning('Done. Run "borg check --repair" to clean up the mess.')
|
|
else:
|
|
logger.warning("Aborted.")
|
|
return self.exit_code
|
|
|
|
stats = Statistics(iec=args.iec)
|
|
with Cache(repository, manifest, progress=args.progress, lock_wait=self.lock_wait, iec=args.iec) as cache:
|
|
|
|
def checkpoint_func():
|
|
manifest.write()
|
|
repository.commit(compact=False)
|
|
cache.commit()
|
|
|
|
msg_delete = "Would delete archive: {} ({}/{})" if dry_run else "Deleting archive: {} ({}/{})"
|
|
msg_not_found = "Archive {} not found ({}/{})."
|
|
logger_list = logging.getLogger("borg.output.list")
|
|
uncommitted_deletes = 0
|
|
for i, archive_name in enumerate(archive_names, 1):
|
|
if sig_int and sig_int.action_done():
|
|
break
|
|
try:
|
|
archive_info = manifest.archives[archive_name]
|
|
except KeyError:
|
|
logger.warning(msg_not_found.format(archive_name, i, len(archive_names)))
|
|
else:
|
|
if self.output_list:
|
|
logger_list.info(msg_delete.format(format_archive(archive_info), i, len(archive_names)))
|
|
|
|
if not dry_run:
|
|
archive = Archive(manifest, archive_name, cache=cache)
|
|
archive.delete(stats, progress=args.progress, forced=args.forced)
|
|
checkpointed = self.maybe_checkpoint(
|
|
checkpoint_func=checkpoint_func, checkpoint_interval=args.checkpoint_interval
|
|
)
|
|
uncommitted_deletes = 0 if checkpointed else (uncommitted_deletes + 1)
|
|
if sig_int:
|
|
# Ctrl-C / SIGINT: do not checkpoint (commit) again, we already have a checkpoint in this case.
|
|
raise Error("Got Ctrl-C / SIGINT.")
|
|
elif uncommitted_deletes > 0:
|
|
checkpoint_func()
|
|
if args.stats:
|
|
log_multi(str(stats), logger=logging.getLogger("borg.output.stats"))
|
|
|
|
return self.exit_code
|
|
|
|
def build_parser_delete(self, subparsers, common_parser, mid_common_parser):
|
|
from ._common import process_epilog, define_archive_filters_group
|
|
|
|
delete_epilog = process_epilog(
|
|
"""
|
|
This command deletes archives from the repository.
|
|
|
|
Important: When deleting archives, repository disk space is **not** freed until
|
|
you run ``borg compact``.
|
|
|
|
When in doubt, use ``--dry-run --list`` to see what would be deleted.
|
|
|
|
When using ``--stats``, you will get some statistics about how much data was
|
|
deleted - the "Deleted data" deduplicated size there is most interesting as
|
|
that is how much your repository will shrink.
|
|
Please note that the "All archives" stats refer to the state after deletion.
|
|
|
|
You can delete multiple archives by specifying a matching pattern,
|
|
using the ``--match-archives PATTERN`` option (for more info on these patterns,
|
|
see :ref:`borg_patterns`).
|
|
|
|
Always first use ``--dry-run --list`` to see what would be deleted.
|
|
"""
|
|
)
|
|
subparser = subparsers.add_parser(
|
|
"delete",
|
|
parents=[common_parser],
|
|
add_help=False,
|
|
description=self.do_delete.__doc__,
|
|
epilog=delete_epilog,
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
help="delete archive",
|
|
)
|
|
subparser.set_defaults(func=self.do_delete)
|
|
subparser.add_argument("-n", "--dry-run", dest="dry_run", action="store_true", help="do not change repository")
|
|
subparser.add_argument(
|
|
"--list", dest="output_list", action="store_true", help="output verbose list of archives"
|
|
)
|
|
subparser.add_argument(
|
|
"--consider-checkpoints",
|
|
action="store_true",
|
|
dest="consider_checkpoints",
|
|
help="consider checkpoint archives for deletion (default: not considered).",
|
|
)
|
|
subparser.add_argument(
|
|
"-s", "--stats", dest="stats", action="store_true", help="print statistics for the deleted archive"
|
|
)
|
|
subparser.add_argument(
|
|
"--force",
|
|
dest="forced",
|
|
action="count",
|
|
default=0,
|
|
help="force deletion of corrupted archives, " "use ``--force --force`` in case ``--force`` does not work.",
|
|
)
|
|
subparser.add_argument(
|
|
"-c",
|
|
"--checkpoint-interval",
|
|
metavar="SECONDS",
|
|
dest="checkpoint_interval",
|
|
type=int,
|
|
default=1800,
|
|
action=Highlander,
|
|
help="write checkpoint every SECONDS seconds (Default: 1800)",
|
|
)
|
|
define_archive_filters_group(subparser)
|