Skip to content

Commit e0b74b1

Browse files
committed
merge cmd docs
1 parent 9351ca8 commit e0b74b1

7 files changed

Lines changed: 127 additions & 8 deletions

File tree

.github/copilot-instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ No Makefile, no code generation, no external linter config. Standard Go toolchai
1717

1818
- `cmd/`: One Cobra command per file. Each exports `<Name>Cmd(cfg *config.Config)` with logic in `run<Name>()`.
1919
- `internal/git/`: `Ops` interface (52 methods) wrapping git CLI. `MockOps` for tests. Package-level functions delegate to swappable `ops` variable.
20-
- `internal/github/`: `ClientOps` interface (13 methods) for GitHub API. `MockClient` for tests. Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`).
20+
- `internal/github/`: `ClientOps` interface (18 methods) for GitHub API. `MockClient` for tests. Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`); merges use the async merge API (`/repos/{owner}/{repo}/pulls/{n}/merge-async`), with `BaseBranchPolicy` (GraphQL) gating merge-queue branches.
2121
- `internal/config/`: `Config` struct passed to all commands. Holds I/O, colors, and test hooks (`SelectFn`, `ConfirmFn`, `InputFn`, `GitHubClientOverride`).
2222
- `internal/stack/`: Stack file (`.git/gh-stack`, JSON) management with file locking.
2323
- `internal/tui/`: bubbletea views (`stackview`, `modifyview`).

AGENTS.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal/
3535
gitops.go # Ops interface (52 methods)
3636
mock_ops.go # MockOps. Each method has a corresponding *Fn field.
3737
github/ # github.ClientOps interface + real Client
38-
client_interface.go # ClientOps interface (13 methods)
38+
client_interface.go # ClientOps interface (18 methods)
3939
mock_client.go # MockClient. Uses function-pointer fields for testing.
4040
stack/ # stack file (.git/gh-stack) management, JSON schema, locking
4141
schema.json # JSON Schema for the stack file format
@@ -57,7 +57,7 @@ skills/ # AI agent skill definition (SKILL.md)
5757
| Group | Commands |
5858
|-------|----------|
5959
| Stack management | `init`, `add`, `view`, `checkout`, `modify`, `unstack` |
60-
| Remote operations | `submit`, `sync`, `rebase`, `push`, `link` |
60+
| Remote operations | `submit`, `sync`, `rebase`, `push`, `link`, `merge` |
6161
| Navigation | `switch`, `up`, `down`, `top`, `bottom`, `trunk` |
6262
| Utilities | `alias`, `feedback` |
6363

@@ -109,7 +109,7 @@ if errors.As(err, &exitErr) { ... }
109109
### Key interfaces
110110

111111
- **`git.Ops`** (`internal/git/gitops.go`): 52 methods wrapping git CLI calls. The production implementation uses `cli/go-gh`'s `client.Command()` via `run()` and `runSilent()` helpers. Package-level functions (e.g., `git.CurrentBranch()`) delegate to a swappable package-level `ops` variable.
112-
- **`github.ClientOps`** (`internal/github/client_interface.go`): 13 methods for GitHub API (PRs, stacks). Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`): `ListStacks`, `FindStackForPR`, `GetStack`, `CreateStack`, `AddToStack` (delta append), `Unstack`. Injected via `cfg.GitHubClientOverride` in tests.
112+
- **`github.ClientOps`** (`internal/github/client_interface.go`): 18 methods for GitHub API (PRs, stacks, merges). Stack operations use the public Stacks REST API (`/repos/{owner}/{repo}/stacks`): `ListStacks`, `FindStackForPR`, `GetStack`, `CreateStack`, `AddToStack` (delta append), `Unstack`. Async stack merges use `RepoMergeConfig` (GraphQL allowed methods + viewer default), `MergeStackAsync`, and `GetAsyncMergeResult` (`/repos/{owner}/{repo}/pulls/{n}/merge-async`); `BaseBranchPolicy` (GraphQL) reports whether the base branch requires a merge queue (unsupported). Injected via `cfg.GitHubClientOverride` in tests.
113113
- **`config.Config`** (`internal/config/config.go`): Central configuration passed to all commands. Holds I/O streams, color functions, and test hook fields (`SelectFn`, `ConfirmFn`, `InputFn`, `RepoOverride`).
114114

115115
### Stack file

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,46 @@ gh stack link 42 43 feature-auth feature-ui
439439
gh stack link --base develop --open feat-a feat-b feat-c
440440
```
441441

