refactor to use constructor functions to create cobra commands

This allows getting rid of the global options variables
This commit is contained in:
Michael Eischer 2025-02-06 23:02:00 +01:00
parent dc9b6378f3
commit aacd6a47e3
35 changed files with 755 additions and 602 deletions

View File

@ -31,10 +31,13 @@ import (
"github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/ui/termstatus"
) )
var cmdBackup = &cobra.Command{ func newBackupCommand() *cobra.Command {
Use: "backup [flags] [FILE/DIR] ...", var opts BackupOptions
Short: "Create a new backup of files and/or directories",
Long: ` cmd := &cobra.Command{
Use: "backup [flags] [FILE/DIR] ...",
Short: "Create a new backup of files and/or directories",
Long: `
The "backup" command creates a new snapshot and saves the files and directories The "backup" command creates a new snapshot and saves the files and directories
given as the arguments. given as the arguments.
@ -48,23 +51,31 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
PreRun: func(_ *cobra.Command, _ []string) { PreRun: func(_ *cobra.Command, _ []string) {
if backupOptions.Host == "" { if opts.Host == "" {
hostname, err := os.Hostname() hostname, err := os.Hostname()
if err != nil { if err != nil {
debug.Log("os.Hostname() returned err: %v", err) debug.Log("os.Hostname() returned err: %v", err)
return return
}
opts.Host = hostname
} }
backupOptions.Host = hostname },
} GroupID: cmdGroupDefault,
}, DisableAutoGenTag: true,
GroupID: cmdGroupDefault, RunE: func(cmd *cobra.Command, args []string) error {
DisableAutoGenTag: true, term, cancel := setupTermstatus()
RunE: func(cmd *cobra.Command, args []string) error { defer cancel()
term, cancel := setupTermstatus() return runBackup(cmd.Context(), opts, globalOptions, term, args)
defer cancel() },
return runBackup(cmd.Context(), backupOptions, globalOptions, term, args) }
},
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newBackupCommand())
} }
// BackupOptions bundles all options for the backup command. // BackupOptions bundles all options for the backup command.
@ -147,17 +158,11 @@ func (opts *BackupOptions) AddFlags(f *pflag.FlagSet) {
} }
} }
var backupOptions BackupOptions
var backupFSTestHook func(fs fs.FS) fs.FS var backupFSTestHook func(fs fs.FS) fs.FS
// ErrInvalidSourceData is used to report an incomplete backup // ErrInvalidSourceData is used to report an incomplete backup
var ErrInvalidSourceData = errors.New("at least one source file could not be read") var ErrInvalidSourceData = errors.New("at least one source file could not be read")
func init() {
cmdRoot.AddCommand(cmdBackup)
backupOptions.AddFlags(cmdBackup.Flags())
}
// filterExisting returns a slice of all existing items, or an error if no // filterExisting returns a slice of all existing items, or an error if no
// items exist at all. // items exist at all.
func filterExisting(items []string) (result []string, err error) { func filterExisting(items []string) (result []string, err error) {

View File

@ -16,10 +16,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdCache = &cobra.Command{ func newCacheCommand() *cobra.Command {
Use: "cache", var opts CacheOptions
Short: "Operate on local cache directories",
Long: ` cmd := &cobra.Command{
Use: "cache",
Short: "Operate on local cache directories",
Long: `
The "cache" command allows listing and cleaning local cache directories. The "cache" command allows listing and cleaning local cache directories.
EXIT STATUS EXIT STATUS
@ -28,11 +31,19 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
return runCache(cacheOptions, globalOptions, args) return runCache(opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCacheCommand())
} }
// CacheOptions bundles all options for the snapshots command. // CacheOptions bundles all options for the snapshots command.
@ -48,13 +59,6 @@ func (opts *CacheOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories") f.BoolVar(&opts.NoSize, "no-size", false, "do not output the size of the cache directories")
} }
var cacheOptions CacheOptions
func init() {
cmdRoot.AddCommand(cmdCache)
cacheOptions.AddFlags(cmdCache.Flags())
}
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error { func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 { if len(args) > 0 {
return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags") return errors.Fatal("the cache command expects no arguments, only options - please see `restic help cache` for usage and flags")

View File

@ -14,10 +14,11 @@ import (
var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"} var catAllowedCmds = []string{"config", "index", "snapshot", "key", "masterkey", "lock", "pack", "blob", "tree"}
var cmdCat = &cobra.Command{ func newCatCommand() *cobra.Command {
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]", cmd := &cobra.Command{
Short: "Print internal objects to stdout", Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
Long: ` Short: "Print internal objects to stdout",
Long: `
The "cat" command is used to print internal objects to stdout. The "cat" command is used to print internal objects to stdout.
EXIT STATUS EXIT STATUS
@ -29,16 +30,18 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCat(cmd.Context(), globalOptions, args) return runCat(cmd.Context(), globalOptions, args)
}, },
ValidArgs: catAllowedCmds, ValidArgs: catAllowedCmds,
}
return cmd
} }
func init() { func init() {
cmdRoot.AddCommand(cmdCat) cmdRoot.AddCommand(newCatCommand())
} }
func validateCatArgs(args []string) error { func validateCatArgs(args []string) error {

View File

@ -23,10 +23,12 @@ import (
"github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/ui/termstatus"
) )
var cmdCheck = &cobra.Command{ func newCheckCommand() *cobra.Command {
Use: "check [flags]", var opts CheckOptions
Short: "Check the repository for errors", cmd := &cobra.Command{
Long: ` Use: "check [flags]",
Short: "Check the repository for errors",
Long: `
The "check" command tests the repository for errors and reports any errors it The "check" command tests the repository for errors and reports any errors it
finds. It can also be used to read all data and therefore simulate a restore. finds. It can also be used to read all data and therefore simulate a restore.
@ -42,23 +44,31 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
summary, err := runCheck(cmd.Context(), checkOptions, globalOptions, args, term) summary, err := runCheck(cmd.Context(), opts, globalOptions, args, term)
if globalOptions.JSON { if globalOptions.JSON {
if err != nil && summary.NumErrors == 0 { if err != nil && summary.NumErrors == 0 {
summary.NumErrors = 1 summary.NumErrors = 1
}
term.Print(ui.ToJSONString(summary))
} }
term.Print(ui.ToJSONString(summary)) return err
} },
return err PreRunE: func(_ *cobra.Command, _ []string) error {
}, return checkFlags(opts)
PreRunE: func(_ *cobra.Command, _ []string) error { },
return checkFlags(checkOptions) }
},
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCheckCommand())
} }
// CheckOptions bundles all options for the 'check' command. // CheckOptions bundles all options for the 'check' command.
@ -82,13 +92,6 @@ func (opts *CheckOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.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 { func checkFlags(opts CheckOptions) error {
if opts.ReadData && opts.ReadDataSubset != "" { if opts.ReadData && opts.ReadDataSubset != "" {
return errors.Fatal("check flags --read-data and --read-data-subset cannot be used together") return errors.Fatal("check flags --read-data and --read-data-subset cannot be used together")

View File

@ -14,10 +14,12 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdCopy = &cobra.Command{ func newCopyCommand() *cobra.Command {
Use: "copy [flags] [snapshotID ...]", var opts CopyOptions
Short: "Copy snapshots from one repository to another", cmd := &cobra.Command{
Long: ` Use: "copy [flags] [snapshotID ...]",
Short: "Copy snapshots from one repository to another",
Long: `
The "copy" command copies one or more snapshots from one repository to another. The "copy" command copies one or more snapshots from one repository to another.
NOTE: This process will have to both download (read) and upload (write) the NOTE: This process will have to both download (read) and upload (write) the
@ -41,11 +43,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCopy(cmd.Context(), copyOptions, globalOptions, args) return runCopy(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCopyCommand())
} }
// CopyOptions bundles all options for the copy command. // CopyOptions bundles all options for the copy command.
@ -59,13 +69,6 @@ func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
var copyOptions CopyOptions
func init() {
cmdRoot.AddCommand(cmdCopy)
copyOptions.AddFlags(cmdCopy.Flags())
}
func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string) error { func runCopy(ctx context.Context, opts CopyOptions, gopts GlobalOptions, args []string) error {
secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination") secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination")
if err != nil { if err != nil {

View File

@ -29,17 +29,27 @@ import (
"github.com/restic/restic/internal/restic" "github.com/restic/restic/internal/restic"
) )
var cmdDebug = &cobra.Command{ func newDebugCommand() *cobra.Command {
Use: "debug", cmd := &cobra.Command{
Short: "Debug commands", Use: "debug",
GroupID: cmdGroupDefault, Short: "Debug commands",
DisableAutoGenTag: true, GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
}
cmd.AddCommand(newDebugDumpCommand())
cmd.AddCommand(newDebugExamineCommand())
return cmd
} }
var cmdDebugDump = &cobra.Command{ func init() {
Use: "dump [indexes|snapshots|all|packs]", cmdRoot.AddCommand(newDebugCommand())
Short: "Dump data structures", }
Long: `
func newDebugDumpCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]",
Short: "Dump data structures",
Long: `
The "dump" command dumps data structures from the repository as JSON objects. It The "dump" command dumps data structures from the repository as JSON objects. It
is used for debugging purposes only. is used for debugging purposes only.
@ -52,10 +62,28 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runDebugDump(cmd.Context(), globalOptions, args) return runDebugDump(cmd.Context(), globalOptions, args)
}, },
}
return cmd
}
func newDebugExamineCommand() *cobra.Command {
var opts DebugExamineOptions
cmd := &cobra.Command{
Use: "examine pack-ID...",
Short: "Examine a pack file",
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDebugExamine(cmd.Context(), globalOptions, opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
} }
type DebugExamineOptions struct { type DebugExamineOptions struct {
@ -72,15 +100,6 @@ func (opts *DebugExamineOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RepairByte, "repair-byte", false, "try to repair broken blobs by trying bytes") 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)
debugExamineOpts.AddFlags(cmdDebugExamine.Flags())
}
func prettyPrintJSON(wr io.Writer, item interface{}) error { func prettyPrintJSON(wr io.Writer, item interface{}) error {
buf, err := json.MarshalIndent(item, "", " ") buf, err := json.MarshalIndent(item, "", " ")
if err != nil { if err != nil {
@ -197,15 +216,6 @@ func runDebugDump(ctx context.Context, gopts GlobalOptions, args []string) error
} }
} }
var cmdDebugExamine = &cobra.Command{
Use: "examine pack-ID...",
Short: "Examine a pack file",
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDebugExamine(cmd.Context(), globalOptions, debugExamineOpts, args)
},
}
func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, bytewise bool) []byte { func tryRepairWithBitflip(ctx context.Context, key *crypto.Key, input []byte, bytewise bool) []byte {
if bytewise { if bytewise {
Printf(" trying to repair blob by finding a broken byte\n") Printf(" trying to repair blob by finding a broken byte\n")

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdDiff = &cobra.Command{ func newDiffCommand() *cobra.Command {
Use: "diff [flags] snapshotID snapshotID", var opts DiffOptions
Short: "Show differences between two snapshots",
Long: ` cmd := &cobra.Command{
Use: "diff [flags] snapshotID snapshotID",
Short: "Show differences between two snapshots",
Long: `
The "diff" command shows differences from the first to the second snapshot. The The "diff" command shows differences from the first to the second snapshot. The
first characters in each line display what has happened to a particular file or first characters in each line display what has happened to a particular file or
directory: directory:
@ -46,11 +49,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runDiff(cmd.Context(), diffOptions, globalOptions, args) return runDiff(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newDiffCommand())
} }
// DiffOptions collects all options for the diff command. // DiffOptions collects all options for the diff command.
@ -62,13 +73,6 @@ func (opts *DiffOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata") f.BoolVar(&opts.ShowMetadata, "metadata", false, "print changes in metadata")
} }
var diffOptions DiffOptions
func init() {
cmdRoot.AddCommand(cmdDiff)
diffOptions.AddFlags(cmdDiff.Flags())
}
func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*restic.Snapshot, string, error) { func loadSnapshot(ctx context.Context, be restic.Lister, repo restic.LoaderUnpacked, desc string) (*restic.Snapshot, string, error) {
sn, subfolder, err := restic.FindSnapshot(ctx, be, repo, desc) sn, subfolder, err := restic.FindSnapshot(ctx, be, repo, desc)
if err != nil { if err != nil {

View File

@ -16,10 +16,12 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdDump = &cobra.Command{ func newDumpCommand() *cobra.Command {
Use: "dump [flags] snapshotID file", var opts DumpOptions
Short: "Print a backed-up file to stdout", cmd := &cobra.Command{
Long: ` Use: "dump [flags] snapshotID file",
Short: "Print a backed-up file to stdout",
Long: `
The "dump" command extracts files from a snapshot from the repository. If a The "dump" command extracts files from a snapshot from the repository. If a
single file is selected, it prints its contents to stdout. Folders are output single file is selected, it prints its contents to stdout. Folders are output
as a tar (default) or zip file containing the contents of the specified folder. as a tar (default) or zip file containing the contents of the specified folder.
@ -41,11 +43,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runDump(cmd.Context(), dumpOptions, globalOptions, args) return runDump(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newDumpCommand())
} }
// DumpOptions collects all options for the dump command. // DumpOptions collects all options for the dump command.
@ -61,13 +71,6 @@ func (opts *DumpOptions) AddFlags(f *pflag.FlagSet) {
f.StringVarP(&opts.Target, "target", "t", "", "write the output to target `path`") f.StringVarP(&opts.Target, "target", "t", "", "write the output to target `path`")
} }
var dumpOptions DumpOptions
func init() {
cmdRoot.AddCommand(cmdDump)
dumpOptions.AddFlags(cmdDump.Flags())
}
func splitPath(p string) []string { func splitPath(p string) []string {
d, f := path.Split(p) d, f := path.Split(p)
if d == "" || d == "/" { if d == "" || d == "/" {

View File

@ -10,10 +10,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var featuresCmd = &cobra.Command{ func newFeaturesCommand() *cobra.Command {
Use: "features", cmd := &cobra.Command{
Short: "Print list of feature flags", Use: "features",
Long: ` Short: "Print list of feature flags",
Long: `
The "features" command prints a list of supported feature flags. The "features" command prints a list of supported feature flags.
To pass feature flags to restic, set the RESTIC_FEATURES environment variable To pass feature flags to restic, set the RESTIC_FEATURES environment variable
@ -31,29 +32,32 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
GroupID: cmdGroupAdvanced, GroupID: cmdGroupAdvanced,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
if len(args) != 0 { if len(args) != 0 {
return errors.Fatal("the feature command expects no arguments") return errors.Fatal("the feature command expects no arguments")
} }
fmt.Printf("All Feature Flags:\n") fmt.Printf("All Feature Flags:\n")
flags := feature.Flag.List() flags := feature.Flag.List()
tab := table.New() tab := table.New()
tab.AddColumn("Name", "{{ .Name }}") tab.AddColumn("Name", "{{ .Name }}")
tab.AddColumn("Type", "{{ .Type }}") tab.AddColumn("Type", "{{ .Type }}")
tab.AddColumn("Default", "{{ .Default }}") tab.AddColumn("Default", "{{ .Default }}")
tab.AddColumn("Description", "{{ .Description }}") tab.AddColumn("Description", "{{ .Description }}")
for _, flag := range flags { for _, flag := range flags {
tab.AddRow(flag) tab.AddRow(flag)
} }
return tab.Write(globalOptions.stdout) return tab.Write(globalOptions.stdout)
}, },
}
return cmd
} }
func init() { func init() {
cmdRoot.AddCommand(featuresCmd) cmdRoot.AddCommand(newFeaturesCommand())
} }

View File

@ -17,16 +17,19 @@ import (
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
var cmdFind = &cobra.Command{ func newFindCommand() *cobra.Command {
Use: "find [flags] PATTERN...", var opts FindOptions
Short: "Find a file, a directory or restic IDs",
Long: ` cmd := &cobra.Command{
Use: "find [flags] PATTERN...",
Short: "Find a file, a directory or restic IDs",
Long: `
The "find" command searches for files or directories in snapshots stored in the The "find" command searches for files or directories in snapshots stored in the
repo. repo.
It can also be used to search for restic blobs or trees for troubleshooting. It can also be used to search for restic blobs or trees for troubleshooting.
The default sort option for the snapshots is youngest to oldest. To sort the The default sort option for the snapshots is youngest to oldest. To sort the
output from oldest to youngest specify --reverse.`, output from oldest to youngest specify --reverse.`,
Example: `restic find config.json Example: `restic find config.json
restic find --json "*.yml" "*.json" restic find --json "*.yml" "*.json"
restic find --json --blob 420f620f b46ebe8a ddd38656 restic find --json --blob 420f620f b46ebe8a ddd38656
restic find --show-pack-id --blob 420f620f restic find --show-pack-id --blob 420f620f
@ -42,11 +45,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runFind(cmd.Context(), findOptions, globalOptions, args) return runFind(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newFindCommand())
} }
// FindOptions bundles all options for the find command. // FindOptions bundles all options for the find command.
@ -79,13 +90,6 @@ func (opts *FindOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
var findOptions FindOptions
func init() {
cmdRoot.AddCommand(cmdFind)
findOptions.AddFlags(cmdFind.Flags())
}
type findPattern struct { type findPattern struct {
oldest, newest time.Time oldest, newest time.Time
pattern []string pattern []string

View File

@ -14,10 +14,14 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdForget = &cobra.Command{ func newForgetCommand() *cobra.Command {
Use: "forget [flags] [snapshot ID] [...]", var opts ForgetOptions
Short: "Remove snapshots from the repository", var pruneOpts PruneOptions
Long: `
cmd := &cobra.Command{
Use: "forget [flags] [snapshot ID] [...]",
Short: "Remove snapshots from the repository",
Long: `
The "forget" command removes snapshots according to a policy. All snapshots are The "forget" command removes snapshots according to a policy. All snapshots are
first divided into groups according to "--group-by", and after that the policy first divided into groups according to "--group-by", and after that the policy
specified by the "--keep-*" options is applied to each group individually. specified by the "--keep-*" options is applied to each group individually.
@ -41,13 +45,22 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runForget(cmd.Context(), forgetOptions, forgetPruneOptions, globalOptions, term, args) return runForget(cmd.Context(), opts, pruneOpts, globalOptions, term, args)
}, },
}
opts.AddFlags(cmd.Flags())
pruneOpts.AddLimitedFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newForgetCommand())
} }
type ForgetPolicyCount int type ForgetPolicyCount int
@ -145,15 +158,6 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
f.SortFlags = false f.SortFlags = false
} }
var forgetOptions ForgetOptions
var forgetPruneOptions PruneOptions
func init() {
cmdRoot.AddCommand(cmdForget)
forgetOptions.AddFlags(cmdForget.Flags())
forgetPruneOptions.AddLimitedFlags(cmdForget.Flags())
}
func verifyForgetOptions(opts *ForgetOptions) error { func verifyForgetOptions(opts *ForgetOptions) error {
if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 || if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 ||
opts.Monthly < -1 || opts.Yearly < -1 { opts.Monthly < -1 || opts.Yearly < -1 {

View File

@ -11,10 +11,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdGenerate = &cobra.Command{ func newGenerateCommand() *cobra.Command {
Use: "generate [flags]", var opts generateOptions
Short: "Generate manual pages and auto-completion files (bash, fish, zsh, powershell)",
Long: ` cmd := &cobra.Command{
Use: "generate [flags]",
Short: "Generate manual pages and auto-completion files (bash, fish, zsh, powershell)",
Long: `
The "generate" command writes automatically generated files (like the man pages The "generate" command writes automatically generated files (like the man pages
and the auto-completion files for bash, fish and zsh). and the auto-completion files for bash, fish and zsh).
@ -24,10 +27,17 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error { RunE: func(_ *cobra.Command, args []string) error {
return runGenerate(genOpts, args) return runGenerate(opts, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newGenerateCommand())
} }
type generateOptions struct { type generateOptions struct {
@ -46,13 +56,6 @@ func (opts *generateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)") f.StringVar(&opts.PowerShellCompletionFile, "powershell-completion", "", "write powershell completion `file` (`-` for stdout)")
} }
var genOpts generateOptions
func init() {
cmdRoot.AddCommand(cmdGenerate)
genOpts.AddFlags(cmdGenerate.Flags())
}
func writeManpages(dir string) error { func writeManpages(dir string) error {
// use a fixed date for the man pages so that generating them is deterministic // use a fixed date for the man pages so that generating them is deterministic
date, err := time.Parse("Jan 2006", "Jan 2017") date, err := time.Parse("Jan 2006", "Jan 2017")

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdInit = &cobra.Command{ func newInitCommand() *cobra.Command {
Use: "init", var opts InitOptions
Short: "Initialize a new repository",
Long: ` cmd := &cobra.Command{
Use: "init",
Short: "Initialize a new repository",
Long: `
The "init" command initializes a new repository. The "init" command initializes a new repository.
EXIT STATUS EXIT STATUS
@ -27,11 +30,18 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runInit(cmd.Context(), initOptions, globalOptions, args) return runInit(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newInitCommand())
} }
// InitOptions bundles all options for the init command. // InitOptions bundles all options for the init command.
@ -47,13 +57,6 @@ func (opts *InitOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.RepositoryVersion, "repository-version", "stable", "repository format version to use, allowed values are a format version, 'latest' and 'stable'") 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)
initOptions.AddFlags(cmdInit.Flags())
}
func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error { func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 { if len(args) > 0 {
return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags") return errors.Fatal("the init command expects no arguments, only options - please see `restic help init` for usage and flags")

View File

@ -10,10 +10,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdKeyAdd = &cobra.Command{ func newKeyAddCommand() *cobra.Command {
Use: "add", var opts KeyAddOptions
Short: "Add a new key (password) to the repository; returns the new key ID",
Long: ` cmd := &cobra.Command{
Use: "add",
Short: "Add a new key (password) to the repository; returns the new key ID",
Long: `
The "add" sub-command creates a new key and validates the key. Returns the new key ID. The "add" sub-command creates a new key and validates the key. Returns the new key ID.
EXIT STATUS EXIT STATUS
@ -25,7 +28,14 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyAdd(cmd.Context(), globalOptions, opts, args)
},
}
opts.Add(cmd.Flags())
return cmd
} }
type KeyAddOptions struct { type KeyAddOptions struct {
@ -43,13 +53,7 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
} }
func init() { func init() {
cmdKey.AddCommand(cmdKeyAdd) cmdKey.AddCommand(newKeyAddCommand())
var keyAddOpts KeyAddOptions
keyAddOpts.Add(cmdKeyAdd.Flags())
cmdKeyAdd.RunE = func(cmd *cobra.Command, args []string) error {
return runKeyAdd(cmd.Context(), globalOptions, keyAddOpts, args)
}
} }
func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error { func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error {

View File

@ -12,10 +12,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var cmdKeyList = &cobra.Command{ func newKeyListCommand() *cobra.Command {
Use: "list", cmd := &cobra.Command{
Short: "List keys (passwords)", Use: "list",
Long: ` Short: "List keys (passwords)",
Long: `
The "list" sub-command lists all the keys (passwords) associated with the repository. The "list" sub-command lists all the keys (passwords) associated with the repository.
Returns the key ID, username, hostname, created time and if it's the current key being Returns the key ID, username, hostname, created time and if it's the current key being
used to access the repository. used to access the repository.
@ -29,14 +30,16 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runKeyList(cmd.Context(), globalOptions, args) return runKeyList(cmd.Context(), globalOptions, args)
}, },
}
return cmd
} }
func init() { func init() {
cmdKey.AddCommand(cmdKeyList) cmdKey.AddCommand(newKeyListCommand())
} }
func runKeyList(ctx context.Context, gopts GlobalOptions, args []string) error { func runKeyList(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -7,12 +7,16 @@ import (
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"github.com/restic/restic/internal/repository" "github.com/restic/restic/internal/repository"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
var cmdKeyPasswd = &cobra.Command{ func newKeyPasswdCommand() *cobra.Command {
Use: "passwd", var opts KeyPasswdOptions
Short: "Change key (password); creates a new key ID and removes the old key ID, returns new key ID",
Long: ` cmd := &cobra.Command{
Use: "passwd",
Short: "Change key (password); creates a new key ID and removes the old key ID, returns new key ID",
Long: `
The "passwd" sub-command creates a new key, validates the key and remove the old key ID. The "passwd" sub-command creates a new key, validates the key and remove the old key ID.
Returns the new key ID. Returns the new key ID.
@ -25,21 +29,26 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyPasswd(cmd.Context(), globalOptions, opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdKey.AddCommand(newKeyPasswdCommand())
} }
type KeyPasswdOptions struct { type KeyPasswdOptions struct {
KeyAddOptions KeyAddOptions
} }
func init() { func (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) {
cmdKey.AddCommand(cmdKeyPasswd) opts.KeyAddOptions.Add(flags)
var keyPasswdOpts KeyPasswdOptions
keyPasswdOpts.KeyAddOptions.Add(cmdKeyPasswd.Flags())
cmdKeyPasswd.RunE = func(cmd *cobra.Command, args []string) error {
return runKeyPasswd(cmd.Context(), globalOptions, keyPasswdOpts, args)
}
} }
func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string) error { func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string) error {

View File

@ -10,10 +10,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var cmdKeyRemove = &cobra.Command{ func newKeyRemoveCommand() *cobra.Command {
Use: "remove [ID]", cmd := &cobra.Command{
Short: "Remove key ID (password) from the repository.", Use: "remove [ID]",
Long: ` Short: "Remove key ID (password) from the repository.",
Long: `
The "remove" sub-command removes the selected key ID. The "remove" command does not allow The "remove" sub-command removes the selected key ID. The "remove" command does not allow
removing the current key being used to access the repository. removing the current key being used to access the repository.
@ -26,14 +27,16 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runKeyRemove(cmd.Context(), globalOptions, args) return runKeyRemove(cmd.Context(), globalOptions, args)
}, },
}
return cmd
} }
func init() { func init() {
cmdKey.AddCommand(cmdKeyRemove) cmdKey.AddCommand(newKeyRemoveCommand())
} }
func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error { func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -11,13 +11,14 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"} func newListCommand() *cobra.Command {
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|") var listAllowedArgs = []string{"blobs", "packs", "index", "snapshots", "keys", "locks"}
var listAllowedArgsUseString = strings.Join(listAllowedArgs, "|")
var cmdList = &cobra.Command{ cmd := &cobra.Command{
Use: "list [flags] [" + listAllowedArgsUseString + "]", Use: "list [flags] [" + listAllowedArgsUseString + "]",
Short: "List objects in the repository", Short: "List objects in the repository",
Long: ` Long: `
The "list" command allows listing objects in the repository based on type. The "list" command allows listing objects in the repository based on type.
EXIT STATUS EXIT STATUS
@ -29,17 +30,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runList(cmd.Context(), globalOptions, args) return runList(cmd.Context(), globalOptions, args)
}, },
ValidArgs: listAllowedArgs, ValidArgs: listAllowedArgs,
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
}
return cmd
} }
func init() { func init() {
cmdRoot.AddCommand(cmdList) cmdRoot.AddCommand(newListCommand())
} }
func runList(ctx context.Context, gopts GlobalOptions, args []string) error { func runList(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

@ -21,10 +21,13 @@ import (
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
var cmdLs = &cobra.Command{ func newLsCommand() *cobra.Command {
Use: "ls [flags] snapshotID [dir...]", var opts LsOptions
Short: "List files in a snapshot",
Long: ` cmd := &cobra.Command{
Use: "ls [flags] snapshotID [dir...]",
Short: "List files in a snapshot",
Long: `
The "ls" command lists files and directories in a snapshot. The "ls" command lists files and directories in a snapshot.
The special snapshot ID "latest" can be used to list files and The special snapshot ID "latest" can be used to list files and
@ -53,11 +56,18 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runLs(cmd.Context(), lsOptions, globalOptions, args) return runLs(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newLsCommand())
} }
// LsOptions collects all options for the ls command. // LsOptions collects all options for the ls command.
@ -81,13 +91,6 @@ func (opts *LsOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.Reverse, "reverse", false, "reverse sorted output") f.BoolVar(&opts.Reverse, "reverse", false, "reverse sorted output")
} }
var lsOptions LsOptions
func init() {
cmdRoot.AddCommand(cmdLs)
lsOptions.AddFlags(cmdLs.Flags())
}
type lsPrinter interface { type lsPrinter interface {
Snapshot(sn *restic.Snapshot) error Snapshot(sn *restic.Snapshot) error
Node(path string, node *restic.Node, isPrefixDirectory bool) error Node(path string, node *restic.Node, isPrefixDirectory bool) error

View File

@ -12,10 +12,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdMigrate = &cobra.Command{ func newMigrateCommand() *cobra.Command {
Use: "migrate [flags] [migration name] [...]", var opts MigrateOptions
Short: "Apply migrations",
Long: ` cmd := &cobra.Command{
Use: "migrate [flags] [migration name] [...]",
Short: "Apply migrations",
Long: `
The "migrate" command checks which migrations can be applied for a repository The "migrate" command checks which migrations can be applied for a repository
and prints a list with available migration names. If one or more migration and prints a list with available migration names. If one or more migration
names are specified, these migrations are applied. names are specified, these migrations are applied.
@ -29,13 +32,21 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runMigrate(cmd.Context(), migrateOptions, globalOptions, args, term) return runMigrate(cmd.Context(), opts, globalOptions, args, term)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newMigrateCommand())
} }
// MigrateOptions bundles all options for the 'check' command. // MigrateOptions bundles all options for the 'check' command.
@ -47,13 +58,6 @@ func (opts *MigrateOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVarP(&opts.Force, "force", "f", false, `apply a migration a second time`) f.BoolVarP(&opts.Force, "force", "f", false, `apply a migration a second time`)
} }
var migrateOptions MigrateOptions
func init() {
cmdRoot.AddCommand(cmdMigrate)
migrateOptions.AddFlags(cmdMigrate.Flags())
}
func checkMigrations(ctx context.Context, repo restic.Repository, printer progress.Printer) error { func checkMigrations(ctx context.Context, repo restic.Repository, printer progress.Printer) error {
printer.P("available migrations:\n") printer.P("available migrations:\n")
found := false found := false

View File

@ -22,10 +22,13 @@ import (
"github.com/anacrolix/fuse/fs" "github.com/anacrolix/fuse/fs"
) )
var cmdMount = &cobra.Command{ func newMountCommand() *cobra.Command {
Use: "mount [flags] mountpoint", var opts MountOptions
Short: "Mount the repository",
Long: ` cmd := &cobra.Command{
Use: "mount [flags] mountpoint",
Short: "Mount the repository",
Long: `
The "mount" command mounts the repository via fuse to a directory. This is a The "mount" command mounts the repository via fuse to a directory. This is a
read-only mount. read-only mount.
@ -70,11 +73,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runMount(cmd.Context(), mountOptions, globalOptions, args) return runMount(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newMountCommand())
} }
// MountOptions collects all options for the mount command. // MountOptions collects all options for the mount command.
@ -100,13 +111,6 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) {
_ = f.MarkDeprecated("snapshot-template", "use --time-template") _ = f.MarkDeprecated("snapshot-template", "use --time-template")
} }
var mountOptions MountOptions
func init() {
cmdRoot.AddCommand(cmdMount)
mountOptions.AddFlags(cmdMount.Flags())
}
func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string) error { func runMount(ctx context.Context, opts MountOptions, gopts GlobalOptions, args []string) error {
if opts.TimeTemplate == "" { if opts.TimeTemplate == "" {
return errors.Fatal("time template string cannot be empty") return errors.Fatal("time template string cannot be empty")

View File

@ -8,10 +8,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var optionsCmd = &cobra.Command{ func newOptionsCommand() *cobra.Command {
Use: "options", cmd := &cobra.Command{
Short: "Print list of extended options", Use: "options",
Long: ` Short: "Print list of extended options",
Long: `
The "options" command prints a list of extended options. The "options" command prints a list of extended options.
EXIT STATUS EXIT STATUS
@ -20,22 +21,24 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
GroupID: cmdGroupAdvanced, GroupID: cmdGroupAdvanced,
DisableAutoGenTag: true, DisableAutoGenTag: true,
Run: func(_ *cobra.Command, _ []string) { Run: func(_ *cobra.Command, _ []string) {
fmt.Printf("All Extended Options:\n") fmt.Printf("All Extended Options:\n")
var maxLen int var maxLen int
for _, opt := range options.List() { for _, opt := range options.List() {
if l := len(opt.Namespace + "." + opt.Name); l > maxLen { if l := len(opt.Namespace + "." + opt.Name); l > maxLen {
maxLen = l maxLen = l
}
} }
} for _, opt := range options.List() {
for _, opt := range options.List() { fmt.Printf(" %*s %s\n", -maxLen, opt.Namespace+"."+opt.Name, opt.Text)
fmt.Printf(" %*s %s\n", -maxLen, opt.Namespace+"."+opt.Name, opt.Text) }
} },
}, }
return cmd
} }
func init() { func init() {
cmdRoot.AddCommand(optionsCmd) cmdRoot.AddCommand(newOptionsCommand())
} }

View File

@ -19,10 +19,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdPrune = &cobra.Command{ func newPruneCommand() *cobra.Command {
Use: "prune [flags]", var opts PruneOptions
Short: "Remove unneeded data from the repository",
Long: ` cmd := &cobra.Command{
Use: "prune [flags]",
Short: "Remove unneeded data from the repository",
Long: `
The "prune" command checks the repository and removes data that is not The "prune" command checks the repository and removes data that is not
referenced and therefore not needed any more. referenced and therefore not needed any more.
@ -35,13 +38,21 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runPrune(cmd.Context(), pruneOptions, globalOptions, term) return runPrune(cmd.Context(), opts, globalOptions, term)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newPruneCommand())
} }
// PruneOptions collects all options for the cleanup command. // PruneOptions collects all options for the cleanup command.
@ -76,13 +87,6 @@ func (opts *PruneOptions) AddLimitedFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data") f.BoolVar(&opts.RepackUncompressed, "repack-uncompressed", false, "repack all uncompressed data")
} }
var pruneOptions PruneOptions
func init() {
cmdRoot.AddCommand(cmdPrune)
pruneOptions.AddFlags(cmdPrune.Flags())
}
func verifyPruneOptions(opts *PruneOptions) error { func verifyPruneOptions(opts *PruneOptions) error {
opts.MaxRepackBytes = math.MaxUint64 opts.MaxRepackBytes = math.MaxUint64
if len(opts.MaxRepackSize) > 0 { if len(opts.MaxRepackSize) > 0 {

View File

@ -11,10 +11,11 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
var cmdRecover = &cobra.Command{ func newRecoverCommand() *cobra.Command {
Use: "recover [flags]", cmd := &cobra.Command{
Short: "Recover data from the repository not referenced by snapshots", Use: "recover [flags]",
Long: ` Short: "Recover data from the repository not referenced by snapshots",
Long: `
The "recover" command builds a new snapshot from all directories it can find in The "recover" command builds a new snapshot from all directories it can find in
the raw data of the repository which are not referenced in an existing snapshot. the raw data of the repository which are not referenced in an existing snapshot.
It can be used if, for example, a snapshot has been removed by accident with "forget". It can be used if, for example, a snapshot has been removed by accident with "forget".
@ -28,15 +29,17 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
return runRecover(cmd.Context(), globalOptions) return runRecover(cmd.Context(), globalOptions)
}, },
}
return cmd
} }
func init() { func init() {
cmdRoot.AddCommand(cmdRecover) cmdRoot.AddCommand(newRecoverCommand())
} }
func runRecover(ctx context.Context, gopts GlobalOptions) error { func runRecover(ctx context.Context, gopts GlobalOptions) error {

View File

@ -9,10 +9,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdRepairIndex = &cobra.Command{ func newRepairIndexCommand() *cobra.Command {
Use: "index [flags]", var opts RepairIndexOptions
Short: "Build a new index",
Long: ` cmd := &cobra.Command{
Use: "index [flags]",
Short: "Build a new index",
Long: `
The "repair index" command creates a new index based on the pack files in the The "repair index" command creates a new index based on the pack files in the
repository. repository.
@ -25,21 +28,20 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runRebuildIndex(cmd.Context(), repairIndexOptions, globalOptions, term) return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
} }
var cmdRebuildIndex = &cobra.Command{ func init() {
Use: "rebuild-index [flags]", cmdRepair.AddCommand(newRepairIndexCommand())
Short: cmdRepairIndex.Short,
Long: cmdRepairIndex.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
RunE: cmdRepairIndex.RunE,
} }
// RepairIndexOptions collects all options for the repair index command. // RepairIndexOptions collects all options for the repair index command.
@ -51,14 +53,32 @@ 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") f.BoolVar(&opts.ReadAllPacks, "read-all-packs", false, "read all pack files to generate new index from scratch")
} }
var repairIndexOptions RepairIndexOptions func newRebuildIndexCommand() *cobra.Command {
var opts RepairIndexOptions
replacement := newRepairIndexCommand()
cmd := &cobra.Command{
Use: "rebuild-index [flags]",
Short: replacement.Short,
Long: replacement.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
// must create a new instance of the run function as it captures opts
// by reference
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRebuildIndex(cmd.Context(), opts, globalOptions, term)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() { func init() {
cmdRepair.AddCommand(cmdRepairIndex)
repairIndexOptions.AddFlags(cmdRepairIndex.Flags())
// add alias for old name // add alias for old name
cmdRoot.AddCommand(cmdRebuildIndex) cmdRoot.AddCommand(newRebuildIndexCommand())
repairIndexOptions.AddFlags(cmdRebuildIndex.Flags())
} }
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error { func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error {

View File

@ -13,10 +13,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var cmdRepairPacks = &cobra.Command{ func newRepairPacksCommand() *cobra.Command {
Use: "packs [packIDs...]", cmd := &cobra.Command{
Short: "Salvage damaged pack files", Use: "packs [packIDs...]",
Long: ` Short: "Salvage damaged pack files",
Long: `
The "repair packs" command extracts intact blobs from the specified pack files, rebuilds The "repair packs" command extracts intact blobs from the specified pack files, rebuilds
the index to remove the damaged pack files and removes the pack files from the repository. the index to remove the damaged pack files and removes the pack files from the repository.
@ -29,16 +30,18 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runRepairPacks(cmd.Context(), globalOptions, term, args) return runRepairPacks(cmd.Context(), globalOptions, term, args)
}, },
}
return cmd
} }
func init() { func init() {
cmdRepair.AddCommand(cmdRepairPacks) cmdRepair.AddCommand(newRepairPacksCommand())
} }
func runRepairPacks(ctx context.Context, gopts GlobalOptions, term *termstatus.Terminal, args []string) error { func runRepairPacks(ctx context.Context, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {

View File

@ -11,10 +11,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdRepairSnapshots = &cobra.Command{ func newRepairSnapshotsCommand() *cobra.Command {
Use: "snapshots [flags] [snapshot ID] [...]", var opts RepairOptions
Short: "Repair snapshots",
Long: ` cmd := &cobra.Command{
Use: "snapshots [flags] [snapshot ID] [...]",
Short: "Repair snapshots",
Long: `
The "repair snapshots" command repairs broken snapshots. It scans the given The "repair snapshots" command repairs broken snapshots. It scans the given
snapshots and generates new ones with damaged directories and file contents snapshots and generates new ones with damaged directories and file contents
removed. If the broken snapshots are deleted, a prune run will be able to removed. If the broken snapshots are deleted, a prune run will be able to
@ -44,10 +47,18 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runRepairSnapshots(cmd.Context(), globalOptions, repairSnapshotOptions, args) return runRepairSnapshots(cmd.Context(), globalOptions, opts, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRepair.AddCommand(newRepairSnapshotsCommand())
} }
// RepairOptions collects all options for the repair command. // RepairOptions collects all options for the repair command.
@ -65,13 +76,6 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
var repairSnapshotOptions RepairOptions
func init() {
cmdRepair.AddCommand(cmdRepairSnapshots)
repairSnapshotOptions.AddFlags(cmdRepairSnapshots.Flags())
}
func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error { func runRepairSnapshots(ctx context.Context, gopts GlobalOptions, opts RepairOptions, args []string) error {
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun) ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun)
if err != nil { if err != nil {

View File

@ -18,10 +18,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdRestore = &cobra.Command{ func newRestoreCommand() *cobra.Command {
Use: "restore [flags] snapshotID", var opts RestoreOptions
Short: "Extract the data from a snapshot",
Long: ` cmd := &cobra.Command{
Use: "restore [flags] snapshotID",
Short: "Extract the data from a snapshot",
Long: `
The "restore" command extracts the data from a snapshot from the repository to The "restore" command extracts the data from a snapshot from the repository to
a directory. a directory.
@ -40,13 +43,21 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runRestore(cmd.Context(), restoreOptions, globalOptions, term, args) return runRestore(cmd.Context(), opts, globalOptions, term, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newRestoreCommand())
} }
// RestoreOptions collects all options for the restore command. // RestoreOptions collects all options for the restore command.
@ -81,13 +92,6 @@ func (opts *RestoreOptions) AddFlags(f *pflag.FlagSet) {
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") 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)
restoreOptions.AddFlags(cmdRestore.Flags())
}
func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions, func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
term *termstatus.Terminal, args []string) error { term *termstatus.Terminal, args []string) error {

View File

@ -16,10 +16,13 @@ import (
"github.com/restic/restic/internal/walker" "github.com/restic/restic/internal/walker"
) )
var cmdRewrite = &cobra.Command{ func newRewriteCommand() *cobra.Command {
Use: "rewrite [flags] [snapshotID ...]", var opts RewriteOptions
Short: "Rewrite snapshots to exclude unwanted files",
Long: ` cmd := &cobra.Command{
Use: "rewrite [flags] [snapshotID ...]",
Short: "Rewrite snapshots to exclude unwanted files",
Long: `
The "rewrite" command excludes files from existing snapshots. It creates new The "rewrite" command excludes files from existing snapshots. It creates new
snapshots containing the same data as the original ones, but without the files snapshots containing the same data as the original ones, but without the files
you specify to exclude. All metadata (time, host, tags) will be preserved. you specify to exclude. All metadata (time, host, tags) will be preserved.
@ -52,11 +55,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runRewrite(cmd.Context(), rewriteOptions, globalOptions, args) return runRewrite(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newRewriteCommand())
} }
type snapshotMetadata struct { type snapshotMetadata struct {
@ -111,13 +122,6 @@ func (opts *RewriteOptions) AddFlags(f *pflag.FlagSet) {
opts.ExcludePatternOptions.Add(f) opts.ExcludePatternOptions.Add(f)
} }
var rewriteOptions RewriteOptions
func init() {
cmdRoot.AddCommand(cmdRewrite)
rewriteOptions.AddFlags(cmdRewrite.Flags())
}
// rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will // rewriteFilterFunc returns the filtered tree ID or an error. If a snapshot summary is returned, the snapshot will
// be updated accordingly. // be updated accordingly.
type rewriteFilterFunc func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error) type rewriteFilterFunc func(ctx context.Context, sn *restic.Snapshot) (restic.ID, *restic.SnapshotSummary, error)

View File

@ -13,10 +13,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdSelfUpdate = &cobra.Command{ func newSelfUpdateCommand() *cobra.Command {
Use: "self-update [flags]", var opts SelfUpdateOptions
Short: "Update the restic binary",
Long: ` cmd := &cobra.Command{
Use: "self-update [flags]",
Short: "Update the restic binary",
Long: `
The command "self-update" downloads the latest stable release of restic from The command "self-update" downloads the latest stable release of restic from
GitHub and replaces the currently running binary. After download, the GitHub and replaces the currently running binary. After download, the
authenticity of the binary is verified using the GPG signature on the release authenticity of the binary is verified using the GPG signature on the release
@ -31,10 +34,18 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runSelfUpdate(cmd.Context(), selfUpdateOptions, globalOptions, args) return runSelfUpdate(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newSelfUpdateCommand())
} }
// SelfUpdateOptions collects all options for the update-restic command. // SelfUpdateOptions collects all options for the update-restic command.
@ -46,13 +57,6 @@ func (opts *SelfUpdateOptions) AddFlags(f *pflag.FlagSet) {
f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)") f.StringVar(&opts.Output, "output", "", "Save the downloaded file as `filename` (default: running binary itself)")
} }
var selfUpdateOptions SelfUpdateOptions
func init() {
cmdRoot.AddCommand(cmdSelfUpdate)
selfUpdateOptions.AddFlags(cmdSelfUpdate.Flags())
}
func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string) error { func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string) error {
if opts.Output == "" { if opts.Output == "" {
file, err := os.Executable() file, err := os.Executable()

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdSnapshots = &cobra.Command{ func newSnapshotsCommand() *cobra.Command {
Use: "snapshots [flags] [snapshotID ...]", var opts SnapshotOptions
Short: "List all snapshots",
Long: ` cmd := &cobra.Command{
Use: "snapshots [flags] [snapshotID ...]",
Short: "List all snapshots",
Long: `
The "snapshots" command lists all snapshots stored in the repository. The "snapshots" command lists all snapshots stored in the repository.
EXIT STATUS EXIT STATUS
@ -30,11 +33,19 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runSnapshots(cmd.Context(), snapshotOptions, globalOptions, args) return runSnapshots(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newSnapshotsCommand())
} }
// SnapshotOptions bundles all options for the snapshots command. // SnapshotOptions bundles all options for the snapshots command.
@ -59,13 +70,6 @@ func (opts *SnapshotOptions) AddFlags(f *pflag.FlagSet) {
f.VarP(&opts.GroupBy, "group-by", "g", "`group` snapshots by host, paths and/or tags, separated by comma") 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 { func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error {
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock) ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock)
if err != nil { if err != nil {

View File

@ -21,10 +21,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var cmdStats = &cobra.Command{ func newStatsCommand() *cobra.Command {
Use: "stats [flags] [snapshot ID] [...]", var opts StatsOptions
Short: "Scan the repository and show basic statistics",
Long: ` cmd := &cobra.Command{
Use: "stats [flags] [snapshot ID] [...]",
Short: "Scan the repository and show basic statistics",
Long: `
The "stats" command walks one or multiple snapshots in a repository The "stats" command walks one or multiple snapshots in a repository
and accumulates statistics about the data stored therein. It reports and accumulates statistics about the data stored therein. It reports
on the number of unique files and their sizes, according to one of on the number of unique files and their sizes, according to one of
@ -56,11 +59,22 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runStats(cmd.Context(), statsOptions, globalOptions, args) return runStats(cmd.Context(), opts, globalOptions, args)
}, },
}
opts.AddFlags(cmd.Flags())
must(cmd.RegisterFlagCompletionFunc("mode", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{countModeRestoreSize, countModeUniqueFilesByContents, countModeBlobsPerFile, countModeRawData}, cobra.ShellCompDirectiveDefault
}))
return cmd
}
func init() {
cmdRoot.AddCommand(newStatsCommand())
} }
// StatsOptions collects all options for the stats command. // StatsOptions collects all options for the stats command.
@ -76,22 +90,12 @@ func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
var statsOptions StatsOptions
func must(err error) { func must(err error) {
if err != nil { if err != nil {
panic(fmt.Sprintf("error during setup: %v", err)) panic(fmt.Sprintf("error during setup: %v", err))
} }
} }
func init() {
cmdRoot.AddCommand(cmdStats)
statsOptions.AddFlags(cmdStats.Flags())
must(cmdStats.RegisterFlagCompletionFunc("mode", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{countModeRestoreSize, countModeUniqueFilesByContents, countModeBlobsPerFile, countModeRawData}, cobra.ShellCompDirectiveDefault
}))
}
func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string) error { func runStats(ctx context.Context, opts StatsOptions, gopts GlobalOptions, args []string) error {
err := verifyStatsInput(opts) err := verifyStatsInput(opts)
if err != nil { if err != nil {

View File

@ -14,10 +14,13 @@ import (
"github.com/restic/restic/internal/ui/termstatus" "github.com/restic/restic/internal/ui/termstatus"
) )
var cmdTag = &cobra.Command{ func newTagCommand() *cobra.Command {
Use: "tag [flags] [snapshotID ...]", var opts TagOptions
Short: "Modify tags on snapshots",
Long: ` cmd := &cobra.Command{
Use: "tag [flags] [snapshotID ...]",
Short: "Modify tags on snapshots",
Long: `
The "tag" command allows you to modify tags on exiting snapshots. The "tag" command allows you to modify tags on exiting snapshots.
You can either set/replace the entire set of tags on a snapshot, or You can either set/replace the entire set of tags on a snapshot, or
@ -34,13 +37,21 @@ Exit status is 10 if the repository does not exist.
Exit status is 11 if the repository is already locked. Exit status is 11 if the repository is already locked.
Exit status is 12 if the password is incorrect. Exit status is 12 if the password is incorrect.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus() term, cancel := setupTermstatus()
defer cancel() defer cancel()
return runTag(cmd.Context(), tagOptions, globalOptions, term, args) return runTag(cmd.Context(), opts, globalOptions, term, args)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newTagCommand())
} }
// TagOptions bundles all options for the 'tag' command. // TagOptions bundles all options for the 'tag' command.
@ -58,13 +69,6 @@ func (opts *TagOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true) initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
} }
var tagOptions TagOptions
func init() {
cmdRoot.AddCommand(cmdTag)
tagOptions.AddFlags(cmdTag.Flags())
}
type changedSnapshot struct { type changedSnapshot struct {
MessageType string `json:"message_type"` // changed MessageType string `json:"message_type"` // changed
OldSnapshotID restic.ID `json:"old_snapshot_id"` OldSnapshotID restic.ID `json:"old_snapshot_id"`

View File

@ -8,10 +8,13 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
var unlockCmd = &cobra.Command{ func newUnlockCommand() *cobra.Command {
Use: "unlock", var opts UnlockOptions
Short: "Remove locks other processes created",
Long: ` cmd := &cobra.Command{
Use: "unlock",
Short: "Remove locks other processes created",
Long: `
The "unlock" command removes stale locks that have been created by other restic processes. The "unlock" command removes stale locks that have been created by other restic processes.
EXIT STATUS EXIT STATUS
@ -20,11 +23,18 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
GroupID: cmdGroupDefault, GroupID: cmdGroupDefault,
DisableAutoGenTag: true, DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error { RunE: func(cmd *cobra.Command, _ []string) error {
return runUnlock(cmd.Context(), unlockOptions, globalOptions) return runUnlock(cmd.Context(), opts, globalOptions)
}, },
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newUnlockCommand())
} }
// UnlockOptions collects all options for the unlock command. // UnlockOptions collects all options for the unlock command.
@ -36,13 +46,6 @@ func (opts *UnlockOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones") f.BoolVar(&opts.RemoveAll, "remove-all", false, "remove all locks, even non-stale ones")
} }
var unlockOptions UnlockOptions
func init() {
cmdRoot.AddCommand(unlockCmd)
unlockOptions.AddFlags(unlockCmd.Flags())
}
func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) error { func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) error {
repo, err := OpenRepository(ctx, gopts) repo, err := OpenRepository(ctx, gopts)
if err != nil { if err != nil {

View File

@ -8,10 +8,11 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var versionCmd = &cobra.Command{ func newVersionCommand() *cobra.Command {
Use: "version", cmd := &cobra.Command{
Short: "Print version information", Use: "version",
Long: ` Short: "Print version information",
Long: `
The "version" command prints detailed information about the build environment The "version" command prints detailed information about the build environment
and the version of this software. and the version of this software.
@ -21,38 +22,40 @@ EXIT STATUS
Exit status is 0 if the command was successful. Exit status is 0 if the command was successful.
Exit status is 1 if there was any error. Exit status is 1 if there was any error.
`, `,
DisableAutoGenTag: true, DisableAutoGenTag: true,
Run: func(_ *cobra.Command, _ []string) { Run: func(_ *cobra.Command, _ []string) {
if globalOptions.JSON { if globalOptions.JSON {
type jsonVersion struct { type jsonVersion struct {
MessageType string `json:"message_type"` // version MessageType string `json:"message_type"` // version
Version string `json:"version"` Version string `json:"version"`
GoVersion string `json:"go_version"` GoVersion string `json:"go_version"`
GoOS string `json:"go_os"` GoOS string `json:"go_os"`
GoArch string `json:"go_arch"` GoArch string `json:"go_arch"`
}
jsonS := jsonVersion{
MessageType: "version",
Version: version,
GoVersion: runtime.Version(),
GoOS: runtime.GOOS,
GoArch: runtime.GOARCH,
}
err := json.NewEncoder(globalOptions.stdout).Encode(jsonS)
if err != nil {
Warnf("JSON encode failed: %v\n", err)
return
}
} else {
fmt.Printf("restic %s compiled with %v on %v/%v\n",
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
} }
jsonS := jsonVersion{ },
MessageType: "version", }
Version: version, return cmd
GoVersion: runtime.Version(),
GoOS: runtime.GOOS,
GoArch: runtime.GOARCH,
}
err := json.NewEncoder(globalOptions.stdout).Encode(jsonS)
if err != nil {
Warnf("JSON encode failed: %v\n", err)
return
}
} else {
fmt.Printf("restic %s compiled with %v on %v/%v\n",
version, runtime.Version(), runtime.GOOS, runtime.GOARCH)
}
},
} }
func init() { func init() {
cmdRoot.AddCommand(versionCmd) cmdRoot.AddCommand(newVersionCommand())
} }