diff --git a/src/cmds/restic/cmd_forget.go b/src/cmds/restic/cmd_forget.go index ba268162c..8a2a98691 100644 --- a/src/cmds/restic/cmd_forget.go +++ b/src/cmds/restic/cmd_forget.go @@ -122,6 +122,11 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { return nil } + var tagLists []restic.TagList + for _, t := range opts.KeepTags { + tagLists = append(tagLists, restic.SplitTagList(t)) + } + policy := restic.ExpirePolicy{ Last: opts.Last, Hourly: opts.Hourly, @@ -129,7 +134,7 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { Weekly: opts.Weekly, Monthly: opts.Monthly, Yearly: opts.Yearly, - Tags: opts.KeepTags, + Tags: tagLists, } if policy.Empty() { diff --git a/src/restic/snapshot.go b/src/restic/snapshot.go index a641e5b8f..150f3b132 100644 --- a/src/restic/snapshot.go +++ b/src/restic/snapshot.go @@ -5,7 +5,6 @@ import ( "fmt" "os/user" "path/filepath" - "strings" "time" ) @@ -140,25 +139,15 @@ func (sn *Snapshot) hasTag(tag string) bool { return false } -// HasTags returns true if the snapshot has at least one of the tags. Tags -// are compared as strings, unless they contain a comma. Then each of the comma -// separated parts of the tag need to be present. -func (sn *Snapshot) HasTags(tags []string) bool { - if len(tags) == 0 { - return true - } -nextTag: - for _, tag := range tags { - for _, s := range strings.Split(tag, ",") { - if !sn.hasTag(s) { - // fail, try next tag - continue nextTag - } +// HasTags returns true if the snapshot has all the tags in l. +func (sn *Snapshot) HasTags(l []string) bool { + for _, tag := range l { + if !sn.hasTag(tag) { + return false } - return true } - return false + return true } func (sn *Snapshot) hasPath(path string) bool { @@ -170,33 +159,15 @@ func (sn *Snapshot) hasPath(path string) bool { return false } -// HasPaths returns true if the snapshot has at least one of the paths. Paths -// are compared as strings unless they contain a comma. Then each of the comma -// separated parts of the path need to be present. +// HasPaths returns true if the snapshot has all of the paths. func (sn *Snapshot) HasPaths(paths []string) bool { - if len(paths) == 0 { - return true - } -nextPath: for _, path := range paths { - for _, p := range strings.Split(path, ",") { - if !sn.hasPath(p) { - // fail, try next path - continue nextPath - } + if !sn.hasPath(path) { + return false } - return true } - return false -} - -// SamePaths returns true if the snapshot matches the entire paths set -func (sn *Snapshot) SamePaths(paths []string) bool { - if len(sn.Paths) != len(paths) { - return false - } - return sn.HasPaths(paths) + return true } // Snapshots is a list of snapshots. diff --git a/src/restic/snapshot_policy.go b/src/restic/snapshot_policy.go index 967d388a4..5e674fcc0 100644 --- a/src/restic/snapshot_policy.go +++ b/src/restic/snapshot_policy.go @@ -3,18 +3,32 @@ package restic import ( "reflect" "sort" + "strings" "time" ) +// TagList is a list of tags. +type TagList []string + +// SplitTagList splits a string into a list of tags. The tags in the string +// need to be separated by commas. Whitespace is stripped around the individual +// tags. +func SplitTagList(s string) (l TagList) { + for _, t := range strings.Split(s, ",") { + l = append(l, strings.TrimSpace(t)) + } + return l +} + // ExpirePolicy configures which snapshots should be automatically removed. type ExpirePolicy struct { - Last int // keep the last n snapshots - Hourly int // keep the last n hourly snapshots - Daily int // keep the last n daily snapshots - Weekly int // keep the last n weekly snapshots - Monthly int // keep the last n monthly snapshots - Yearly int // keep the last n yearly snapshots - Tags []string // keep all snapshots with these tags + Last int // keep the last n snapshots + Hourly int // keep the last n hourly snapshots + Daily int // keep the last n daily snapshots + Weekly int // keep the last n weekly snapshots + Monthly int // keep the last n monthly snapshots + Yearly int // keep the last n yearly snapshots + Tags []TagList // keep all snapshots that include at least one of the tag lists. } // Sum returns the maximum number of snapshots to be kept according to this @@ -94,11 +108,12 @@ func ApplyPolicy(list Snapshots, p ExpirePolicy) (keep, remove Snapshots) { var keepSnap bool // Tags are handled specially as they are not counted. - if len(p.Tags) > 0 { - if cur.HasTags(p.Tags) { + for _, l := range p.Tags { + if cur.HasTags(l) { keepSnap = true } } + // Now update the other buckets and see if they have some counts left. for i, b := range buckets { if b.Count > 0 { diff --git a/src/restic/snapshot_policy_test.go b/src/restic/snapshot_policy_test.go index 63dec3583..f7913ccba 100644 --- a/src/restic/snapshot_policy_test.go +++ b/src/restic/snapshot_policy_test.go @@ -27,7 +27,7 @@ func TestExpireSnapshotOps(t *testing.T) { p *restic.ExpirePolicy }{ {true, 0, &restic.ExpirePolicy{}}, - {true, 0, &restic.ExpirePolicy{Tags: []string{}}}, + {true, 0, &restic.ExpirePolicy{Tags: []restic.TagList{}}}, {false, 22, &restic.ExpirePolicy{Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}}, } for i, d := range data { @@ -163,9 +163,9 @@ var expireTests = []restic.ExpirePolicy{ {Daily: 2, Weekly: 2, Monthly: 6}, {Yearly: 10}, {Daily: 7, Weekly: 2, Monthly: 3, Yearly: 10}, - {Tags: []string{"foo"}}, - {Tags: []string{"foo,bar"}}, - {Tags: []string{"foo", "bar"}}, + {Tags: []restic.TagList{{"foo"}}}, + {Tags: []restic.TagList{{"foo", "bar"}}}, + {Tags: []restic.TagList{{"foo"}, {"bar"}}}, } func TestApplyPolicy(t *testing.T) {