442+
### `gh stack merge`
443+
444+
Merge one or multiple stacked PRs at once.
445+
446+
```
447+
gh stack merge [<stack-number> | <pr-number>]
448+
```
449+
450+
All members of the stack up to and including your chosen pull request are merged into the base branch in a single, all-or-nothing operation: if any PR can't be merged, none are.
451+
452+
With no argument, the current active local stack is used. Pass a stack number to merge a stack you don't have checked out (a purely remote operation), or a pull request number to merge directly up to that PR.
453+
454+
In an interactive terminal, a short wizard walks you through three steps — choose which PRs to merge, pick the merge method, and confirm. In a non-interactive terminal, or with `--yes`, the whole stack (or everything up to the given PR) is merged without prompting, using your last-used merge method unless one is specified.
455+
456+
Only basic pull request state is checked before merging (open and not a draft); GitHub evaluates branch protection and repository rules when the merge runs, so any such failure is reported back to you. **Admin bypass is not supported** for stacked PR merges at this time.
457+
458+
Note that this command does not work with merge queues. If the stack's base branch uses a merge queue, use `gh pr merge` instead.
459+
460+
| Flag | Description |
461+
|------|-------------|
462+
| `--merge-method <method>` | Merge method to use: `merge`, `squash`, or `rebase` |
463+
| `--merge` / `--squash` / `--rebase` | Shorthands for the corresponding merge method |
464+
| `-y, --yes` | Merge without prompting for confirmation |
465+
466+
**Examples:**
467+
468+
```sh
469+
# Merge the current stack (interactive picker)
470+
gh stack merge
471+
472+
# Merge a stack you don't have checked out, by stack number
473+
gh stack merge 7
474+
475+
# Merge everything up to and including PR #42
476+
gh stack merge 42
477+
478+
# Merge the whole current stack without prompting, squashing
479+
gh stack merge --yes --squash
480+
```
481+
442482
### `gh stack view`
443483

444484
View the current stack.

cmd/merge.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ prompting, using your last-used merge method unless one is specified.
6363
6464
Only basic pull request state is checked before merging (open and not a draft);
6565
GitHub evaluates branch protection and repository rules when the merge runs, so
66-
any such failure is reported back to you.`,
66+
any such failure is reported back to you.
67+
68+
If the base branch uses a merge queue, this command isn't supported (it merges
69+
directly, not through the queue); use "gh pr merge" or the web UI instead.`,
6770
Example: ` # Merge the current stack (interactive picker)
6871
$ gh stack merge
6972
@@ -501,7 +504,7 @@ func warnAsyncMergeUnavailable(cfg *config.Config) {
501504

502505
// explainMergeQueueUnsupported reports that the stack's base branch merges
503506
// through a merge queue, which the async stack merge cannot use, and points the
504-
// user to the web UI.
507+
// user to the merge queue (via `gh pr merge` or the web UI) instead.
505508
func explainMergeQueueUnsupported(cfg *config.Config, base string) error {
506509
cfg.Errorf("the base branch %q requires a merge queue, which \"gh stack merge\" does not support", base)
507510
cfg.Printf("Merge this stack using `%q` or from the GitHub web UI instead.", "gh pr merge")

docs/src/content/docs/guides/workflows.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ gh stack rebase
3333
# 7. Push the updated branches
3434
gh stack push
3535

36-
# 8. Sync upstream changes as PRs get merged
36+
# 8. Land the stack once it's approved (merges bottom to top, atomically)
37+
gh stack merge
38+
39+
# 9. Sync upstream changes as PRs get merged
3740
gh stack sync
3841
```
3942

@@ -120,6 +123,32 @@ gh stack push
120123

121124
The rebase ensures all branches above the changed one pick up the fixes. `gh stack push` uses `--force-with-lease` to safely update the rebased branches.
122125

126+
## Merging Your Stack
127+
128+
When your stack is approved, land it with `gh stack merge`. Regular `gh pr merge` doesn't work with stacked PRs — `gh stack merge` uses GitHub's atomic stack merge, which merges every PR up to and including your chosen one in a single, all-or-nothing operation. If any PR can't be merged, none are.
129+
130+
```sh
131+
# Merge the current stack (interactive picker for how far up to merge)
132+
gh stack merge
133+
134+
# Merge everything up to and including a specific PR
135+
gh stack merge 42
136+
137+
# Merge a stack you don't have checked out, by its stack number
138+
gh stack merge 7
139+
140+
# Merge without prompting for confirmation, specifying the merge method
141+
gh stack merge --yes --squash
142+
```
143+
144+
In an interactive terminal, a short wizard lets you choose how far up the stack to merge, pick the merge method (only the ones your repository allows, defaulting to your last-used method), and confirm — then shows live progress. In a non-interactive terminal, or with `--yes`, the whole stack (or everything up to the given PR) is merged without prompting. After merging, run `gh stack sync` to update your local branches.
145+
146+
The exception is a trunk that uses a merge queue: `gh stack merge` merges directly rather than through the queue, so it isn't supported there. Use `gh pr merge` or the GitHub web UI to merge through the queue instead.
147+
148+
:::note[Admin bypass not supported]
149+
Stack merges currently do not support admin bypass merging.
150+
:::
151+
123152
## Syncing After Merges
124153

125154
When a PR at the bottom of the stack is merged on GitHub, use `gh stack sync` to update your local state:

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,48 @@ gh stack link --base develop --open feat-a feat-b feat-c
460460

461461
---
462462

