gh stack
gh stack is a GitHub CLI ⧉ extension for managing a PR stack.
It opens one PR per branch of the stack, similar in spirit to stack-pr but driven through the gh tool.
Cheat sheet
| To | Run |
|---|---|
| Open PRs for the stack, as drafts | gh stack submit --auto |
| Open PRs for the stack, choosing titles and draft state | gh stack submit |
| Update the PRs after changing branches | gh stack push |
Pick up changes on main |
gh stack rebase, then gh stack push |
| Update the branches above one you changed | gh stack rebase --upstack from that branch, then gh stack push |
| Carry on after resolving rebase conflicts | Stage the files, then gh stack rebase --continue |
| See the stack's branches and their PRs | gh stack view |
Usage
Each PR in a stack has its own local branch, and each branch builds on the one below it. The commands below are the ones you need to get those branches onto GitHub as PRs and keep them up to date.
Opening the PRs with submit
gh stack submit pushes every branch of the stack to GitHub and opens a PR for each branch that does not have one yet.
It also points each PR at the branch below it, so every PR only shows its own changes, and groups the PRs into a stack
on GitHub.
Before opening the PRs, it shows a screen listing the new PRs, where you can edit each one's title and description and untick any you do not want yet. Each PR there has a "CREATE AS" toggle that decides whether it is opened as a draft or as ready for review. The default is ready for review.
To skip that screen, pass --auto. The PRs then get generated titles and are opened as drafts, unless you also pass
--open. Usually you'll want to run gh stack submit --auto initially.
Updating the PRs with push
Once the PRs exist, gh stack push is all you need after changing the branches. It only force-pushes the branches and
leaves the PRs themselves alone.
Running submit again instead is also fine. It changes nothing about existing PRs, except that --open marks them all
ready for review. But it opens a PR for every branch that has none, so untick any branch you added that is not ready
yet.
Keeping the branches on top of each other with rebase
Changing a branch does not change the branches above it. They still build on the old version of it until you rebase them. Until then, the PR directly above shows "This stack has conflicts that must be resolved" on GitHub.
gh stack rebase fetches the latest main and rebases the whole stack onto it, branch by branch. It does this no
matter which of the stack's branches you are on. Use it to pick up changes on main.
gh stack rebase --upstack skips main and the branches below you, and rebases only your current branch and the ones
above it. Use it after changing a branch in the middle of the stack, and run it from that branch, not from the top of
the stack.
If a rebase stops on conflicts, resolve them, stage the files and run gh stack rebase --continue. Then run
gh stack push to update the PRs.
Limitations
Local main must match origin/main
gh stack works out what each branch adds by comparing the bottom branch against your local main, not the one on
GitHub. If you made the stack's commits on main before moving them onto their own branches, the bottom branch looks
like it adds nothing. Its PR is then titled after the branch name, and gh stack view, which lists the stack's branches
and their PRs, marks it as needing a rebase. Reset your local main to origin/main before running gh stack submit.
Stacking a stack on top of another stack
gh stack cannot always stack one stack directly on top of another.
If the base of your stack is the tip of another stack, the tool cannot tell where one stack ends and the next begins.
Consider two stacks where second-stack-1 is meant to build on first-stack-3:
* second-stack-3
* second-stack-2
* second-stack-1 first-stack-3
* first-stack-2
* first-stack-1
* main
You have two ways around this:
- Re-create one of the stacks.
- Keep two branches pointing at the same commit, so each stack has its own base to track.
This does not block day-to-day use.
Both workarounds are cheap. The limitation only affects the rarer case of stacking a stack on top of another.