Stacked pull requests
The default here. One layer per reviewable change, landed as one atomic merge.
Stacked pull requests are the default. A change that splits into two reviewable pieces becomes two layers, not one large pull request and not two unrelated ones.
The wrapper
npm run stack -- doctor # measure before assuming
npm run stack -- new <slug> # open a stack, or add the next layer
npm run stack -- submit # gates, push, open or update every layer
npm run stack -- view # the stack as GitHub sees it
npm run stack -- sync # cascading rebase onto main, prune merged
npm run stack -- land # merge the whole stack, atomically
npm run stack -- link A B C # retrofit already-open pull requests, bottom to top
npm run stack -- adopt <n> # adopt a server-side stack into local tracking
scripts/stack.sh is deliberately thin. It carries three opinions that differ from
gh stack's defaults:
The procedure
A chain of pull requests is not a stack
Two different things share the word.
| Base-chaining by hand | Native stack | |
|---|---|---|
| What it is | Ordinary pull requests, each based on the branch below |
Server-tracked state |
| Rebase after a merge | Manual cascade, every branch, bottom to top | npm run stack -- sync |
| Merging | One at a time, in order | One atomic operation |
link leaves no local tracking, on purpose
After a successful link the stack is real on GitHub and gh stack view still exits 2 in
your clone. Measured here on 2026-09-01: link 1 2 created stack #3, and the same shell
then reported current branch ... is not part of a stack. link exists for people who
manage branches with another tool, so it writes no local state.
Ask the server instead, and adopt the stack if you want the local commands to work:
gh api repos/akwlabs-ai/akw-factory-floors/pulls/2 --jq .stack
npm run stack -- adopt 3
npm run stack -- doctor prints stack (local) and stack (server) as separate lines for
this reason.
Exit codes
Branch on the code. Do not parse the message.
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | generic error |
| 2 | not in a stack, or stack not found |
| 3 | rebase conflict — resolve, then gh stack rebase --continue |
| 4 | GitHub API failure |
| 5 | invalid arguments or flags |
| 6 | disambiguation required: the branch belongs to more than one stack |
Merging through the API
GraphQL exposes stack and stackEntry on PullRequest as read-only query fields.
Creating or modifying a stack is REST only.
If main ever gains a merge queue
gh stack merge adds the stack to the queue instead of merging it. The queue picks the
merge method, so --squash is ignored with a warning, and the layers can land in separate
groups rather than together.