Stacked Pull Requests: The end of rebase hell
If you want to ship large features faster, without giant pull requests, endless merge conflicts, or rebase hell, GitHub finally built what developers have been asking for: Stacked Pull Requests.
In a hurry? Jump straight to the command cheat sheet ↓.
Audience
- Anyone who already splits big changes into small, dependent PRs and is tired of babysitting the branches
- Anyone wondering what stacked PRs actually add on top of plain branching
The problem with your two current options
Every big feature forces the same decision.
Option one: one giant PR. You open it, and your reviewer magically becomes "busy." Large diffs are slow to review, easy to skim, and more likely to go stale and pick up conflicts.
Option two: small dependent PRs. This is the "correct" advice, and it works right up until the first PR gets a review comment. One tiny change at the bottom, and now you're rebasing every branch above it, one at a time, resolving the same conflicts on each layer. Split into enough branches and you spend your afternoon rebasing instead of building.
For years those were the only two options. The reason option two hurts isn't the small PRs, it's that Git and GitHub treat every branch as independent. You are the only thing that knows they form a chain, so you do all the bookkeeping by hand.
What a stack actually is
A stack is a chain of PRs in the same repository where each PR targets the branch below it instead of main:
┌── feat/frontend → PR #3 (base: feat/api-endpoints) ← top
┌── feat/api-endpoints → PR #2 (base: feat/auth-layer)
┌── feat/auth-layer → PR #1 (base: main) ← bottom
main
Foundational changes (shared types, schema) live at the bottom; the code that depends on them (API routes, UI) lives higher up. Each PR shows only the diff for its own layer. That part you could already do with plain branching. The difference is everything GitHub now does because it knows the branches are connected.
On GitHub, a stacked PR shows a stack map like the one above: every layer, its position, and one-click navigation between them.
Why this beats normal branching with small PRs
This is the whole point, so let me be specific. Small PRs off dependent branches already give you focused diffs. Stacks fix the four things that plain branching leaves on your plate.
1. GitHub does the cascading rebase
This is the big one. Change a lower branch or merge the bottom PR, and GitHub automatically rebases every branch above it so the chain stays consistent — from the PR (server-side) or locally with gh stack rebase. With plain branches, that cascade is a manual loop you run every time the base moves. GitHub even enables git rerere, so conflict fixes are remembered across rebases instead of re-solved on every layer.
2. Branch protection and CI run on every layer
With hand-rolled branches, rules and CI often only fire on the bottom PR that targets main; mid-stack PRs point at other feature branches, so you can't tell if layer 3 really passes the bar. In a stack, GitHub judges every PR against the bottom PR's base (usually main) — so branch protection like CODEOWNER approvals and your default-branch CI apply to every layer. Nothing sneaks in under a weaker gate.
3. Reviewers get the layer and the context
Reviewers still get a small, focused diff, but a stacked PR also shows a stack map: every PR in the chain, its status, and one-click navigation between layers. Reviewing a change in isolation is how bad reviews happen — the stack keeps the small diff without hiding the bigger picture.
4. Merging is bottom-up and automatic
Merge from the bottom up — one PR, a contiguous group, or the whole stack by merging the top. When a lower PR merges, the next one is automatically re-targeted to the base and drops to the bottom, ready to go. The final history matches merging each PR individually, and stacks are merge-queue aware. No re-pointing bases by hand.
Put simply: plain small PRs give you the diffs. Stacks give you the diffs plus the rebasing, the enforcement, the context, and the merge choreography that you were doing manually before.
The commands
Stacked PRs are in public preview. The whole local workflow lives in the gh stack extension for GitHub CLI (needs gh 2.90+ and Git 2.20+). The complete CLI command reference lists every command and flag — below is the subset I found most useful, top to bottom, in the order you'll actually use it.
# ── Setup (once) ────────────────────────────────────────
gh extension install github/gh-stack # add the extension
gh skill install github/gh-stack # optional: let AI agents drive stacks
# ── Build the stack ─────────────────────────────────────
gh stack init # start a stack, names your first branch
git add . && git commit -m "auth layer"
gh stack add -Am "api routes" # -Am = stage + commit + next branch, in one
gh stack add -Am "frontend"
# ── Ship it ─────────────────────────────────────────────
gh stack push # push every branch in the stack
gh stack submit # create + link the PRs on GitHub
gh stack view # see layers, PR links, statuses, latest commit
# ── Keep it in sync ─────────────────────────────────────
gh stack rebase # cascading rebase after main or a lower branch moves
gh stack sync # fetch + rebase + push + sync PR state, in one
# ── Navigate ────────────────────────────────────────────
gh stack up / down # move one layer up (top) or down (trunk)
gh stack top / bottom # jump to either end
# ── Come back / restructure ─────────────────────────────
gh stack checkout <pr|branch|number> # resume a stack by PR #, URL, branch, or stack #
gh stack modify # interactively reorder, insert, or drop layers
# ── Merge ───────────────────────────────────────────────
gh stack merge # merge bottom-up: one PR, a group, or the whole stack
Stacks work from the CLI, the GitHub website, GitHub Mobile, and programmatically via Webhooks, the REST API, and GraphQL. A couple of limits worth knowing: all branches must live in the same repository (no cross-fork stacks), GitHub Desktop isn't supported, and auto-merge isn't supported for stacked PRs.
The takeaway
Small, dependent PRs were always the right idea. What made them painful was doing all the connective work yourself: the rebasing, checking that each layer really passed CI, keeping reviewers oriented, and re-pointing bases at merge time. Stacked Pull Requests keep the small diffs and hand that connective work to GitHub, because it finally understands your PRs are a chain and not a pile.
Try it on your next big feature. Until the next blog, Chau.