Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion internal/cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@ Required: <name> + --email. Everything else is optional.

Run with no flags (just the name) and you'll get an interactive
prompt for each field.`,
Args: cobra.ExactArgs(1),
Args: exactArgsHelp(1,
"gitswitch add requires an identity name",
"gitswitch add work --email you@company.com",
"gitswitch add personal (interactive prompts for the rest)",
),
Example: " gitswitch add work --email you@company.com --gh you-work\n" +
" gitswitch add personal # interactive: prompts for email, key, gh, etc.",
RunE: func(cmd *cobra.Command, args []string) error {
return runAdd(cmd, args[0])
},
Expand Down
62 changes: 62 additions & 0 deletions internal/cmd/argshelp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cmd

import (
"errors"
"strings"

"github.com/spf13/cobra"
)

// argError formats a friendly "you used this wrong" error for argument
// validation failures. The default cobra message ("accepts 1 arg(s),
// received 0") is technically correct but tells the user nothing
// useful — they have to leave the error and run --help to recover.
//
// Usage: pass a one-line description of what's wrong, then a list of
// worked examples. The output is:
//
// <usage>
//
// examples:
// <ex 1>
// <ex 2>
//
// run "<cmdpath> --help" for the full reference.
func argError(cmd *cobra.Command, usage string, examples ...string) error {
var sb strings.Builder
sb.WriteString(usage)
if len(examples) > 0 {
sb.WriteString("\n\nexamples:")
for _, ex := range examples {
sb.WriteString("\n ")
sb.WriteString(ex)
}
}
if cmd != nil {
sb.WriteString("\n\nrun \"")
sb.WriteString(cmd.CommandPath())
sb.WriteString(" --help\" for the full reference.")
}
return errors.New(sb.String())
}

// exactArgsHelp is cobra.ExactArgs(n) with a useful error message on
// miscount. Pass the same usage + example shape as argError.
func exactArgsHelp(n int, usage string, examples ...string) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if len(args) == n {
return nil
}
return argError(cmd, usage, examples...)
}
}

// rangeArgsHelp is cobra.RangeArgs(min, max) with a useful error message.
func rangeArgsHelp(minArgs, maxArgs int, usage string, examples ...string) cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
if len(args) >= minArgs && len(args) <= maxArgs {
return nil
}
return argError(cmd, usage, examples...)
}
}
9 changes: 8 additions & 1 deletion internal/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ Does NOT touch your SSH keys, GPG keys, or gh CLI authentication —
those aren't gitswitch's to delete. Use this when you've added a
duplicate by accident or want to retire an identity from gitswitch
without nuking the underlying credentials.`,
Args: cobra.ExactArgs(1),
Args: exactArgsHelp(1,
"gitswitch delete requires an identity name",
"gitswitch delete work (prompts before removing)",
"gitswitch delete work -y (skips the confirmation)",
),
Example: " gitswitch delete work\n" +
" gitswitch rm personal -y\n" +
" gitswitch list # see what's configured first",
RunE: func(cmd *cobra.Command, args []string) error {
yes, _ := cmd.Flags().GetBool("yes")
return runDelete(args[0], yes)
Expand Down
3 changes: 2 additions & 1 deletion internal/cmd/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ green line if everything agrees, or a red diff if they disagree.

This is the command you should run any time you suspect "wait, did
my last commit go out as the right person?"`,
RunE: runDoctor,
Example: " gitswitch doctor",
RunE: runDoctor,
}
}

Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ one. Writes the gitswitch config — but does not yet apply anything to

This command is read-only with respect to the rest of your system. The
only file it writes is ~/.config/gitswitch/config.json.`,
Example: " gitswitch init # interactive — prompts to name each identity\n" +
" gitswitch init -y # accept proposed names, no prompts",
RunE: func(_ *cobra.Command, _ []string) error {
return runInit(assumeYes)
},
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ binding currently active. Reads ~/.config/gitswitch/config.json.

This is what you should run before "gitswitch use" if you've
forgotten the names of your identities.`,
Example: " gitswitch list\n" +
" gitswitch ls",
RunE: func(_ *cobra.Command, _ []string) error {
return runList()
},
Expand Down
8 changes: 7 additions & 1 deletion internal/cmd/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ func newRenameCommand() *cobra.Command {
Useful when init auto-named an identity something awkward (e.g. a
lowercased gh login) and you want a more human name. Underlying
SSH keys and gh accounts are untouched.`,
Args: cobra.ExactArgs(2),
Args: exactArgsHelp(2,
"gitswitch rename takes <old-name> <new-name>",
"gitswitch rename ofirhaim1 personal",
"gitswitch mv work-old work # alias",
),
Example: " gitswitch rename ofirhaim1 personal\n" +
" gitswitch mv work-old work-new",
RunE: func(_ *cobra.Command, args []string) error {
return runRename(args[0], args[1])
},
Expand Down
10 changes: 9 additions & 1 deletion internal/cmd/use.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,15 @@ Concretely, "use" does three things:
Re-running "use" with the same identity + directory is a no-op (the
sentinel-marked block is replaced in place). Run with --unbind to
remove the binding.`,
Args: cobra.RangeArgs(1, 2),
Args: rangeArgsHelp(1, 2,
"gitswitch use takes <identity> [<directory>]",
"gitswitch use work ~/work # bind work identity to ~/work",
"gitswitch use personal ~/code",
"gitswitch use work # refresh per-identity config (no binding)",
),
Example: " gitswitch use work ~/work\n" +
" gitswitch use personal ~/code\n" +
" gitswitch use work ~/work --unbind # reverse the binding",
RunE: func(cmd *cobra.Command, args []string) error {
unbind, _ := cmd.Flags().GetBool("unbind")
return runUse(args, unbind)
Expand Down
2 changes: 2 additions & 0 deletions internal/cmd/why.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ included, what user.email resolves to, and whether the layers agree.

The honest counterweight to any "automatic" tool — magic you can't
inspect is just a bug waiting to happen.`,
Example: " cd ~/work && gitswitch why\n" +
" cd ~/personal && gitswitch why",
RunE: func(_ *cobra.Command, _ []string) error {
return runWhy()
},
Expand Down
Loading