From b743fd09ab0c56484c1400d1bbdfc08a4836e157 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 2 May 2016 01:12:15 +0200 Subject: [PATCH] borg prune: ignore checkpoints, fixes #997 also: - add a test for this - add some words to borg create help about the archive name --- borg/archiver.py | 8 ++++++++ borg/testsuite/archiver.py | 9 ++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/borg/archiver.py b/borg/archiver.py index 1b2d6875f..995278b75 100644 --- a/borg/archiver.py +++ b/borg/archiver.py @@ -7,6 +7,7 @@ import functools import inspect import io import os +import re import shlex import signal import stat @@ -574,6 +575,10 @@ class Archiver: archives = manifest.list_archive_infos(sort_by='ts', reverse=True) # just a ArchiveInfo list if args.prefix: archives = [archive for archive in archives if archive.name.startswith(args.prefix)] + # ignore all checkpoint archives to avoid keeping one (which is an incomplete backup) + # that is newer than a successfully completed backup - and killing the successful backup. + is_checkpoint = re.compile(r'\.checkpoint(\.\d+)?$').search + archives = [archive for archive in archives if not is_checkpoint(archive.name)] keep = [] if args.within: keep += prune_within(archives, args.within) @@ -988,6 +993,9 @@ class Archiver: traversing all paths specified. The archive will consume almost no disk space for files or parts of files that have already been stored in other archives. + The archive name needs to be unique. It must not end in '.checkpoint' or + '.checkpoint.N' (with N being a number), because these names are used for + checkpoints and treated in special ways. To speed up pulling backups over sshfs and similar network file systems which do not provide correct inode information the --ignore-inode flag can be used. This diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index 7ee13a77f..6a89213e1 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -863,15 +863,22 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd('init', self.repository_location) self.cmd('create', self.repository_location + '::test1', src_dir) self.cmd('create', self.repository_location + '::test2', src_dir) + # these are not really a checkpoints, but they look like some: + self.cmd('create', self.repository_location + '::test3.checkpoint', src_dir) + self.cmd('create', self.repository_location + '::test3.checkpoint.1', src_dir) output = self.cmd('prune', '-v', '--list', '--dry-run', self.repository_location, '--keep-daily=2') - self.assert_in('Keeping archive: test2', output) self.assert_in('Would prune: test1', output) + # must keep the latest non-checkpoint archive: + self.assert_in('Keeping archive: test2', output) output = self.cmd('list', self.repository_location) self.assert_in('test1', output) self.assert_in('test2', output) + self.assert_in('test3.checkpoint', output) + self.assert_in('test3.checkpoint.1', output) self.cmd('prune', self.repository_location, '--keep-daily=2') output = self.cmd('list', self.repository_location) self.assert_not_in('test1', output) + # the latest non-checkpoint archive must be still there: self.assert_in('test2', output) def test_prune_repository_save_space(self):