From e4a5cdc5bc61fe5ddc5405ac38f4e12fb14287f7 Mon Sep 17 00:00:00 2001 From: Tobias Klein Date: Tue, 29 Aug 2017 18:30:13 +0200 Subject: [PATCH 1/4] forget: group-by-tags-only --- cmd/restic/cmd_forget.go | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index cb230944a..d0d32fd5f 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -38,9 +38,10 @@ type ForgetOptions struct { Tags restic.TagLists Paths []string - GroupByTags bool - DryRun bool - Prune bool + GroupByTags bool + GroupByTagsOnly bool + DryRun bool + Prune bool } var forgetOptions ForgetOptions @@ -58,6 +59,7 @@ func init() { f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") f.BoolVarP(&forgetOptions.GroupByTags, "group-by-tags", "G", false, "Group by host,paths,tags instead of just host,paths") + f.BoolVarP(&forgetOptions.GroupByTagsOnly, "group-by-tags-only", "", false, "Group by tags only instead of host,paths") // Sadly the commonly used shortcut `H` is already used. f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`") // Deprecated since 2017-03-07. @@ -107,12 +109,18 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { } } else { var tags []string - if opts.GroupByTags { + if opts.GroupByTags || opts.GroupByTagsOnly { tags = sn.Tags sort.StringSlice(tags).Sort() } sort.StringSlice(sn.Paths).Sort() - k, err := json.Marshal(key{Hostname: sn.Hostname, Tags: tags, Paths: sn.Paths}) + var k []byte + var err error + if opts.GroupByTagsOnly { + k, err = json.Marshal(key{Tags: tags}) + } else { + k, err = json.Marshal(key{Hostname: sn.Hostname, Tags: tags, Paths: sn.Paths}) + } if err != nil { return err } @@ -144,7 +152,9 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { if json.Unmarshal([]byte(k), &key) != nil { return err } - if opts.GroupByTags { + if opts.GroupByTagsOnly { + Verbosef("snapshots for tags [%v]:\n\n", strings.Join(key.Tags, ", ")) + } else if opts.GroupByTags { Verbosef("snapshots for host %v, tags [%v], paths: [%v]:\n\n", key.Hostname, strings.Join(key.Tags, ", "), strings.Join(key.Paths, ", ")) } else { Verbosef("snapshots for host %v, paths: [%v]:\n\n", key.Hostname, strings.Join(key.Paths, ", ")) From 1073bfba375b804e065678396f1ca504efbf5ece Mon Sep 17 00:00:00 2001 From: Tobias Klein Date: Wed, 6 Sep 2017 20:14:18 +0200 Subject: [PATCH 2/4] flexible grouping option for the forget-command --- cmd/restic/cmd_forget.go | 59 +++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index d0d32fd5f..8735676ac 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -38,8 +38,8 @@ type ForgetOptions struct { Tags restic.TagLists Paths []string - GroupByTags bool - GroupByTagsOnly bool + // Grouping + GroupBy string DryRun bool Prune bool } @@ -58,8 +58,6 @@ func init() { f.IntVarP(&forgetOptions.Yearly, "keep-yearly", "y", 0, "keep the last `n` yearly snapshots") f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") - f.BoolVarP(&forgetOptions.GroupByTags, "group-by-tags", "G", false, "Group by host,paths,tags instead of just host,paths") - f.BoolVarP(&forgetOptions.GroupByTagsOnly, "group-by-tags-only", "", false, "Group by tags only instead of host,paths") // Sadly the commonly used shortcut `H` is already used. f.StringVar(&forgetOptions.Host, "host", "", "only consider snapshots with the given `host`") // Deprecated since 2017-03-07. @@ -67,6 +65,7 @@ func init() { f.Var(&forgetOptions.Tags, "tag", "only consider snapshots which include this `taglist` in the format `tag[,tag,...]` (can be specified multiple times)") f.StringArrayVar(&forgetOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` (can be specified multiple times)") + f.StringVarP(&forgetOptions.GroupBy, "group-by", "g", "host,paths", "string for grouping snapshots by host,paths,tags") f.BoolVarP(&forgetOptions.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done") f.BoolVar(&forgetOptions.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed") @@ -93,6 +92,14 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { } snapshotGroups := make(map[string]restic.Snapshots) + var GroupByTag bool + var GroupByHost bool + var GroupByPath bool + + GroupByTag = strings.Contains( opts.GroupBy, "tag" ) + GroupByHost = strings.Contains( opts.GroupBy, "host" ) + GroupByPath = strings.Contains( opts.GroupBy, "path" ) + ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() for sn := range FindFilteredSnapshots(ctx, repo, opts.Host, opts.Tags, opts.Paths, args) { @@ -108,19 +115,28 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { Verbosef("would have removed snapshot %v\n", sn.ID().Str()) } } else { + // Determing grouping-keys var tags []string - if opts.GroupByTags || opts.GroupByTagsOnly { + var hostname string + var paths []string + + if GroupByTag { tags = sn.Tags sort.StringSlice(tags).Sort() } + if GroupByHost { + hostname = sn.Hostname + } + if GroupByPath { + paths = sn.Paths + } + sort.StringSlice(sn.Paths).Sort() var k []byte var err error - if opts.GroupByTagsOnly { - k, err = json.Marshal(key{Tags: tags}) - } else { - k, err = json.Marshal(key{Hostname: sn.Hostname, Tags: tags, Paths: sn.Paths}) - } + + k, err = json.Marshal(key{Tags: tags, Hostname: hostname, Paths: paths}) + if err != nil { return err } @@ -152,13 +168,24 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { if json.Unmarshal([]byte(k), &key) != nil { return err } - if opts.GroupByTagsOnly { - Verbosef("snapshots for tags [%v]:\n\n", strings.Join(key.Tags, ", ")) - } else if opts.GroupByTags { - Verbosef("snapshots for host %v, tags [%v], paths: [%v]:\n\n", key.Hostname, strings.Join(key.Tags, ", "), strings.Join(key.Paths, ", ")) - } else { - Verbosef("snapshots for host %v, paths: [%v]:\n\n", key.Hostname, strings.Join(key.Paths, ", ")) + + // Info + Verbosef( "snapshots" ) + var infoStrings []string + if GroupByTag { + infoStrings = append( infoStrings, "tags [" + strings.Join( key.Tags, ", " ) + "]" ) } + if GroupByHost { + infoStrings = append( infoStrings, "host [" + key.Hostname + "]" ) + } + if GroupByPath { + infoStrings = append( infoStrings, "paths [" + strings.Join( key.Paths, ", " ) + "]" ) + } + if infoStrings != nil { + Verbosef( " for (" + strings.Join( infoStrings, ", " ) + ")" ) + } + Verbosef( ":\n\n" ) + keep, remove := restic.ApplyPolicy(snapshotGroup, policy) if len(keep) != 0 && !gopts.Quiet { From 8f9ef4402b98a5652281365c89af05fa209c1412 Mon Sep 17 00:00:00 2001 From: Tobias Klein Date: Sat, 9 Sep 2017 13:04:12 +0200 Subject: [PATCH 3/4] error in case of unknown grouping option --- cmd/restic/cmd_forget.go | 16 +++++++++++++--- doc/man/restic-forget.1 | 8 ++++---- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 8735676ac..8595fd13c 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/restic/restic/internal/restic" + "github.com/restic/restic/internal/errors" "github.com/spf13/cobra" ) @@ -95,10 +96,19 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { var GroupByTag bool var GroupByHost bool var GroupByPath bool + var GroupOptionList []string - GroupByTag = strings.Contains( opts.GroupBy, "tag" ) - GroupByHost = strings.Contains( opts.GroupBy, "host" ) - GroupByPath = strings.Contains( opts.GroupBy, "path" ) + GroupOptionList = strings.Split( opts.GroupBy, "," ) + + for _, option := range GroupOptionList { + switch( option ) { + case "host": GroupByHost = true + case "paths": GroupByPath = true + case "tags": GroupByTag = true + case "": + default: return errors.Fatal( "unknown grouping option: '" + option + "'" ) + } + } ctx, cancel := context.WithCancel(gopts.ctx) defer cancel() diff --git a/doc/man/restic-forget.1 b/doc/man/restic-forget.1 index b9a1ff550..b7462eaf2 100644 --- a/doc/man/restic-forget.1 +++ b/doc/man/restic-forget.1 @@ -50,10 +50,6 @@ data after 'forget' was run successfully, see the 'prune' command. \fB\-\-keep\-tag\fP=[] keep snapshots with this \fB\fCtaglist\fR (can be specified multiple times) -.PP -\fB\-G\fP, \fB\-\-group\-by\-tags\fP[=false] - Group by host,paths,tags instead of just host,paths - .PP \fB\-\-host\fP="" only consider snapshots with the given \fB\fChost\fR @@ -70,6 +66,10 @@ data after 'forget' was run successfully, see the 'prune' command. \fB\-\-path\fP=[] only consider snapshots which include this (absolute) \fB\fCpath\fR (can be specified multiple times) +.PP +\fB\-g\fP, \fB\-\-group\-by\fP="host,paths" + string for grouping snapshots by host,paths,tags + .PP \fB\-n\fP, \fB\-\-dry\-run\fP[=false] do not delete anything, just print what would be done From ed30bd7b76fad4bbc0b44029e9f2127000c0dbbf Mon Sep 17 00:00:00 2001 From: Tobias Klein Date: Sat, 9 Sep 2017 18:19:19 +0200 Subject: [PATCH 4/4] gofmt --- cmd/restic/cmd_forget.go | 44 ++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index 8595fd13c..d3f42d4d0 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -6,8 +6,8 @@ import ( "sort" "strings" - "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/errors" + "github.com/restic/restic/internal/restic" "github.com/spf13/cobra" ) @@ -40,9 +40,9 @@ type ForgetOptions struct { Paths []string // Grouping - GroupBy string - DryRun bool - Prune bool + GroupBy string + DryRun bool + Prune bool } var forgetOptions ForgetOptions @@ -93,20 +93,24 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { } snapshotGroups := make(map[string]restic.Snapshots) - var GroupByTag bool - var GroupByHost bool - var GroupByPath bool + var GroupByTag bool + var GroupByHost bool + var GroupByPath bool var GroupOptionList []string - GroupOptionList = strings.Split( opts.GroupBy, "," ) + GroupOptionList = strings.Split(opts.GroupBy, ",") for _, option := range GroupOptionList { - switch( option ) { - case "host": GroupByHost = true - case "paths": GroupByPath = true - case "tags": GroupByTag = true - case "": - default: return errors.Fatal( "unknown grouping option: '" + option + "'" ) + switch option { + case "host": + GroupByHost = true + case "paths": + GroupByPath = true + case "tags": + GroupByTag = true + case "": + default: + return errors.Fatal("unknown grouping option: '" + option + "'") } } @@ -180,21 +184,21 @@ func runForget(opts ForgetOptions, gopts GlobalOptions, args []string) error { } // Info - Verbosef( "snapshots" ) + Verbosef("snapshots") var infoStrings []string if GroupByTag { - infoStrings = append( infoStrings, "tags [" + strings.Join( key.Tags, ", " ) + "]" ) + infoStrings = append(infoStrings, "tags ["+strings.Join(key.Tags, ", ")+"]") } if GroupByHost { - infoStrings = append( infoStrings, "host [" + key.Hostname + "]" ) + infoStrings = append(infoStrings, "host ["+key.Hostname+"]") } if GroupByPath { - infoStrings = append( infoStrings, "paths [" + strings.Join( key.Paths, ", " ) + "]" ) + infoStrings = append(infoStrings, "paths ["+strings.Join(key.Paths, ", ")+"]") } if infoStrings != nil { - Verbosef( " for (" + strings.Join( infoStrings, ", " ) + ")" ) + Verbosef(" for (" + strings.Join(infoStrings, ", ") + ")") } - Verbosef( ":\n\n" ) + Verbosef(":\n\n") keep, remove := restic.ApplyPolicy(snapshotGroup, policy)