diff --git a/changelog/unreleased/issue-3624 b/changelog/unreleased/issue-3624 new file mode 100644 index 000000000..53837e61c --- /dev/null +++ b/changelog/unreleased/issue-3624 @@ -0,0 +1,12 @@ +Enhancement: Keep oldest snapshot when there aren't enough snapshots + +The `forget` command does not preserve the oldest snapshot incase the +keep-* parameters are not satisfied, which led to users not being able to +preserve old data. Now, restic will always preserve the oldest snapshot +whenever any of the keep-* options to the `forget` command are not +satisfied. + + +https://github.com/restic/restic/issues/3624 +https://github.com/restic/restic/pull/4366 +https://forum.restic.net/t/keeping-yearly-snapshots-policy-when-backup-began-during-the-year/4670/2 diff --git a/internal/restic/snapshot_policy.go b/internal/restic/snapshot_policy.go index bec594707..1d6e129a2 100644 --- a/internal/restic/snapshot_policy.go +++ b/internal/restic/snapshot_policy.go @@ -256,7 +256,7 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots, reason // -1 means "keep all" if b.Count > 0 || b.Count == -1 { val := b.bucker(cur.Time, nr) - if val != b.Last { + if val != b.Last || nr == len(list)-1 { debug.Log("keep %v %v, bucker %v, val %v\n", cur.Time, cur.id.Str(), i, val) keepSnap = true buckets[i].Last = val diff --git a/internal/restic/testdata/policy_keep_snapshots_16 b/internal/restic/testdata/policy_keep_snapshots_16 index d0cae94b5..da6f43a1c 100644 --- a/internal/restic/testdata/policy_keep_snapshots_16 +++ b/internal/restic/testdata/policy_keep_snapshots_16 @@ -14,6 +14,11 @@ "time": "2014-11-22T10:20:30Z", "tree": null, "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null } ], "reasons": [ @@ -55,6 +60,19 @@ "counters": { "yearly": 7 } + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "yearly snapshot" + ], + "counters": { + "yearly": 6 + } } ] } \ No newline at end of file diff --git a/internal/restic/testdata/policy_keep_snapshots_17 b/internal/restic/testdata/policy_keep_snapshots_17 index 742b8005b..ee728d4e0 100644 --- a/internal/restic/testdata/policy_keep_snapshots_17 +++ b/internal/restic/testdata/policy_keep_snapshots_17 @@ -49,6 +49,11 @@ "time": "2014-11-22T10:20:30Z", "tree": null, "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null } ], "reasons": [ @@ -201,6 +206,19 @@ "counters": { "yearly": 7 } + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "yearly snapshot" + ], + "counters": { + "yearly": 6 + } } ] } \ No newline at end of file diff --git a/internal/restic/testdata/policy_keep_snapshots_39 b/internal/restic/testdata/policy_keep_snapshots_39 index a8e6ca827..4b111503b 100644 --- a/internal/restic/testdata/policy_keep_snapshots_39 +++ b/internal/restic/testdata/policy_keep_snapshots_39 @@ -57,6 +57,11 @@ "time": "2014-08-22T10:20:30Z", "tree": null, "paths": null + }, + { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null } ], "reasons": [ @@ -189,6 +194,18 @@ "monthly snapshot" ], "counters": {"Monthly": -1, "Yearly": -1} + }, + { + "snapshot": { + "time": "2014-08-08T10:20:30Z", + "tree": null, + "paths": null + }, + "matches": [ + "monthly snapshot", + "yearly snapshot" + ], + "counters": {"Monthly": -1, "Yearly": -1} } ] } \ No newline at end of file