From dc9b6378f3eb9ca6bfc73dbeb057f58d7ee3e580 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Thu, 6 Feb 2025 21:52:02 +0100 Subject: [PATCH] move cli flags into AddFlags on option structs --- cmd/restic/cmd_backup.go | 99 +++++++++++++++--------------- cmd/restic/cmd_cache.go | 13 ++-- cmd/restic/cmd_check.go | 21 ++++--- cmd/restic/cmd_copy.go | 11 ++-- cmd/restic/cmd_debug.go | 13 ++-- cmd/restic/cmd_diff.go | 9 ++- cmd/restic/cmd_dump.go | 13 ++-- cmd/restic/cmd_find.go | 33 +++++----- cmd/restic/cmd_forget.go | 61 +++++++++--------- cmd/restic/cmd_generate.go | 16 +++-- cmd/restic/cmd_init.go | 13 ++-- cmd/restic/cmd_ls.go | 21 ++++--- cmd/restic/cmd_migrate.go | 8 ++- cmd/restic/cmd_mount.go | 27 ++++---- cmd/restic/cmd_prune.go | 29 +++++---- cmd/restic/cmd_repair_index.go | 10 +-- cmd/restic/cmd_repair_snapshots.go | 15 +++-- cmd/restic/cmd_restore.go | 35 ++++++----- cmd/restic/cmd_rewrite.go | 23 ++++--- cmd/restic/cmd_self_update.go | 9 ++- cmd/restic/cmd_snapshots.go | 25 ++++---- cmd/restic/cmd_stats.go | 11 ++-- cmd/restic/cmd_tag.go | 15 +++-- cmd/restic/cmd_unlock.go | 8 ++- 24 files changed, 307 insertions(+), 231 deletions(-) diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 93b4556c7..b7f0d7af6 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -15,6 +15,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "golang.org/x/sync/errgroup" "github.com/restic/restic/internal/archiver" @@ -97,6 +98,55 @@ type BackupOptions struct { SkipIfUnchanged bool } +func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) { + f.StringVar(&opts.Parent, "parent", "", "use this parent `snapshot` (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)") + opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} + f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") + f.BoolVarP(&opts.Force, "force", "f", false, `force re-reading the source files/directories (overrides the "parent" flag)`) + + opts.ExcludePatternOptions.Add(f) + + f.BoolVarP(&opts.ExcludeOtherFS, "one-file-system", "x", false, "exclude other file systems, don't cross filesystem boundaries and subvolumes") + f.StringArrayVar(&opts.ExcludeIfPresent, "exclude-if-present", nil, "takes `filename[:header]`, exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)") + f.BoolVar(&opts.ExcludeCaches, "exclude-caches", false, `excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard`) + f.StringVar(&opts.ExcludeLargerThan, "exclude-larger-than", "", "max `size` of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)") + f.BoolVar(&opts.Stdin, "stdin", false, "read backup from stdin") + f.StringVar(&opts.StdinFilename, "stdin-filename", "stdin", "`filename` to use when reading from stdin") + f.BoolVar(&opts.StdinCommand, "stdin-from-command", false, "interpret arguments as command to execute and store its stdout") + f.Var(&opts.Tags, "tag", "add `tags` for the new snapshot in the format `tag[,tag,...]` (can be specified multiple times)") + f.UintVar(&opts.ReadConcurrency, "read-concurrency", 0, "read `n` files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)") + f.StringVarP(&opts.Host, "host", "H", "", "set the `hostname` for the snapshot manually (default: $RESTIC_HOST). To prevent an expensive rescan use the \"parent\" flag") + f.StringVar(&opts.Host, "hostname", "", "set the `hostname` for the snapshot manually") + err := f.MarkDeprecated("hostname", "use --host") + if err != nil { + // MarkDeprecated only returns an error when the flag could not be found + panic(err) + } + f.StringArrayVar(&opts.FilesFrom, "files-from", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") + f.StringArrayVar(&opts.FilesFromVerbatim, "files-from-verbatim", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") + f.StringArrayVar(&opts.FilesFromRaw, "files-from-raw", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") + f.StringVar(&opts.TimeStamp, "time", "", "`time` of the backup (ex. '2012-11-01 22:08:41') (default: now)") + f.BoolVar(&opts.WithAtime, "with-atime", false, "store the atime for all files and directories") + f.BoolVar(&opts.IgnoreInode, "ignore-inode", false, "ignore inode number and ctime changes when checking for modified files") + f.BoolVar(&opts.IgnoreCtime, "ignore-ctime", false, "ignore ctime changes when checking for modified files") + f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not upload or write any data, just show what would be done") + f.BoolVar(&opts.NoScan, "no-scan", false, "do not run scanner to estimate size of backup") + if runtime.GOOS == "windows" { + f.BoolVar(&opts.UseFsSnapshot, "use-fs-snapshot", false, "use filesystem snapshot where possible (currently only Windows VSS)") + f.BoolVar(&opts.ExcludeCloudFiles, "exclude-cloud-files", false, "excludes online-only cloud files (such as OneDrive Files On-Demand)") + } + f.BoolVar(&opts.SkipIfUnchanged, "skip-if-unchanged", false, "skip snapshot creation if identical to parent snapshot") + + // parse read concurrency from env, on error the default value will be used + readConcurrency, _ := strconv.ParseUint(os.Getenv("RESTIC_READ_CONCURRENCY"), 10, 32) + opts.ReadConcurrency = uint(readConcurrency) + + // parse host from env, if not exists or empty the default value will be used + if host := os.Getenv("RESTIC_HOST"); host != "" { + opts.Host = host + } +} + var backupOptions BackupOptions var backupFSTestHook func(fs fs.FS) fs.FS @@ -105,54 +155,7 @@ var ErrInvalidSourceData = errors.New("at least one source file could not be rea func init() { cmdRoot.AddCommand(cmdBackup) - - f := cmdBackup.Flags() - f.StringVar(&backupOptions.Parent, "parent", "", "use this parent `snapshot` (default: latest snapshot in the group determined by --group-by and not newer than the timestamp determined by --time)") - backupOptions.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} - f.VarP(&backupOptions.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") - f.BoolVarP(&backupOptions.Force, "force", "f", false, `force re-reading the source files/directories (overrides the "parent" flag)`) - - backupOptions.ExcludePatternOptions.Add(f) - - f.BoolVarP(&backupOptions.ExcludeOtherFS, "one-file-system", "x", false, "exclude other file systems, don't cross filesystem boundaries and subvolumes") - f.StringArrayVar(&backupOptions.ExcludeIfPresent, "exclude-if-present", nil, "takes `filename[:header]`, exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)") - f.BoolVar(&backupOptions.ExcludeCaches, "exclude-caches", false, `excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard`) - f.StringVar(&backupOptions.ExcludeLargerThan, "exclude-larger-than", "", "max `size` of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)") - f.BoolVar(&backupOptions.Stdin, "stdin", false, "read backup from stdin") - f.StringVar(&backupOptions.StdinFilename, "stdin-filename", "stdin", "`filename` to use when reading from stdin") - f.BoolVar(&backupOptions.StdinCommand, "stdin-from-command", false, "interpret arguments as command to execute and store its stdout") - f.Var(&backupOptions.Tags, "tag", "add `tags` for the new snapshot in the format `tag[,tag,...]` (can be specified multiple times)") - f.UintVar(&backupOptions.ReadConcurrency, "read-concurrency", 0, "read `n` files concurrently (default: $RESTIC_READ_CONCURRENCY or 2)") - f.StringVarP(&backupOptions.Host, "host", "H", "", "set the `hostname` for the snapshot manually (default: $RESTIC_HOST). To prevent an expensive rescan use the \"parent\" flag") - f.StringVar(&backupOptions.Host, "hostname", "", "set the `hostname` for the snapshot manually") - err := f.MarkDeprecated("hostname", "use --host") - if err != nil { - // MarkDeprecated only returns an error when the flag could not be found - panic(err) - } - f.StringArrayVar(&backupOptions.FilesFrom, "files-from", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") - f.StringArrayVar(&backupOptions.FilesFromVerbatim, "files-from-verbatim", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") - f.StringArrayVar(&backupOptions.FilesFromRaw, "files-from-raw", nil, "read the files to backup from `file` (can be combined with file args; can be specified multiple times)") - f.StringVar(&backupOptions.TimeStamp, "time", "", "`time` of the backup (ex. '2012-11-01 22:08:41') (default: now)") - f.BoolVar(&backupOptions.WithAtime, "with-atime", false, "store the atime for all files and directories") - f.BoolVar(&backupOptions.IgnoreInode, "ignore-inode", false, "ignore inode number and ctime changes when checking for modified files") - f.BoolVar(&backupOptions.IgnoreCtime, "ignore-ctime", false, "ignore ctime changes when checking for modified files") - f.BoolVarP(&backupOptions.DryRun, "dry-run", "n", false, "do not upload or write any data, just show what would be done") - f.BoolVar(&backupOptions.NoScan, "no-scan", false, "do not run scanner to estimate size of backup") - if runtime.GOOS == "windows" { - f.BoolVar(&backupOptions.UseFsSnapshot, "use-fs-snapshot", false, "use filesystem snapshot where possible (currently only Windows VSS)") - f.BoolVar(&backupOptions.ExcludeCloudFiles, "exclude-cloud-files", false, "excludes online-only cloud files (such as OneDrive Files On-Demand)") - } - f.BoolVar(&backupOptions.SkipIfUnchanged, "skip-if-unchanged", false, "skip snapshot creation if identical to parent snapshot") - - // parse read concurrency from env, on error the default value will be used - readConcurrency, _ := strconv.ParseUint(os.Getenv("RESTIC_READ_CONCURRENCY"), 10, 32) - backupOptions.ReadConcurrency = uint(readConcurrency) - - // parse host from env, if not exists or empty the default value will be used - if host := os.Getenv("RESTIC_HOST"); host != "" { - backupOptions.Host = host - } + backupOptions.AddFlags(cmdBackup.Flags()) } // filterExisting returns a slice of all existing items, or an error if no diff --git a/cmd/restic/cmd_cache.go b/cmd/restic/cmd_cache.go index cd970b699..009026a9f 100644 --- a/cmd/restic/cmd_cache.go +++ b/cmd/restic/cmd_cache.go @@ -13,6 +13,7 @@ import ( "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/table" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdCache = &cobra.Command{ @@ -41,15 +42,17 @@ type CacheOptions struct { NoSize bool } +func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.Cleanup, "cleanup", false, "remove old cache directories") + f.UintVar(&opts.MaxAge, "max-age", 30, "max age in `days` for cache directories to be considered old") + f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories") +} + var cacheOptions CacheOptions func init() { cmdRoot.AddCommand(cmdCache) - - f := cmdCache.Flags() - f.BoolVar(&cacheOptions.Cleanup, "cleanup", false, "remove old cache directories") - f.UintVar(&cacheOptions.MaxAge, "max-age", 30, "max age in `days` for cache directories to be considered old") - f.BoolVar(&cacheOptions.NoSize, "no-size", false, "do not output the size of the cache directories") + cacheOptions.AddFlags(cmdCache.Flags()) } func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_check.go b/cmd/restic/cmd_check.go index 99e933af2..2ec4385c8 100644 --- a/cmd/restic/cmd_check.go +++ b/cmd/restic/cmd_check.go @@ -11,6 +11,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/restic/restic/internal/backend/cache" "github.com/restic/restic/internal/checker" @@ -68,14 +69,9 @@ type CheckOptions struct { WithCache bool } -var checkOptions CheckOptions - -func init() { - cmdRoot.AddCommand(cmdCheck) - - f := cmdCheck.Flags() - f.BoolVar(&checkOptions.ReadData, "read-data", false, "read all data blobs") - f.StringVar(&checkOptions.ReadDataSubset, "read-data-subset", "", "read a `subset` of data packs, specified as 'n/t' for specific part, or either 'x%' or 'x.y%' or a size in bytes with suffixes k/K, m/M, g/G, t/T for a random subset") +func (opts *CheckOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.ReadData, "read-data", false, "read all data blobs") + f.StringVar(&opts.ReadDataSubset, "read-data-subset", "", "read a `subset` of data packs, specified as 'n/t' for specific part, or either 'x%' or 'x.y%' or a size in bytes with suffixes k/K, m/M, g/G, t/T for a random subset") var ignored bool f.BoolVar(&ignored, "check-unused", false, "find unused blobs") err := f.MarkDeprecated("check-unused", "`--check-unused` is deprecated and will be ignored") @@ -83,7 +79,14 @@ func init() { // MarkDeprecated only returns an error when the flag is not found panic(err) } - f.BoolVar(&checkOptions.WithCache, "with-cache", false, "use existing cache, only read uncached data from repository") + f.BoolVar(&opts.WithCache, "with-cache", false, "use existing cache, only read uncached data from repository") +} + +var checkOptions CheckOptions + +func init() { + cmdRoot.AddCommand(cmdCheck) + checkOptions.AddFlags(cmdCheck.Flags()) } func checkFlags(opts CheckOptions) error { diff --git a/cmd/restic/cmd_copy.go b/cmd/restic/cmd_copy.go index 301e0e180..5d43b71f0 100644 --- a/cmd/restic/cmd_copy.go +++ b/cmd/restic/cmd_copy.go @@ -11,6 +11,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdCopy = &cobra.Command{ @@ -53,14 +54,16 @@ type CopyOptions struct { restic.SnapshotFilter } +func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) { + initSecondaryRepoOptions(f, &opts.secondaryRepoOptions, "destination", "to copy snapshots from") + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) +} + var copyOptions CopyOptions func init() { cmdRoot.AddCommand(cmdCopy) - - f := cmdCopy.Flags() - initSecondaryRepoOptions(f, ©Options.secondaryRepoOptions, "destination", "to copy snapshots from") - initMultiSnapshotFilter(f, ©Options.SnapshotFilter, true) + copyOptions.AddFlags(cmdCopy.Flags()) } func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_debug.go b/cmd/restic/cmd_debug.go index 4ce17f899..89e2bebd4 100644 --- a/cmd/restic/cmd_debug.go +++ b/cmd/restic/cmd_debug.go @@ -18,6 +18,7 @@ import ( "github.com/klauspost/compress/zstd" "github.com/spf13/cobra" + "github.com/spf13/pflag" "golang.org/x/sync/errgroup" "github.com/restic/restic/internal/crypto" @@ -64,16 +65,20 @@ type DebugExamineOptions struct { ReuploadBlobs bool } +func (opts *DebugExamineOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.ExtractPack, "extract-pack", false, "write blobs to the current directory") + f.BoolVar(&opts.ReuploadBlobs, "reupload-blobs", false, "reupload blobs to the repository") + f.BoolVar(&opts.TryRepair, "try-repair", false, "try to repair broken blobs with single bit flips") + f.BoolVar(&opts.RepairByte, "repair-byte", false, "try to repair broken blobs by trying bytes") +} + var debugExamineOpts DebugExamineOptions func init() { cmdRoot.AddCommand(cmdDebug) cmdDebug.AddCommand(cmdDebugDump) cmdDebug.AddCommand(cmdDebugExamine) - cmdDebugExamine.Flags().BoolVar(&debugExamineOpts.ExtractPack, "extract-pack", false, "write blobs to the current directory") - cmdDebugExamine.Flags().BoolVar(&debugExamineOpts.ReuploadBlobs, "reupload-blobs", false, "reupload blobs to the repository") - cmdDebugExamine.Flags().BoolVar(&debugExamineOpts.TryRepair, "try-repair", false, "try to repair broken blobs with single bit flips") - cmdDebugExamine.Flags().BoolVar(&debugExamineOpts.RepairByte, "repair-byte", false, "try to repair broken blobs by trying bytes") + debugExamineOpts.AddFlags(cmdDebugExamine.Flags()) } func prettyPrintJSON(wr io.Writer, item interface{}) error { diff --git a/cmd/restic/cmd_diff.go b/cmd/restic/cmd_diff.go index d1067b5ec..2b5bc67c6 100644 --- a/cmd/restic/cmd_diff.go +++ b/cmd/restic/cmd_diff.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdDiff = &cobra.Command{ @@ -57,13 +58,15 @@ type DiffOptions struct { ShowMetadata bool } +func (opts *DiffOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata") +} + var diffOptions DiffOptions func init() { cmdRoot.AddCommand(cmdDiff) - - f := cmdDiff.Flags() - f.BoolVar(&diffOptions.ShowMetadata, "metadata", false, "print changes in metadata") + diffOptions.AddFlags(cmdDiff.Flags()) } func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*restic.Snapshot, string, error) { diff --git a/cmd/restic/cmd_dump.go b/cmd/restic/cmd_dump.go index 6b7f8d012..4f4a44f9c 100644 --- a/cmd/restic/cmd_dump.go +++ b/cmd/restic/cmd_dump.go @@ -13,6 +13,7 @@ import ( "github.com/restic/restic/internal/restic" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdDump = &cobra.Command{ @@ -54,15 +55,17 @@ type DumpOptions struct { Target string } +func (opts *DumpOptions) AddFlags(f *pflag.FlagSet) { + initSingleSnapshotFilter(f, &opts.SnapshotFilter) + f.StringVarP(&opts.Archive, "archive", "a", "tar", "set archive `format` as \"tar\" or \"zip\"") + f.StringVarP(&opts.Target, "target", "t", "", "write the output to target `path`") +} + var dumpOptions DumpOptions func init() { cmdRoot.AddCommand(cmdDump) - - flags := cmdDump.Flags() - initSingleSnapshotFilter(flags, &dumpOptions.SnapshotFilter) - flags.StringVarP(&dumpOptions.Archive, "archive", "a", "tar", "set archive `format` as \"tar\" or \"zip\"") - flags.StringVarP(&dumpOptions.Target, "target", "t", "", "write the output to target `path`") + dumpOptions.AddFlags(cmdDump.Flags()) } func splitPath(p string) []string { diff --git a/cmd/restic/cmd_find.go b/cmd/restic/cmd_find.go index 2fcef5741..04b16ef8b 100644 --- a/cmd/restic/cmd_find.go +++ b/cmd/restic/cmd_find.go @@ -8,6 +8,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" @@ -62,25 +63,27 @@ type FindOptions struct { restic.SnapshotFilter } +func (opts *FindOptions) AddFlags(f *pflag.FlagSet) { + f.StringVarP(&opts.Oldest, "oldest", "O", "", "oldest modification date/time") + f.StringVarP(&opts.Newest, "newest", "N", "", "newest modification date/time") + f.StringArrayVarP(&opts.Snapshots, "snapshot", "s", nil, "snapshot `id` to search in (can be given multiple times)") + f.BoolVar(&opts.BlobID, "blob", false, "pattern is a blob-ID") + f.BoolVar(&opts.TreeID, "tree", false, "pattern is a tree-ID") + f.BoolVar(&opts.PackID, "pack", false, "pattern is a pack-ID") + f.BoolVar(&opts.ShowPackID, "show-pack-id", false, "display the pack-ID the blobs belong to (with --blob or --tree)") + f.BoolVarP(&opts.CaseInsensitive, "ignore-case", "i", false, "ignore case for pattern") + f.BoolVarP(&opts.Reverse, "reverse", "R", false, "reverse sort order oldest to newest") + f.BoolVarP(&opts.ListLong, "long", "l", false, "use a long listing format showing size and mode") + f.BoolVar(&opts.HumanReadable, "human-readable", false, "print sizes in human readable format") + + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) +} + var findOptions FindOptions func init() { cmdRoot.AddCommand(cmdFind) - - f := cmdFind.Flags() - f.StringVarP(&findOptions.Oldest, "oldest", "O", "", "oldest modification date/time") - f.StringVarP(&findOptions.Newest, "newest", "N", "", "newest modification date/time") - f.StringArrayVarP(&findOptions.Snapshots, "snapshot", "s", nil, "snapshot `id` to search in (can be given multiple times)") - f.BoolVar(&findOptions.BlobID, "blob", false, "pattern is a blob-ID") - f.BoolVar(&findOptions.TreeID, "tree", false, "pattern is a tree-ID") - f.BoolVar(&findOptions.PackID, "pack", false, "pattern is a pack-ID") - f.BoolVar(&findOptions.ShowPackID, "show-pack-id", false, "display the pack-ID the blobs belong to (with --blob or --tree)") - f.BoolVarP(&findOptions.CaseInsensitive, "ignore-case", "i", false, "ignore case for pattern") - f.BoolVarP(&findOptions.Reverse, "reverse", "R", false, "reverse sort order oldest to newest") - f.BoolVarP(&findOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") - f.BoolVar(&findOptions.HumanReadable, "human-readable", false, "print sizes in human readable format") - - initMultiSnapshotFilter(f, &findOptions.SnapshotFilter, true) + findOptions.AddFlags(cmdFind.Flags()) } type findPattern struct { diff --git a/cmd/restic/cmd_forget.go b/cmd/restic/cmd_forget.go index f9ae85cd1..572caae42 100644 --- a/cmd/restic/cmd_forget.go +++ b/cmd/restic/cmd_forget.go @@ -11,6 +11,7 @@ import ( "github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdForget = &cobra.Command{ @@ -111,44 +112,46 @@ type ForgetOptions struct { Prune bool } -var forgetOptions ForgetOptions -var forgetPruneOptions PruneOptions +func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) { + f.VarP(&opts.Last, "keep-last", "l", "keep the last `n` snapshots (use 'unlimited' to keep all snapshots)") + f.VarP(&opts.Hourly, "keep-hourly", "H", "keep the last `n` hourly snapshots (use 'unlimited' to keep all hourly snapshots)") + f.VarP(&opts.Daily, "keep-daily", "d", "keep the last `n` daily snapshots (use 'unlimited' to keep all daily snapshots)") + f.VarP(&opts.Weekly, "keep-weekly", "w", "keep the last `n` weekly snapshots (use 'unlimited' to keep all weekly snapshots)") + f.VarP(&opts.Monthly, "keep-monthly", "m", "keep the last `n` monthly snapshots (use 'unlimited' to keep all monthly snapshots)") + f.VarP(&opts.Yearly, "keep-yearly", "y", "keep the last `n` yearly snapshots (use 'unlimited' to keep all yearly snapshots)") + f.VarP(&opts.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.VarP(&opts.WithinHourly, "keep-within-hourly", "", "keep hourly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.VarP(&opts.WithinDaily, "keep-within-daily", "", "keep daily snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.VarP(&opts.WithinWeekly, "keep-within-weekly", "", "keep weekly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.VarP(&opts.WithinMonthly, "keep-within-monthly", "", "keep monthly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.VarP(&opts.WithinYearly, "keep-within-yearly", "", "keep yearly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") + f.Var(&opts.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") + f.BoolVar(&opts.UnsafeAllowRemoveAll, "unsafe-allow-remove-all", false, "allow deleting all snapshots of a snapshot group") -func init() { - cmdRoot.AddCommand(cmdForget) - - f := cmdForget.Flags() - f.VarP(&forgetOptions.Last, "keep-last", "l", "keep the last `n` snapshots (use 'unlimited' to keep all snapshots)") - f.VarP(&forgetOptions.Hourly, "keep-hourly", "H", "keep the last `n` hourly snapshots (use 'unlimited' to keep all hourly snapshots)") - f.VarP(&forgetOptions.Daily, "keep-daily", "d", "keep the last `n` daily snapshots (use 'unlimited' to keep all daily snapshots)") - f.VarP(&forgetOptions.Weekly, "keep-weekly", "w", "keep the last `n` weekly snapshots (use 'unlimited' to keep all weekly snapshots)") - f.VarP(&forgetOptions.Monthly, "keep-monthly", "m", "keep the last `n` monthly snapshots (use 'unlimited' to keep all monthly snapshots)") - f.VarP(&forgetOptions.Yearly, "keep-yearly", "y", "keep the last `n` yearly snapshots (use 'unlimited' to keep all yearly snapshots)") - f.VarP(&forgetOptions.Within, "keep-within", "", "keep snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.VarP(&forgetOptions.WithinHourly, "keep-within-hourly", "", "keep hourly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.VarP(&forgetOptions.WithinDaily, "keep-within-daily", "", "keep daily snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.VarP(&forgetOptions.WithinWeekly, "keep-within-weekly", "", "keep weekly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.VarP(&forgetOptions.WithinMonthly, "keep-within-monthly", "", "keep monthly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.VarP(&forgetOptions.WithinYearly, "keep-within-yearly", "", "keep yearly snapshots that are newer than `duration` (eg. 1y5m7d2h) relative to the latest snapshot") - f.Var(&forgetOptions.KeepTags, "keep-tag", "keep snapshots with this `taglist` (can be specified multiple times)") - f.BoolVar(&forgetOptions.UnsafeAllowRemoveAll, "unsafe-allow-remove-all", false, "allow deleting all snapshots of a snapshot group") - - initMultiSnapshotFilter(f, &forgetOptions.SnapshotFilter, false) - f.StringArrayVar(&forgetOptions.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)") + initMultiSnapshotFilter(f, &opts.SnapshotFilter, false) + f.StringArrayVar(&opts.Hosts, "hostname", nil, "only consider snapshots with the given `hostname` (can be specified multiple times)") err := f.MarkDeprecated("hostname", "use --host") if err != nil { // MarkDeprecated only returns an error when the flag is not found panic(err) } - f.BoolVarP(&forgetOptions.Compact, "compact", "c", false, "use compact output format") - forgetOptions.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} - f.VarP(&forgetOptions.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") - 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") + f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format") + opts.GroupBy = restic.SnapshotGroupByOptions{Host: true, Path: true} + f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma (disable grouping with '')") + f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not delete anything, just print what would be done") + f.BoolVar(&opts.Prune, "prune", false, "automatically run the 'prune' command if snapshots have been removed") f.SortFlags = false - addPruneOptions(cmdForget, &forgetPruneOptions) +} + +var forgetOptions ForgetOptions +var forgetPruneOptions PruneOptions + +func init() { + cmdRoot.AddCommand(cmdForget) + forgetOptions.AddFlags(cmdForget.Flags()) + forgetPruneOptions.AddLimitedFlags(cmdForget.Flags()) } func verifyForgetOptions(opts *ForgetOptions) error { diff --git a/cmd/restic/cmd_generate.go b/cmd/restic/cmd_generate.go index 66b3fa7c5..37d9724e0 100644 --- a/cmd/restic/cmd_generate.go +++ b/cmd/restic/cmd_generate.go @@ -8,6 +8,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" + "github.com/spf13/pflag" ) var cmdGenerate = &cobra.Command{ @@ -37,16 +38,19 @@ type generateOptions struct { PowerShellCompletionFile string } +func (opts *generateOptions) AddFlags(f *pflag.FlagSet) { + f.StringVar(&opts.ManDir, "man", "", "write man pages to `directory`") + f.StringVar(&opts.BashCompletionFile, "bash-completion", "", "write bash completion `file` (`-` for stdout)") + f.StringVar(&opts.FishCompletionFile, "fish-completion", "", "write fish completion `file` (`-` for stdout)") + f.StringVar(&opts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file` (`-` for stdout)") + f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)") +} + var genOpts generateOptions func init() { cmdRoot.AddCommand(cmdGenerate) - fs := cmdGenerate.Flags() - fs.StringVar(&genOpts.ManDir, "man", "", "write man pages to `directory`") - fs.StringVar(&genOpts.BashCompletionFile, "bash-completion", "", "write bash completion `file` (`-` for stdout)") - fs.StringVar(&genOpts.FishCompletionFile, "fish-completion", "", "write fish completion `file` (`-` for stdout)") - fs.StringVar(&genOpts.ZSHCompletionFile, "zsh-completion", "", "write zsh completion `file` (`-` for stdout)") - fs.StringVar(&genOpts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)") + genOpts.AddFlags(cmdGenerate.Flags()) } func writeManpages(dir string) error { diff --git a/cmd/restic/cmd_init.go b/cmd/restic/cmd_init.go index 2a2aae1dc..f3f22d1d7 100644 --- a/cmd/restic/cmd_init.go +++ b/cmd/restic/cmd_init.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/restic" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdInit = &cobra.Command{ @@ -40,15 +41,17 @@ type InitOptions struct { RepositoryVersion string } +func (opts *InitOptions) AddFlags(f *pflag.FlagSet) { + initSecondaryRepoOptions(f, &opts.secondaryRepoOptions, "secondary", "to copy chunker parameters from") + f.BoolVar(&opts.CopyChunkerParameters, "copy-chunker-params", false, "copy chunker parameters from the secondary repository (useful with the copy command)") + f.StringVar(&opts.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'") +} + var initOptions InitOptions func init() { cmdRoot.AddCommand(cmdInit) - - f := cmdInit.Flags() - initSecondaryRepoOptions(f, &initOptions.secondaryRepoOptions, "secondary", "to copy chunker parameters from") - f.BoolVar(&initOptions.CopyChunkerParameters, "copy-chunker-params", false, "copy chunker parameters from the secondary repository (useful with the copy command)") - f.StringVar(&initOptions.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'") + initOptions.AddFlags(cmdInit.Flags()) } func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_ls.go b/cmd/restic/cmd_ls.go index 373a31a40..7fda0b043 100644 --- a/cmd/restic/cmd_ls.go +++ b/cmd/restic/cmd_ls.go @@ -13,6 +13,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/fs" @@ -70,19 +71,21 @@ type LsOptions struct { Reverse bool } +func (opts *LsOptions) AddFlags(f *pflag.FlagSet) { + initSingleSnapshotFilter(f, &opts.SnapshotFilter) + f.BoolVarP(&opts.ListLong, "long", "l", false, "use a long listing format showing size and mode") + f.BoolVar(&opts.Recursive, "recursive", false, "include files in subfolders of the listed directories") + f.BoolVar(&opts.HumanReadable, "human-readable", false, "print sizes in human readable format") + f.BoolVar(&opts.Ncdu, "ncdu", false, "output NCDU export format (pipe into 'ncdu -f -')") + f.VarP(&opts.Sort, "sort", "s", "sort output by (name|size|time=mtime|atime|ctime|extension)") + f.BoolVar(&opts.Reverse, "reverse", false, "reverse sorted output") +} + var lsOptions LsOptions func init() { cmdRoot.AddCommand(cmdLs) - - flags := cmdLs.Flags() - initSingleSnapshotFilter(flags, &lsOptions.SnapshotFilter) - flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode") - flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories") - flags.BoolVar(&lsOptions.HumanReadable, "human-readable", false, "print sizes in human readable format") - flags.BoolVar(&lsOptions.Ncdu, "ncdu", false, "output NCDU export format (pipe into 'ncdu -f -')") - flags.VarP(&lsOptions.Sort, "sort", "s", "sort output by (name|size|time=mtime|atime|ctime|extension)") - flags.BoolVar(&lsOptions.Reverse, "reverse", false, "reverse sorted output") + lsOptions.AddFlags(cmdLs.Flags()) } type lsPrinter interface { diff --git a/cmd/restic/cmd_migrate.go b/cmd/restic/cmd_migrate.go index f6c28e383..4a5ecf84b 100644 --- a/cmd/restic/cmd_migrate.go +++ b/cmd/restic/cmd_migrate.go @@ -9,6 +9,7 @@ import ( "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdMigrate = &cobra.Command{ @@ -42,12 +43,15 @@ type MigrateOptions struct { Force bool } +func (opts *MigrateOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVarP(&opts.Force, "force", "f", false, `apply a migration a second time`) +} + var migrateOptions MigrateOptions func init() { cmdRoot.AddCommand(cmdMigrate) - f := cmdMigrate.Flags() - f.BoolVarP(&migrateOptions.Force, "force", "f", false, `apply a migration a second time`) + migrateOptions.AddFlags(cmdMigrate.Flags()) } func checkMigrations(ctx context.Context, repo restic.Repository, printer progress.Printer) error { diff --git a/cmd/restic/cmd_mount.go b/cmd/restic/cmd_mount.go index b8a66dc90..21c9d62f4 100644 --- a/cmd/restic/cmd_mount.go +++ b/cmd/restic/cmd_mount.go @@ -10,6 +10,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" @@ -86,22 +87,24 @@ type MountOptions struct { PathTemplates []string } +func (opts *MountOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs") + f.BoolVar(&opts.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory") + f.BoolVar(&opts.NoDefaultPermissions, "no-default-permissions", false, "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files") + + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) + + f.StringArrayVar(&opts.PathTemplates, "path-template", nil, "set `template` for path names (can be specified multiple times)") + f.StringVar(&opts.TimeTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs") + f.StringVar(&opts.TimeTemplate, "time-template", time.RFC3339, "set `template` to use for times") + _ = f.MarkDeprecated("snapshot-template", "use --time-template") +} + var mountOptions MountOptions func init() { cmdRoot.AddCommand(cmdMount) - - mountFlags := cmdMount.Flags() - mountFlags.BoolVar(&mountOptions.OwnerRoot, "owner-root", false, "use 'root' as the owner of files and dirs") - mountFlags.BoolVar(&mountOptions.AllowOther, "allow-other", false, "allow other users to access the data in the mounted directory") - mountFlags.BoolVar(&mountOptions.NoDefaultPermissions, "no-default-permissions", false, "for 'allow-other', ignore Unix permissions and allow users to read all snapshot files") - - initMultiSnapshotFilter(mountFlags, &mountOptions.SnapshotFilter, true) - - mountFlags.StringArrayVar(&mountOptions.PathTemplates, "path-template", nil, "set `template` for path names (can be specified multiple times)") - mountFlags.StringVar(&mountOptions.TimeTemplate, "snapshot-template", time.RFC3339, "set `template` to use for snapshot dirs") - mountFlags.StringVar(&mountOptions.TimeTemplate, "time-template", time.RFC3339, "set `template` to use for times") - _ = mountFlags.MarkDeprecated("snapshot-template", "use --time-template") + mountOptions.AddFlags(cmdMount.Flags()) } func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_prune.go b/cmd/restic/cmd_prune.go index fce109bdd..99b69c093 100644 --- a/cmd/restic/cmd_prune.go +++ b/cmd/restic/cmd_prune.go @@ -16,6 +16,7 @@ import ( "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdPrune = &cobra.Command{ @@ -61,23 +62,25 @@ type PruneOptions struct { RepackUncompressed bool } +func (opts *PruneOptions) AddFlags(f *pflag.FlagSet) { + opts.AddLimitedFlags(f) + f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not modify the repository, just print what would be done") + f.StringVarP(&opts.UnsafeNoSpaceRecovery, "unsafe-recover-no-free-space", "", "", "UNSAFE, READ THE DOCUMENTATION BEFORE USING! Try to recover a repository stuck with no free space. Do not use without trying out 'prune --max-repack-size 0' first.") +} + +func (opts *PruneOptions) AddLimitedFlags(f *pflag.FlagSet) { + f.StringVar(&opts.MaxUnused, "max-unused", "5%", "tolerate given `limit` of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')") + f.StringVar(&opts.MaxRepackSize, "max-repack-size", "", "stop after repacking this much data in total (allowed suffixes for `size`: k/K, m/M, g/G, t/T)") + f.BoolVar(&opts.RepackCacheableOnly, "repack-cacheable-only", false, "only repack packs which are cacheable") + f.BoolVar(&opts.RepackSmall, "repack-small", false, "repack pack files below 80% of target pack size") + f.BoolVar(&opts.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data") +} + var pruneOptions PruneOptions func init() { cmdRoot.AddCommand(cmdPrune) - f := cmdPrune.Flags() - f.BoolVarP(&pruneOptions.DryRun, "dry-run", "n", false, "do not modify the repository, just print what would be done") - f.StringVarP(&pruneOptions.UnsafeNoSpaceRecovery, "unsafe-recover-no-free-space", "", "", "UNSAFE, READ THE DOCUMENTATION BEFORE USING! Try to recover a repository stuck with no free space. Do not use without trying out 'prune --max-repack-size 0' first.") - addPruneOptions(cmdPrune, &pruneOptions) -} - -func addPruneOptions(c *cobra.Command, pruneOptions *PruneOptions) { - f := c.Flags() - f.StringVar(&pruneOptions.MaxUnused, "max-unused", "5%", "tolerate given `limit` of unused data (absolute value in bytes with suffixes k/K, m/M, g/G, t/T, a value in % or the word 'unlimited')") - f.StringVar(&pruneOptions.MaxRepackSize, "max-repack-size", "", "stop after repacking this much data in total (allowed suffixes for `size`: k/K, m/M, g/G, t/T)") - f.BoolVar(&pruneOptions.RepackCacheableOnly, "repack-cacheable-only", false, "only repack packs which are cacheable") - f.BoolVar(&pruneOptions.RepackSmall, "repack-small", false, "repack pack files below 80% of target pack size") - f.BoolVar(&pruneOptions.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data") + pruneOptions.AddFlags(cmdPrune.Flags()) } func verifyPruneOptions(opts *PruneOptions) error { diff --git a/cmd/restic/cmd_repair_index.go b/cmd/restic/cmd_repair_index.go index 83c1bfa7f..46ec1db98 100644 --- a/cmd/restic/cmd_repair_index.go +++ b/cmd/restic/cmd_repair_index.go @@ -47,16 +47,18 @@ type RepairIndexOptions struct { ReadAllPacks bool } +func (opts *RepairIndexOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch") +} + var repairIndexOptions RepairIndexOptions func init() { cmdRepair.AddCommand(cmdRepairIndex) + repairIndexOptions.AddFlags(cmdRepairIndex.Flags()) // add alias for old name cmdRoot.AddCommand(cmdRebuildIndex) - - for _, f := range []*pflag.FlagSet{cmdRepairIndex.Flags(), cmdRebuildIndex.Flags()} { - f.BoolVar(&repairIndexOptions.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch") - } + repairIndexOptions.AddFlags(cmdRebuildIndex.Flags()) } func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error { diff --git a/cmd/restic/cmd_repair_snapshots.go b/cmd/restic/cmd_repair_snapshots.go index 34c02b3ff..397f8a600 100644 --- a/cmd/restic/cmd_repair_snapshots.go +++ b/cmd/restic/cmd_repair_snapshots.go @@ -8,6 +8,7 @@ import ( "github.com/restic/restic/internal/walker" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdRepairSnapshots = &cobra.Command{ @@ -57,16 +58,18 @@ type RepairOptions struct { restic.SnapshotFilter } +func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done") + f.BoolVarP(&opts.Forget, "forget", "", false, "remove original snapshots after creating new ones") + + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) +} + var repairSnapshotOptions RepairOptions func init() { cmdRepair.AddCommand(cmdRepairSnapshots) - flags := cmdRepairSnapshots.Flags() - - flags.BoolVarP(&repairSnapshotOptions.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done") - flags.BoolVarP(&repairSnapshotOptions.Forget, "forget", "", false, "remove original snapshots after creating new ones") - - initMultiSnapshotFilter(flags, &repairSnapshotOptions.SnapshotFilter, true) + repairSnapshotOptions.AddFlags(cmdRepairSnapshots.Flags()) } func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error { diff --git a/cmd/restic/cmd_restore.go b/cmd/restic/cmd_restore.go index c930abc31..0382b859f 100644 --- a/cmd/restic/cmd_restore.go +++ b/cmd/restic/cmd_restore.go @@ -15,6 +15,7 @@ import ( "github.com/restic/restic/internal/ui/termstatus" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdRestore = &cobra.Command{ @@ -63,26 +64,28 @@ type RestoreOptions struct { IncludeXattrPattern []string } +func (opts *RestoreOptions) AddFlags(f *pflag.FlagSet) { + f.StringVarP(&opts.Target, "target", "t", "", "directory to extract data to") + + opts.ExcludePatternOptions.Add(f) + opts.IncludePatternOptions.Add(f) + + f.StringArrayVar(&opts.ExcludeXattrPattern, "exclude-xattr", nil, "exclude xattr by `pattern` (can be specified multiple times)") + f.StringArrayVar(&opts.IncludeXattrPattern, "include-xattr", nil, "include xattr by `pattern` (can be specified multiple times)") + + initSingleSnapshotFilter(f, &opts.SnapshotFilter) + f.BoolVar(&opts.DryRun, "dry-run", false, "do not write any data, just show what would be done") + f.BoolVar(&opts.Sparse, "sparse", false, "restore files as sparse") + f.BoolVar(&opts.Verify, "verify", false, "verify restored files content") + f.Var(&opts.Overwrite, "overwrite", "overwrite behavior, one of (always|if-changed|if-newer|never) (default: always)") + f.BoolVar(&opts.Delete, "delete", false, "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted") +} + var restoreOptions RestoreOptions func init() { cmdRoot.AddCommand(cmdRestore) - - flags := cmdRestore.Flags() - flags.StringVarP(&restoreOptions.Target, "target", "t", "", "directory to extract data to") - - restoreOptions.ExcludePatternOptions.Add(flags) - restoreOptions.IncludePatternOptions.Add(flags) - - flags.StringArrayVar(&restoreOptions.ExcludeXattrPattern, "exclude-xattr", nil, "exclude xattr by `pattern` (can be specified multiple times)") - flags.StringArrayVar(&restoreOptions.IncludeXattrPattern, "include-xattr", nil, "include xattr by `pattern` (can be specified multiple times)") - - initSingleSnapshotFilter(flags, &restoreOptions.SnapshotFilter) - flags.BoolVar(&restoreOptions.DryRun, "dry-run", false, "do not write any data, just show what would be done") - flags.BoolVar(&restoreOptions.Sparse, "sparse", false, "restore files as sparse") - flags.BoolVar(&restoreOptions.Verify, "verify", false, "verify restored files content") - flags.Var(&restoreOptions.Overwrite, "overwrite", "overwrite behavior, one of (always|if-changed|if-newer|never) (default: always)") - flags.BoolVar(&restoreOptions.Delete, "delete", false, "delete files from target directory if they do not exist in snapshot. Use '--dry-run -vv' to check what would be deleted") + restoreOptions.AddFlags(cmdRestore.Flags()) } func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, diff --git a/cmd/restic/cmd_rewrite.go b/cmd/restic/cmd_rewrite.go index f847aa372..3200a1e52 100644 --- a/cmd/restic/cmd_rewrite.go +++ b/cmd/restic/cmd_rewrite.go @@ -5,6 +5,7 @@ import ( "time" "github.com/spf13/cobra" + "github.com/spf13/pflag" "golang.org/x/sync/errgroup" "github.com/restic/restic/internal/debug" @@ -99,20 +100,22 @@ type RewriteOptions struct { filter.ExcludePatternOptions } +func (opts *RewriteOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVarP(&opts.Forget, "forget", "", false, "remove original snapshots after creating new ones") + f.BoolVarP(&opts.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done") + f.StringVar(&opts.Metadata.Hostname, "new-host", "", "replace hostname") + f.StringVar(&opts.Metadata.Time, "new-time", "", "replace time of the backup") + f.BoolVarP(&opts.SnapshotSummary, "snapshot-summary", "s", false, "create snapshot summary record if it does not exist") + + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) + opts.ExcludePatternOptions.Add(f) +} + var rewriteOptions RewriteOptions func init() { cmdRoot.AddCommand(cmdRewrite) - - f := cmdRewrite.Flags() - f.BoolVarP(&rewriteOptions.Forget, "forget", "", false, "remove original snapshots after creating new ones") - f.BoolVarP(&rewriteOptions.DryRun, "dry-run", "n", false, "do not do anything, just print what would be done") - f.StringVar(&rewriteOptions.Metadata.Hostname, "new-host", "", "replace hostname") - f.StringVar(&rewriteOptions.Metadata.Time, "new-time", "", "replace time of the backup") - f.BoolVarP(&rewriteOptions.SnapshotSummary, "snapshot-summary", "s", false, "create snapshot summary record if it does not exist") - - initMultiSnapshotFilter(f, &rewriteOptions.SnapshotFilter, true) - rewriteOptions.ExcludePatternOptions.Add(f) + rewriteOptions.AddFlags(cmdRewrite.Flags()) } // rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will diff --git a/cmd/restic/cmd_self_update.go b/cmd/restic/cmd_self_update.go index 09c86bf2c..3e218a408 100644 --- a/cmd/restic/cmd_self_update.go +++ b/cmd/restic/cmd_self_update.go @@ -10,6 +10,7 @@ import ( "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/selfupdate" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdSelfUpdate = &cobra.Command{ @@ -41,13 +42,15 @@ type SelfUpdateOptions struct { Output string } +func (opts *SelfUpdateOptions) AddFlags(f *pflag.FlagSet) { + f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)") +} + var selfUpdateOptions SelfUpdateOptions func init() { cmdRoot.AddCommand(cmdSelfUpdate) - - flags := cmdSelfUpdate.Flags() - flags.StringVar(&selfUpdateOptions.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)") + selfUpdateOptions.AddFlags(cmdSelfUpdate.Flags()) } func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_snapshots.go b/cmd/restic/cmd_snapshots.go index f935cec86..b426484d3 100644 --- a/cmd/restic/cmd_snapshots.go +++ b/cmd/restic/cmd_snapshots.go @@ -12,6 +12,7 @@ import ( "github.com/restic/restic/internal/ui" "github.com/restic/restic/internal/ui/table" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdSnapshots = &cobra.Command{ @@ -45,22 +46,24 @@ type SnapshotOptions struct { GroupBy restic.SnapshotGroupByOptions } -var snapshotOptions SnapshotOptions - -func init() { - cmdRoot.AddCommand(cmdSnapshots) - - f := cmdSnapshots.Flags() - initMultiSnapshotFilter(f, &snapshotOptions.SnapshotFilter, true) - f.BoolVarP(&snapshotOptions.Compact, "compact", "c", false, "use compact output format") - f.BoolVar(&snapshotOptions.Last, "last", false, "only show the last snapshot for each host and path") +func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) { + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) + f.BoolVarP(&opts.Compact, "compact", "c", false, "use compact output format") + f.BoolVar(&opts.Last, "last", false, "only show the last snapshot for each host and path") err := f.MarkDeprecated("last", "use --latest 1") if err != nil { // MarkDeprecated only returns an error when the flag is not found panic(err) } - f.IntVar(&snapshotOptions.Latest, "latest", 0, "only show the last `n` snapshots for each host and path") - f.VarP(&snapshotOptions.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma") + f.IntVar(&opts.Latest, "latest", 0, "only show the last `n` snapshots for each host and path") + f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma") +} + +var snapshotOptions SnapshotOptions + +func init() { + cmdRoot.AddCommand(cmdSnapshots) + snapshotOptions.AddFlags(cmdSnapshots.Flags()) } func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_stats.go b/cmd/restic/cmd_stats.go index e0b60a29e..c3512c2d5 100644 --- a/cmd/restic/cmd_stats.go +++ b/cmd/restic/cmd_stats.go @@ -18,6 +18,7 @@ import ( "github.com/restic/restic/internal/walker" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var cmdStats = &cobra.Command{ @@ -70,6 +71,11 @@ type StatsOptions struct { restic.SnapshotFilter } +func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) { + f.StringVar(&opts.countMode, "mode", countModeRestoreSize, "counting mode: restore-size (default), files-by-contents, blobs-per-file or raw-data") + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) +} + var statsOptions StatsOptions func must(err error) { @@ -80,13 +86,10 @@ func must(err error) { func init() { cmdRoot.AddCommand(cmdStats) - f := cmdStats.Flags() - f.StringVar(&statsOptions.countMode, "mode", countModeRestoreSize, "counting mode: restore-size (default), files-by-contents, blobs-per-file or raw-data") + statsOptions.AddFlags(cmdStats.Flags()) must(cmdStats.RegisterFlagCompletionFunc("mode", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { return []string{countModeRestoreSize, countModeUniqueFilesByContents, countModeBlobsPerFile, countModeRawData}, cobra.ShellCompDirectiveDefault })) - - initMultiSnapshotFilter(f, &statsOptions.SnapshotFilter, true) } func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string) error { diff --git a/cmd/restic/cmd_tag.go b/cmd/restic/cmd_tag.go index f71e2556c..ab90cbd4f 100644 --- a/cmd/restic/cmd_tag.go +++ b/cmd/restic/cmd_tag.go @@ -4,6 +4,7 @@ import ( "context" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/restic/restic/internal/debug" "github.com/restic/restic/internal/errors" @@ -50,16 +51,18 @@ type TagOptions struct { RemoveTags restic.TagLists } +func (opts *TagOptions) AddFlags(f *pflag.FlagSet) { + f.Var(&opts.SetTags, "set", "`tags` which will replace the existing tags in the format `tag[,tag,...]` (can be given multiple times)") + f.Var(&opts.AddTags, "add", "`tags` which will be added to the existing tags in the format `tag[,tag,...]` (can be given multiple times)") + f.Var(&opts.RemoveTags, "remove", "`tags` which will be removed from the existing tags in the format `tag[,tag,...]` (can be given multiple times)") + initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) +} + var tagOptions TagOptions func init() { cmdRoot.AddCommand(cmdTag) - - tagFlags := cmdTag.Flags() - tagFlags.Var(&tagOptions.SetTags, "set", "`tags` which will replace the existing tags in the format `tag[,tag,...]` (can be given multiple times)") - tagFlags.Var(&tagOptions.AddTags, "add", "`tags` which will be added to the existing tags in the format `tag[,tag,...]` (can be given multiple times)") - tagFlags.Var(&tagOptions.RemoveTags, "remove", "`tags` which will be removed from the existing tags in the format `tag[,tag,...]` (can be given multiple times)") - initMultiSnapshotFilter(tagFlags, &tagOptions.SnapshotFilter, true) + tagOptions.AddFlags(cmdTag.Flags()) } type changedSnapshot struct { diff --git a/cmd/restic/cmd_unlock.go b/cmd/restic/cmd_unlock.go index 825eb815c..03066451c 100644 --- a/cmd/restic/cmd_unlock.go +++ b/cmd/restic/cmd_unlock.go @@ -5,6 +5,7 @@ import ( "github.com/restic/restic/internal/repository" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var unlockCmd = &cobra.Command{ @@ -31,12 +32,15 @@ type UnlockOptions struct { RemoveAll bool } +func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) { + f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones") +} + var unlockOptions UnlockOptions func init() { cmdRoot.AddCommand(unlockCmd) - - unlockCmd.Flags().BoolVar(&unlockOptions.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones") + unlockOptions.AddFlags(unlockCmd.Flags()) } func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) error {