463+
### `gh stack merge`
464+
465+
Merge one or multiple stacked PRs at once.
466+
467+
```sh
468+
gh stack merge [<stack-number> | <pr-number>]
469+
```
470+
471+
All members of the stack up to and including your chosen pull request are merged into the base branch in a single, all-or-nothing operation: if any PR can't be merged, none are.
472+
473+
With no argument, the current active local stack is used. Pass a stack number to merge a stack you don't have checked out (a purely remote operation), or a pull request number to merge directly up to that PR.
474+
475+
In an interactive terminal, a short wizard walks you through three steps — choose which PRs to merge, pick the merge method, and confirm. In a non-interactive terminal, or with `--yes`, the whole stack (or everything up to the given PR) is merged without prompting, using your last-used merge method unless one is specified.
476+
477+
Only basic pull request state is checked before merging (open and not a draft); GitHub evaluates branch protection and repository rules when the merge runs, so any such failure is reported back to you. **Admin bypass is not supported** for stacked PR merges at this time.
478+
479+
Note that this command does not work with merge queues. If the stack's base branch uses a merge queue, use `gh pr merge` instead.
480+
481+
| Flag | Description |
482+
|------|-------------|
483+
| `--merge-method <method>` | Merge method to use: `merge`, `squash`, or `rebase` |
484+
| `--merge` / `--squash` / `--rebase` | Shorthands for the corresponding merge method |
485+
| `-y, --yes` | Merge without prompting for confirmation |
486+
487+
**Examples:**
488+
489+
```sh
490+
# Merge the current stack (interactive picker)
491+
gh stack merge
492+
493+
# Merge a stack you don't have checked out, by stack number
494+
gh stack merge 7
495+
496+
# Merge everything up to and including PR #42
497+
gh stack merge 42
498+
499+
# Merge the whole current stack without prompting, squashing
500+
gh stack merge --yes --squash
501+
```
502+
503+
---
504+
463505
## Navigation
464506

465507
Move between branches in the current stack without having to remember branch names. The **bottom** of the stack is the branch closest to the trunk, and the **top** is furthest from it. `up` moves away from trunk; `down` moves toward it.

skills/gh-stack/SKILL.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ description: >
77
branch chains, or incremental code review workflows.
88
metadata:
99
author: github
10-
version: "0.0.8"
10+
version: "0.0.9"
1111
---
1212

1313
# gh-stack
@@ -61,6 +61,7 @@ git config remote.pushDefault origin # if multiple remotes exist (skips remo
6161
7. **Use standard `git add` and `git commit` for staging and committing.** This gives you full control over which changes go into each branch. The `-Am` shortcut is available but should not be the default approach—stacked PRs are most effective when each branch contains a deliberate, logical set of changes.
6262
8. **Navigate down the stack when you need to change a lower layer.** If you're working on a frontend branch and realize you need API changes, don't hack around it at the current layer. Navigate to the appropriate branch (`gh stack down`, `gh stack checkout`, or `gh stack bottom`), make and commit the changes there, run `gh stack rebase --upstack`, then navigate back up to continue.
6363
9. **Use `gh stack link` for external tool workflows.** When branches are managed by an external tool (jj, Sapling, etc.), use `gh stack link branch-a branch-b`. `link` does not rely on local tracking state and is intended for API-driven PR and stack management. Provide at least two branches/PRs to create or update a stack, or a stack number followed by the new branches/PRs to append them to the top of an existing stack (e.g. `gh stack link 7 branch-c`).
64+
10. **Use `gh stack merge --yes` to merge stacked PRs.** `gh pr merge` does not work with stacked PRs. In a non-interactive terminal `gh stack merge` runs without prompting and merges the entire stack (bottom to top) atomically; pass `--yes` to be explicit. Scope the merge by passing a pull request number (`gh stack merge 42 --yes` merges everything up to and including PR #42) or a stack number (`gh stack merge 7 --yes`, which needs no local checkout). Choose the method with `--squash`, `--rebase`, `--merge`, or `--merge-method <method>`; without one, the last-used method is used. The merge is all-or-nothing — if any PR can't be merged, none are, and the failure reason is reported. Only basic pull request state is checked before merging (open and not a draft); admin bypass is not supported for stacked PR merges. This command does not work with merge queues — if the base branch uses a merge queue, the command exits with an error, so use `gh pr merge` instead.
6465

6566
**Never do any of the following — each triggers an interactive prompt or TUI that will hang:**
6667
-`gh stack view` or `gh stack view --short` — always use `gh stack view --json`
@@ -164,6 +165,10 @@ Small, incidental fixes (e.g., fixing a typo you noticed) can go in the current
164165
| Check out by branch (local only) | `gh stack checkout feature-auth` |
165166
| Tear down the current stack to restructure it | `gh stack unstack` |
166167
| Tear down a specific stack by number | `gh stack unstack 7` |
168+
| Merge the whole current stack | `gh stack merge --yes` |
169+
| Merge a stack by number | `gh stack merge 7 --yes` |
170+
| Merge up to a specific PR | `gh stack merge 42 --yes` |
171+
| Merge with a specific method | `gh stack merge --yes --squash` |
167172

168173
---
169174

0 commit comments

Comments
 (0)