Skip to content

Commit 0491058

Browse files
committed
improved root help
1 parent 0f53bd4 commit 0491058

3 files changed

Lines changed: 184 additions & 80 deletions

File tree

cmd/root.go

Lines changed: 112 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,25 @@ func RootCmd() *cobra.Command {
1313
cfg := config.New()
1414

1515
root := &cobra.Command{
16-
Use: "stack <command>",
17-
Short: "Manage stacked branches and pull requests",
18-
Long: "Create, navigate, and manage stacks of branches and pull requests.",
16+
Use: "stack <command>",
17+
Short: "Manage stacked branches and pull requests",
18+
Long: `Stacked PRs let you break a large change into a chain of pull requests
19+
that build on each other. Use ` + "`gh stack`" + ` to create and manage your stack
20+
locally, then push to GitHub to create your stack of PRs.`,
21+
Example: ` # Start a new stack targeting your default branch
22+
$ gh stack init
23+
24+
# Or turn an existing set of branches into a stack
25+
$ gh stack init --adopt branch1 branch2 branch3
26+
27+
# Make changes and commit, then add a branch to the stack
28+
$ gh stack add branch4
29+
30+
# Push all branches and create/update PRs on GitHub
31+
$ gh stack submit
32+
33+
# Keep your local in sync with remote
34+
$ gh stack sync`,
1935
Version: Version,
2036
SilenceUsage: true,
2137
SilenceErrors: true,
@@ -26,36 +42,104 @@ func RootCmd() *cobra.Command {
2642
root.SetOut(cfg.Out)
2743
root.SetErr(cfg.Err)
2844

29-
// Local operations
30-
root.AddCommand(InitCmd(cfg))
31-
root.AddCommand(AddCmd(cfg))
45+
root.AddGroup(
46+
&cobra.Group{ID: "stack", Title: "Stack management:"},
47+
&cobra.Group{ID: "remote", Title: "Remote operations:"},
48+
&cobra.Group{ID: "nav", Title: "Navigation:"},
49+
&cobra.Group{ID: "utils", Title: "Utilities:"},
50+
)
51+
52+
defaultHelp := root.HelpFunc()
53+
root.SetHelpFunc(func(cmd *cobra.Command, args []string) {
54+
defaultHelp(cmd, args)
55+
if cmd.Name() == "stack" {
56+
out := cmd.OutOrStderr()
57+
fmt.Fprintln(out)
58+
fmt.Fprintln(out, "Learn more:")
59+
fmt.Fprintln(out, " Documentation: https://gh.io/stacks")
60+
fmt.Fprintln(out, " Feedback: https://gh.io/stacks-feedback")
61+
}
62+
})
3263

33-
// Remote operations
34-
root.AddCommand(CheckoutCmd(cfg))
35-
root.AddCommand(PushCmd(cfg))
36-
root.AddCommand(SubmitCmd(cfg))
37-
root.AddCommand(SyncCmd(cfg))
38-
root.AddCommand(UnstackCmd(cfg))
39-
root.AddCommand(MergeCmd(cfg))
40-
root.AddCommand(LinkCmd(cfg))
64+
// Stack management commands
65+
initCmd := InitCmd(cfg)
66+
initCmd.GroupID = "stack"
67+
root.AddCommand(initCmd)
4168

42-
// Helper commands
43-
root.AddCommand(ViewCmd(cfg))
44-
root.AddCommand(RebaseCmd(cfg))
45-
root.AddCommand(ModifyCmd(cfg))
69+
addCmd := AddCmd(cfg)
70+
addCmd.GroupID = "stack"
71+
root.AddCommand(addCmd)
4672

47-
// Navigation commands
48-
root.AddCommand(UpCmd(cfg))
49-
root.AddCommand(DownCmd(cfg))
50-
root.AddCommand(TopCmd(cfg))
51-
root.AddCommand(BottomCmd(cfg))
52-
root.AddCommand(SwitchCmd(cfg))
73+
viewCmd := ViewCmd(cfg)
74+
viewCmd.GroupID = "stack"
75+
root.AddCommand(viewCmd)
76+
77+
checkoutCmd := CheckoutCmd(cfg)
78+
checkoutCmd.GroupID = "stack"
79+
root.AddCommand(checkoutCmd)
80+
81+
modifyCmd := ModifyCmd(cfg)
82+
modifyCmd.GroupID = "stack"
83+
root.AddCommand(modifyCmd)
5384

54-
// Alias
55-
root.AddCommand(AliasCmd(cfg))
85+
unstackCmd := UnstackCmd(cfg)
86+
unstackCmd.GroupID = "stack"
87+
root.AddCommand(unstackCmd)
5688

57-
// Feedback
58-
root.AddCommand(FeedbackCmd(cfg))
89+
// Remote operations commands
90+
submitCmd := SubmitCmd(cfg)
91+
submitCmd.GroupID = "remote"
92+
root.AddCommand(submitCmd)
93+
94+
syncCmd := SyncCmd(cfg)
95+
syncCmd.GroupID = "remote"
96+
root.AddCommand(syncCmd)
97+
98+
rebaseCmd := RebaseCmd(cfg)
99+
rebaseCmd.GroupID = "remote"
100+
root.AddCommand(rebaseCmd)
101+
102+
pushCmd := PushCmd(cfg)
103+
pushCmd.GroupID = "remote"
104+
root.AddCommand(pushCmd)
105+
106+
linkCmd := LinkCmd(cfg)
107+
linkCmd.GroupID = "remote"
108+
root.AddCommand(linkCmd)
109+
110+
mergeCmd := MergeCmd(cfg)
111+
mergeCmd.GroupID = "remote"
112+
root.AddCommand(mergeCmd)
113+
114+
// Navigation commands
115+
switchCmd := SwitchCmd(cfg)
116+
switchCmd.GroupID = "nav"
117+
root.AddCommand(switchCmd)
118+
119+
upCmd := UpCmd(cfg)
120+
upCmd.GroupID = "nav"
121+
root.AddCommand(upCmd)
122+
123+
downCmd := DownCmd(cfg)
124+
downCmd.GroupID = "nav"
125+
root.AddCommand(downCmd)
126+
127+
topCmd := TopCmd(cfg)
128+
topCmd.GroupID = "nav"
129+
root.AddCommand(topCmd)
130+
131+
bottomCmd := BottomCmd(cfg)
132+
bottomCmd.GroupID = "nav"
133+
root.AddCommand(bottomCmd)
134+
135+
// Utility commands
136+
aliasCmd := AliasCmd(cfg)
137+
aliasCmd.GroupID = "utils"
138+
root.AddCommand(aliasCmd)
139+
140+
feedbackCmd := FeedbackCmd(cfg)
141+
feedbackCmd.GroupID = "utils"
142+
root.AddCommand(feedbackCmd)
59143

60144
return root
61145
}

cmd/root_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package cmd
22

33
import (
4+
"bytes"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
79
)
810

911
func TestRootCmd_SubcommandRegistration(t *testing.T) {
@@ -19,3 +21,21 @@ func TestRootCmd_SubcommandRegistration(t *testing.T) {
1921
assert.True(t, registered[name], "expected subcommand %q to be registered", name)
2022
}
2123
}
24+
25+
func TestRootCmd_HelpOutput(t *testing.T) {
26+
root := RootCmd()
27+
var stdout bytes.Buffer
28+
var stderr bytes.Buffer
29+
root.SetOut(&stdout)
30+
root.SetErr(&stderr)
31+
root.SetArgs([]string{"--help"})
32+
33+
err := root.Execute()
34+
require.NoError(t, err)
35+
36+
output := stdout.String() + stderr.String()
37+
assert.Contains(t, output, "Stacked PRs")
38+
assert.Contains(t, output, "Stack management:")
39+
assert.Contains(t, output, "Learn more:")
40+
assert.Contains(t, output, "https://gh.io/stacks")
41+
}

docs/src/content/docs/reference/cli.md

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,34 @@ gh stack modify --continue
223223
gh stack modify --abort
224224
```
225225

226+
### `gh stack unstack`
227+
228+
Remove a stack from local tracking and delete it on GitHub. Also available as `gh stack delete`.
229+
230+
```sh
231+
gh stack unstack [flags]
232+
```
233+
234+
You must have a branch from the stack checked out locally. The command targets the active stack — the one that contains the currently checked out branch.
235+
236+
Deletes the stack on GitHub first, if it exists, then removes it from local tracking. If the remote deletion fails, the local state is left untouched so you can retry. Use `--local` to skip the remote deletion and only remove local tracking.
237+
238+
This is useful when you need to restructure a stack — remove a branch, reorder branches, rename branches, or make other large changes. After unstacking, use `gh stack init --adopt` to re-create the stack with the desired structure.
239+
240+
| Flag | Description |
241+
|------|-------------|
242+
| `--local` | Only delete the stack locally (keep it on GitHub) |
243+
244+
**Examples:**
245+
246+
```sh
247+
# Delete the stack on GitHub and remove local tracking
248+
gh stack unstack
249+
250+
# Only remove local tracking
251+
gh stack unstack --local
252+
```
253+
226254
---
227255

228256
## Remote Operations
@@ -345,34 +373,6 @@ gh stack push
345373
gh stack push --remote upstream
346374
```
347375

348-
### `gh stack unstack`
349-
350-
Remove a stack from local tracking and delete it on GitHub. Also available as `gh stack delete`.
351-
352-
```sh
353-
gh stack unstack [flags]
354-
```
355-
356-
You must have a branch from the stack checked out locally. The command targets the active stack — the one that contains the currently checked out branch.
357-
358-
Deletes the stack on GitHub first, if it exists, then removes it from local tracking. If the remote deletion fails, the local state is left untouched so you can retry. Use `--local` to skip the remote deletion and only remove local tracking.
359-
360-
This is useful when you need to restructure a stack — remove a branch, reorder branches, rename branches, or make other large changes. After unstacking, use `gh stack init --adopt` to re-create the stack with the desired structure.
361-
362-
| Flag | Description |
363-
|------|-------------|
364-
| `--local` | Only delete the stack locally (keep it on GitHub) |
365-
366-
**Examples:**
367-
368-
```sh
369-
# Delete the stack on GitHub and remove local tracking
370-
gh stack unstack
371-
372-
# Only remove local tracking
373-
gh stack unstack --local
374-
```
375-
376376
### `gh stack link`
377377

378378
Link PRs into a stack on GitHub without local tracking.
@@ -417,6 +417,30 @@ Move between branches in the current stack without having to remember branch nam
417417

418418
All navigation commands clamp to the bounds of the stack — moving up from the top or down from the bottom is a no-op with a message.
419419

420+
### `gh stack switch`
421+
422+
Interactively switch to another branch in the stack.
423+
424+
```sh
425+
gh stack switch
426+
```
427+
428+
Shows an interactive picker listing all branches in the current stack, ordered from top (furthest from trunk) to bottom (closest to trunk) with their position number. Select a branch to check it out.
429+
430+
Requires an interactive terminal.
431+
432+
**Examples:**
433+
434+
```sh
435+
gh stack switch
436+
# → Select a branch in the stack to switch to
437+
# 5. frontend
438+
# 4. api-endpoints
439+
# 3. auth-layer
440+
# 2. db-schema
441+
# 1. config-setup
442+
```
443+
420444
### `gh stack up`
421445

422446
Move up toward the top of the stack (away from trunk).
@@ -471,30 +495,6 @@ gh stack bottom
471495

472496
Checks out the branch closest to the trunk.
473497

474-
### `gh stack switch`
475-
476-
Interactively switch to another branch in the stack.
477-
478-
```sh
479-
gh stack switch
480-
```
481-
482-
Shows an interactive picker listing all branches in the current stack, ordered from top (furthest from trunk) to bottom (closest to trunk) with their position number. Select a branch to check it out.
483-
484-
Requires an interactive terminal.
485-
486-
**Examples:**
487-
488-
```sh
489-
gh stack switch
490-
# → Select a branch in the stack to switch to
491-
# 5. frontend
492-
# 4. api-endpoints
493-
# 3. auth-layer
494-
# 2. db-schema
495-
# 1. config-setup
496-
```
497-
498498
---
499499

500500
## Utilities

0 commit comments

Comments
 (0)