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"
)
var cmdBackup = &cobra.Command{
Use: "backup [flags] [FILE/DIR] ...",
Short: "Create a new backup of files and/or directories",
Long: `
func newBackupCommand() *cobra.Command {
var opts BackupOptions
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
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 12 if the password is incorrect.
`,
PreRun: func(_ *cobra.Command, _ []string) {
if backupOptions.Host == "" {
hostname, err := os.Hostname()
if err != nil {
debug.Log("os.Hostname() returned err: %v", err)
return
PreRun: func(_ *cobra.Command, _ []string) {
if opts.Host == "" {
hostname, err := os.Hostname()
if err != nil {
debug.Log("os.Hostname() returned err: %v", err)
return
}
opts.Host = hostname
}
backupOptions.Host = hostname
}
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runBackup(cmd.Context(), backupOptions, globalOptions, term, args)
},
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runBackup(cmd.Context(), opts, globalOptions, term, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newBackupCommand())
}
// 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
// ErrInvalidSourceData is used to report an incomplete backup
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
// items exist at all.
func filterExisting(items []string) (result []string, err error) {

View File

@ -16,10 +16,13 @@ import (
"github.com/spf13/pflag"
)
var cmdCache = &cobra.Command{
Use: "cache",
Short: "Operate on local cache directories",
Long: `
func newCacheCommand() *cobra.Command {
var opts CacheOptions
cmd := &cobra.Command{
Use: "cache",
Short: "Operate on local cache directories",
Long: `
The "cache" command allows listing and cleaning local cache directories.
EXIT STATUS
@ -28,11 +31,19 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runCache(cacheOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runCache(opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCacheCommand())
}
// 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")
}
var cacheOptions CacheOptions
func init() {
cmdRoot.AddCommand(cmdCache)
cacheOptions.AddFlags(cmdCache.Flags())
}
func runCache(opts CacheOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 {
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 cmdCat = &cobra.Command{
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
Short: "Print internal objects to stdout",
Long: `
func newCatCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "cat [flags] [masterkey|config|pack ID|blob ID|snapshot ID|index ID|key ID|lock ID|tree snapshot:subfolder]",
Short: "Print internal objects to stdout",
Long: `
The "cat" command is used to print internal objects to stdout.
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runCat(cmd.Context(), globalOptions, args)
},
ValidArgs: catAllowedCmds,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runCat(cmd.Context(), globalOptions, args)
},
ValidArgs: catAllowedCmds,
}
return cmd
}
func init() {
cmdRoot.AddCommand(cmdCat)
cmdRoot.AddCommand(newCatCommand())
}
func validateCatArgs(args []string) error {

View File

@ -23,10 +23,12 @@ import (
"github.com/restic/restic/internal/ui/termstatus"
)
var cmdCheck = &cobra.Command{
Use: "check [flags]",
Short: "Check the repository for errors",
Long: `
func newCheckCommand() *cobra.Command {
var opts CheckOptions
cmd := &cobra.Command{
Use: "check [flags]",
Short: "Check the repository for errors",
Long: `
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.
@ -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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
summary, err := runCheck(cmd.Context(), checkOptions, globalOptions, args, term)
if globalOptions.JSON {
if err != nil && summary.NumErrors == 0 {
summary.NumErrors = 1
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
summary, err := runCheck(cmd.Context(), opts, globalOptions, args, term)
if globalOptions.JSON {
if err != nil && summary.NumErrors == 0 {
summary.NumErrors = 1
}
term.Print(ui.ToJSONString(summary))
}
term.Print(ui.ToJSONString(summary))
}
return err
},
PreRunE: func(_ *cobra.Command, _ []string) error {
return checkFlags(checkOptions)
},
return err
},
PreRunE: func(_ *cobra.Command, _ []string) error {
return checkFlags(opts)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newCheckCommand())
}
// 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")
}
var checkOptions CheckOptions
func init() {
cmdRoot.AddCommand(cmdCheck)
checkOptions.AddFlags(cmdCheck.Flags())
}
func checkFlags(opts CheckOptions) error {
if opts.ReadData && opts.ReadDataSubset != "" {
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"
)
var cmdCopy = &cobra.Command{
Use: "copy [flags] [snapshotID ...]",
Short: "Copy snapshots from one repository to another",
Long: `
func newCopyCommand() *cobra.Command {
var opts CopyOptions
cmd := &cobra.Command{
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.
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runCopy(cmd.Context(), copyOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -59,13 +69,6 @@ func (opts *CopyOptions) AddFlags(f *pflag.FlagSet) {
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 {
secondaryGopts, isFromRepo, err := fillSecondaryGlobalOpts(ctx, opts.secondaryRepoOptions, gopts, "destination")
if err != nil {

View File

@ -29,17 +29,27 @@ import (
"github.com/restic/restic/internal/restic"
)
var cmdDebug = &cobra.Command{
Use: "debug",
Short: "Debug commands",
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
func newDebugCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "debug",
Short: "Debug commands",
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
}
cmd.AddCommand(newDebugDumpCommand())
cmd.AddCommand(newDebugExamineCommand())
return cmd
}
var cmdDebugDump = &cobra.Command{
Use: "dump [indexes|snapshots|all|packs]",
Short: "Dump data structures",
Long: `
func init() {
cmdRoot.AddCommand(newDebugCommand())
}
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
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDebugDump(cmd.Context(), globalOptions, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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 {
@ -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")
}
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 {
buf, err := json.MarshalIndent(item, "", " ")
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 {
if bytewise {
Printf(" trying to repair blob by finding a broken byte\n")

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag"
)
var cmdDiff = &cobra.Command{
Use: "diff [flags] snapshotID snapshotID",
Short: "Show differences between two snapshots",
Long: `
func newDiffCommand() *cobra.Command {
var opts DiffOptions
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
first characters in each line display what has happened to a particular file or
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDiff(cmd.Context(), diffOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -62,13 +73,6 @@ 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)
diffOptions.AddFlags(cmdDiff.Flags())
}
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)
if err != nil {

View File

@ -16,10 +16,12 @@ import (
"github.com/spf13/pflag"
)
var cmdDump = &cobra.Command{
Use: "dump [flags] snapshotID file",
Short: "Print a backed-up file to stdout",
Long: `
func newDumpCommand() *cobra.Command {
var opts DumpOptions
cmd := &cobra.Command{
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
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.
@ -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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runDump(cmd.Context(), dumpOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -61,13 +71,6 @@ func (opts *DumpOptions) AddFlags(f *pflag.FlagSet) {
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 {
d, f := path.Split(p)
if d == "" || d == "/" {

View File

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

View File

@ -17,16 +17,19 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdFind = &cobra.Command{
Use: "find [flags] PATTERN...",
Short: "Find a file, a directory or restic IDs",
Long: `
func newFindCommand() *cobra.Command {
var opts FindOptions
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
repo.
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
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 --blob 420f620f b46ebe8a ddd38656
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runFind(cmd.Context(), findOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -79,13 +90,6 @@ func (opts *FindOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var findOptions FindOptions
func init() {
cmdRoot.AddCommand(cmdFind)
findOptions.AddFlags(cmdFind.Flags())
}
type findPattern struct {
oldest, newest time.Time
pattern []string

View File

@ -14,10 +14,14 @@ import (
"github.com/spf13/pflag"
)
var cmdForget = &cobra.Command{
Use: "forget [flags] [snapshot ID] [...]",
Short: "Remove snapshots from the repository",
Long: `
func newForgetCommand() *cobra.Command {
var opts ForgetOptions
var pruneOpts PruneOptions
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
first divided into groups according to "--group-by", and after that the policy
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runForget(cmd.Context(), forgetOptions, forgetPruneOptions, globalOptions, term, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
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
@ -145,15 +158,6 @@ func (opts *ForgetOptions) AddFlags(f *pflag.FlagSet) {
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 {
if opts.Last < -1 || opts.Hourly < -1 || opts.Daily < -1 || opts.Weekly < -1 ||
opts.Monthly < -1 || opts.Yearly < -1 {

View File

@ -11,10 +11,13 @@ import (
"github.com/spf13/pflag"
)
var cmdGenerate = &cobra.Command{
Use: "generate [flags]",
Short: "Generate manual pages and auto-completion files (bash, fish, zsh, powershell)",
Long: `
func newGenerateCommand() *cobra.Command {
var opts generateOptions
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
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 1 if there was any error.
`,
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runGenerate(genOpts, args)
},
DisableAutoGenTag: true,
RunE: func(_ *cobra.Command, args []string) error {
return runGenerate(opts, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newGenerateCommand())
}
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)")
}
var genOpts generateOptions
func init() {
cmdRoot.AddCommand(cmdGenerate)
genOpts.AddFlags(cmdGenerate.Flags())
}
func writeManpages(dir string) error {
// use a fixed date for the man pages so that generating them is deterministic
date, err := time.Parse("Jan 2006", "Jan 2017")

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag"
)
var cmdInit = &cobra.Command{
Use: "init",
Short: "Initialize a new repository",
Long: `
func newInitCommand() *cobra.Command {
var opts InitOptions
cmd := &cobra.Command{
Use: "init",
Short: "Initialize a new repository",
Long: `
The "init" command initializes a new repository.
EXIT STATUS
@ -27,11 +30,18 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runInit(cmd.Context(), initOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -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'")
}
var initOptions InitOptions
func init() {
cmdRoot.AddCommand(cmdInit)
initOptions.AddFlags(cmdInit.Flags())
}
func runInit(ctx context.Context, opts InitOptions, gopts GlobalOptions, args []string) error {
if len(args) > 0 {
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"
)
var cmdKeyAdd = &cobra.Command{
Use: "add",
Short: "Add a new key (password) to the repository; returns the new key ID",
Long: `
func newKeyAddCommand() *cobra.Command {
var opts KeyAddOptions
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.
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 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 {
@ -43,13 +53,7 @@ func (opts *KeyAddOptions) Add(flags *pflag.FlagSet) {
}
func init() {
cmdKey.AddCommand(cmdKeyAdd)
var keyAddOpts KeyAddOptions
keyAddOpts.Add(cmdKeyAdd.Flags())
cmdKeyAdd.RunE = func(cmd *cobra.Command, args []string) error {
return runKeyAdd(cmd.Context(), globalOptions, keyAddOpts, args)
}
cmdKey.AddCommand(newKeyAddCommand())
}
func runKeyAdd(ctx context.Context, gopts GlobalOptions, opts KeyAddOptions, args []string) error {

View File

@ -12,10 +12,11 @@ import (
"github.com/spf13/cobra"
)
var cmdKeyList = &cobra.Command{
Use: "list",
Short: "List keys (passwords)",
Long: `
func newKeyListCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "list",
Short: "List keys (passwords)",
Long: `
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
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyList(cmd.Context(), globalOptions, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyList(cmd.Context(), globalOptions, args)
},
}
return cmd
}
func init() {
cmdKey.AddCommand(cmdKeyList)
cmdKey.AddCommand(newKeyListCommand())
}
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/repository"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var cmdKeyPasswd = &cobra.Command{
Use: "passwd",
Short: "Change key (password); creates a new key ID and removes the old key ID, returns new key ID",
Long: `
func newKeyPasswdCommand() *cobra.Command {
var opts KeyPasswdOptions
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.
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 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 {
KeyAddOptions
}
func init() {
cmdKey.AddCommand(cmdKeyPasswd)
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 (opts *KeyPasswdOptions) AddFlags(flags *pflag.FlagSet) {
opts.KeyAddOptions.Add(flags)
}
func runKeyPasswd(ctx context.Context, gopts GlobalOptions, opts KeyPasswdOptions, args []string) error {

View File

@ -10,10 +10,11 @@ import (
"github.com/spf13/cobra"
)
var cmdKeyRemove = &cobra.Command{
Use: "remove [ID]",
Short: "Remove key ID (password) from the repository.",
Long: `
func newKeyRemoveCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "remove [ID]",
Short: "Remove key ID (password) from the repository.",
Long: `
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.
@ -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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyRemove(cmd.Context(), globalOptions, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runKeyRemove(cmd.Context(), globalOptions, args)
},
}
return cmd
}
func init() {
cmdKey.AddCommand(cmdKeyRemove)
cmdKey.AddCommand(newKeyRemoveCommand())
}
func runKeyRemove(ctx context.Context, gopts GlobalOptions, args []string) error {

View File

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

View File

@ -21,10 +21,13 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdLs = &cobra.Command{
Use: "ls [flags] snapshotID [dir...]",
Short: "List files in a snapshot",
Long: `
func newLsCommand() *cobra.Command {
var opts LsOptions
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 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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
return runLs(cmd.Context(), lsOptions, globalOptions, args)
},
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -81,13 +91,6 @@ func (opts *LsOptions) AddFlags(f *pflag.FlagSet) {
f.BoolVar(&opts.Reverse, "reverse", false, "reverse sorted output")
}
var lsOptions LsOptions
func init() {
cmdRoot.AddCommand(cmdLs)
lsOptions.AddFlags(cmdLs.Flags())
}
type lsPrinter interface {
Snapshot(sn *restic.Snapshot) error
Node(path string, node *restic.Node, isPrefixDirectory bool) error

View File

@ -12,10 +12,13 @@ import (
"github.com/spf13/pflag"
)
var cmdMigrate = &cobra.Command{
Use: "migrate [flags] [migration name] [...]",
Short: "Apply migrations",
Long: `
func newMigrateCommand() *cobra.Command {
var opts MigrateOptions
cmd := &cobra.Command{
Use: "migrate [flags] [migration name] [...]",
Short: "Apply migrations",
Long: `
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
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runMigrate(cmd.Context(), migrateOptions, globalOptions, args, term)
},
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
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.
@ -47,13 +58,6 @@ 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)
migrateOptions.AddFlags(cmdMigrate.Flags())
}
func checkMigrations(ctx context.Context, repo restic.Repository, printer progress.Printer) error {
printer.P("available migrations:\n")
found := false

View File

@ -22,10 +22,13 @@ import (
"github.com/anacrolix/fuse/fs"
)
var cmdMount = &cobra.Command{
Use: "mount [flags] mountpoint",
Short: "Mount the repository",
Long: `
func newMountCommand() *cobra.Command {
var opts MountOptions
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
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
return runMount(cmd.Context(), mountOptions, globalOptions, args)
},
DisableAutoGenTag: true,
GroupID: cmdGroupDefault,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -100,13 +111,6 @@ func (opts *MountOptions) AddFlags(f *pflag.FlagSet) {
_ = 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 {
if opts.TimeTemplate == "" {
return errors.Fatal("time template string cannot be empty")

View File

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

View File

@ -19,10 +19,13 @@ import (
"github.com/spf13/pflag"
)
var cmdPrune = &cobra.Command{
Use: "prune [flags]",
Short: "Remove unneeded data from the repository",
Long: `
func newPruneCommand() *cobra.Command {
var opts PruneOptions
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
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runPrune(cmd.Context(), pruneOptions, globalOptions, term)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
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.
@ -76,13 +87,6 @@ func (opts *PruneOptions) AddLimitedFlags(f *pflag.FlagSet) {
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 {
opts.MaxRepackBytes = math.MaxUint64
if len(opts.MaxRepackSize) > 0 {

View File

@ -11,10 +11,11 @@ import (
"golang.org/x/sync/errgroup"
)
var cmdRecover = &cobra.Command{
Use: "recover [flags]",
Short: "Recover data from the repository not referenced by snapshots",
Long: `
func newRecoverCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "recover [flags]",
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 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".
@ -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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return runRecover(cmd.Context(), globalOptions)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return runRecover(cmd.Context(), globalOptions)
},
}
return cmd
}
func init() {
cmdRoot.AddCommand(cmdRecover)
cmdRoot.AddCommand(newRecoverCommand())
}
func runRecover(ctx context.Context, gopts GlobalOptions) error {

View File

@ -9,10 +9,13 @@ import (
"github.com/spf13/pflag"
)
var cmdRepairIndex = &cobra.Command{
Use: "index [flags]",
Short: "Build a new index",
Long: `
func newRepairIndexCommand() *cobra.Command {
var opts RepairIndexOptions
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
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRebuildIndex(cmd.Context(), repairIndexOptions, globalOptions, term)
},
DisableAutoGenTag: true,
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
}
var cmdRebuildIndex = &cobra.Command{
Use: "rebuild-index [flags]",
Short: cmdRepairIndex.Short,
Long: cmdRepairIndex.Long,
Deprecated: `Use "repair index" instead`,
DisableAutoGenTag: true,
RunE: cmdRepairIndex.RunE,
func init() {
cmdRepair.AddCommand(newRepairIndexCommand())
}
// 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")
}
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() {
cmdRepair.AddCommand(cmdRepairIndex)
repairIndexOptions.AddFlags(cmdRepairIndex.Flags())
// add alias for old name
cmdRoot.AddCommand(cmdRebuildIndex)
repairIndexOptions.AddFlags(cmdRebuildIndex.Flags())
cmdRoot.AddCommand(newRebuildIndexCommand())
}
func runRebuildIndex(ctx context.Context, opts RepairIndexOptions, gopts GlobalOptions, term *termstatus.Terminal) error {

View File

@ -13,10 +13,11 @@ import (
"github.com/spf13/cobra"
)
var cmdRepairPacks = &cobra.Command{
Use: "packs [packIDs...]",
Short: "Salvage damaged pack files",
Long: `
func newRepairPacksCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "packs [packIDs...]",
Short: "Salvage damaged pack files",
Long: `
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.
@ -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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRepairPacks(cmd.Context(), globalOptions, term, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRepairPacks(cmd.Context(), globalOptions, term, args)
},
}
return cmd
}
func init() {
cmdRepair.AddCommand(cmdRepairPacks)
cmdRepair.AddCommand(newRepairPacksCommand())
}
func runRepairPacks(ctx context.Context, gopts GlobalOptions, term *termstatus.Terminal, args []string) error {

View File

@ -11,10 +11,13 @@ import (
"github.com/spf13/pflag"
)
var cmdRepairSnapshots = &cobra.Command{
Use: "snapshots [flags] [snapshot ID] [...]",
Short: "Repair snapshots",
Long: `
func newRepairSnapshotsCommand() *cobra.Command {
var opts RepairOptions
cmd := &cobra.Command{
Use: "snapshots [flags] [snapshot ID] [...]",
Short: "Repair snapshots",
Long: `
The "repair snapshots" command repairs broken snapshots. It scans the given
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
@ -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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRepairSnapshots(cmd.Context(), globalOptions, repairSnapshotOptions, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -65,13 +76,6 @@ func (opts *RepairOptions) AddFlags(f *pflag.FlagSet) {
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 {
ctx, repo, unlock, err := openWithExclusiveLock(ctx, gopts, opts.DryRun)
if err != nil {

View File

@ -18,10 +18,13 @@ import (
"github.com/spf13/pflag"
)
var cmdRestore = &cobra.Command{
Use: "restore [flags] snapshotID",
Short: "Extract the data from a snapshot",
Long: `
func newRestoreCommand() *cobra.Command {
var opts RestoreOptions
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
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runRestore(cmd.Context(), restoreOptions, globalOptions, term, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
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.
@ -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")
}
var restoreOptions RestoreOptions
func init() {
cmdRoot.AddCommand(cmdRestore)
restoreOptions.AddFlags(cmdRestore.Flags())
}
func runRestore(ctx context.Context, opts RestoreOptions, gopts GlobalOptions,
term *termstatus.Terminal, args []string) error {

View File

@ -16,10 +16,13 @@ import (
"github.com/restic/restic/internal/walker"
)
var cmdRewrite = &cobra.Command{
Use: "rewrite [flags] [snapshotID ...]",
Short: "Rewrite snapshots to exclude unwanted files",
Long: `
func newRewriteCommand() *cobra.Command {
var opts RewriteOptions
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
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.
@ -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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRewrite(cmd.Context(), rewriteOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runRewrite(cmd.Context(), opts, globalOptions, args)
},
}
opts.AddFlags(cmd.Flags())
return cmd
}
func init() {
cmdRoot.AddCommand(newRewriteCommand())
}
type snapshotMetadata struct {
@ -111,13 +122,6 @@ func (opts *RewriteOptions) AddFlags(f *pflag.FlagSet) {
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
// be updated accordingly.
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"
)
var cmdSelfUpdate = &cobra.Command{
Use: "self-update [flags]",
Short: "Update the restic binary",
Long: `
func newSelfUpdateCommand() *cobra.Command {
var opts SelfUpdateOptions
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
GitHub and replaces the currently running binary. After download, the
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 12 if the password is incorrect.
`,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSelfUpdate(cmd.Context(), selfUpdateOptions, globalOptions, args)
},
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -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)")
}
var selfUpdateOptions SelfUpdateOptions
func init() {
cmdRoot.AddCommand(cmdSelfUpdate)
selfUpdateOptions.AddFlags(cmdSelfUpdate.Flags())
}
func runSelfUpdate(ctx context.Context, opts SelfUpdateOptions, gopts GlobalOptions, args []string) error {
if opts.Output == "" {
file, err := os.Executable()

View File

@ -15,10 +15,13 @@ import (
"github.com/spf13/pflag"
)
var cmdSnapshots = &cobra.Command{
Use: "snapshots [flags] [snapshotID ...]",
Short: "List all snapshots",
Long: `
func newSnapshotsCommand() *cobra.Command {
var opts SnapshotOptions
cmd := &cobra.Command{
Use: "snapshots [flags] [snapshotID ...]",
Short: "List all snapshots",
Long: `
The "snapshots" command lists all snapshots stored in the repository.
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runSnapshots(cmd.Context(), snapshotOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -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")
}
var snapshotOptions SnapshotOptions
func init() {
cmdRoot.AddCommand(cmdSnapshots)
snapshotOptions.AddFlags(cmdSnapshots.Flags())
}
func runSnapshots(ctx context.Context, opts SnapshotOptions, gopts GlobalOptions, args []string) error {
ctx, repo, unlock, err := openWithReadLock(ctx, gopts, gopts.NoLock)
if err != nil {

View File

@ -21,10 +21,13 @@ import (
"github.com/spf13/pflag"
)
var cmdStats = &cobra.Command{
Use: "stats [flags] [snapshot ID] [...]",
Short: "Scan the repository and show basic statistics",
Long: `
func newStatsCommand() *cobra.Command {
var opts StatsOptions
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
and accumulates statistics about the data stored therein. It reports
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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
return runStats(cmd.Context(), statsOptions, globalOptions, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
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.
@ -76,22 +90,12 @@ func (opts *StatsOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var statsOptions StatsOptions
func must(err error) {
if err != nil {
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 {
err := verifyStatsInput(opts)
if err != nil {

View File

@ -14,10 +14,13 @@ import (
"github.com/restic/restic/internal/ui/termstatus"
)
var cmdTag = &cobra.Command{
Use: "tag [flags] [snapshotID ...]",
Short: "Modify tags on snapshots",
Long: `
func newTagCommand() *cobra.Command {
var opts TagOptions
cmd := &cobra.Command{
Use: "tag [flags] [snapshotID ...]",
Short: "Modify tags on snapshots",
Long: `
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
@ -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 12 if the password is incorrect.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
return runTag(cmd.Context(), tagOptions, globalOptions, term, args)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, args []string) error {
term, cancel := setupTermstatus()
defer cancel()
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.
@ -58,13 +69,6 @@ func (opts *TagOptions) AddFlags(f *pflag.FlagSet) {
initMultiSnapshotFilter(f, &opts.SnapshotFilter, true)
}
var tagOptions TagOptions
func init() {
cmdRoot.AddCommand(cmdTag)
tagOptions.AddFlags(cmdTag.Flags())
}
type changedSnapshot struct {
MessageType string `json:"message_type"` // changed
OldSnapshotID restic.ID `json:"old_snapshot_id"`

View File

@ -8,10 +8,13 @@ import (
"github.com/spf13/pflag"
)
var unlockCmd = &cobra.Command{
Use: "unlock",
Short: "Remove locks other processes created",
Long: `
func newUnlockCommand() *cobra.Command {
var opts UnlockOptions
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.
EXIT STATUS
@ -20,11 +23,18 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
return runUnlock(cmd.Context(), unlockOptions, globalOptions)
},
GroupID: cmdGroupDefault,
DisableAutoGenTag: true,
RunE: func(cmd *cobra.Command, _ []string) error {
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.
@ -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")
}
var unlockOptions UnlockOptions
func init() {
cmdRoot.AddCommand(unlockCmd)
unlockOptions.AddFlags(unlockCmd.Flags())
}
func runUnlock(ctx context.Context, opts UnlockOptions, gopts GlobalOptions) error {
repo, err := OpenRepository(ctx, gopts)
if err != nil {

View File

@ -8,10 +8,11 @@ import (
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version information",
Long: `
func newVersionCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "Print version information",
Long: `
The "version" command prints detailed information about the build environment
and the version of this software.
@ -21,38 +22,40 @@ EXIT STATUS
Exit status is 0 if the command was successful.
Exit status is 1 if there was any error.
`,
DisableAutoGenTag: true,
Run: func(_ *cobra.Command, _ []string) {
if globalOptions.JSON {
type jsonVersion struct {
MessageType string `json:"message_type"` // version
Version string `json:"version"`
GoVersion string `json:"go_version"`
GoOS string `json:"go_os"`
GoArch string `json:"go_arch"`
DisableAutoGenTag: true,
Run: func(_ *cobra.Command, _ []string) {
if globalOptions.JSON {
type jsonVersion struct {
MessageType string `json:"message_type"` // version
Version string `json:"version"`
GoVersion string `json:"go_version"`
GoOS string `json:"go_os"`
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,
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)
}
},
},
}
return cmd
}
func init() {
cmdRoot.AddCommand(versionCmd)
cmdRoot.AddCommand(newVersionCommand())
}