How to Use Stacked Pull Requests on GitHub
GitHub shipped native stacked pull requests into public preview on July 30, 2026. Here's how the gh stack workflow actually works, where stacking pays off, how to pick layer boundaries, and when a stack is the wrong tool.
Key takeaways
- A stack is a chain of pull requests where each one targets the branch below it instead of main. Reviewers see only the diff for their layer, so a 1,400-line change becomes four PRs a human can actually hold in their head.
- Native support landed in public preview on July 30, 2026. Install it with `gh extension install github/gh-stack`, then `gh stack init`, `gh stack add`, `gh stack submit`. GitHub handles the cascading rebase and the retargeting that used to be the whole cost of stacking.
- The skill isn't the CLI, it's the layer boundary. Each layer should be independently revertible and independently correct: schema before the code that reads it, the mechanical rename apart from the judgment call it enables.
- Branch protection and CODEOWNER approvals are enforced on every layer, not just the bottom one, and CI configured for your default branch runs on all of them. Cross-fork stacks are not supported, so open-source drive-by contributions are out.
Summary
TL;DR: Stacked pull requests split one large change into an ordered chain of small PRs, each targeting the branch below it, so reviewers get four focused diffs instead of one wall of code. GitHub made this native in public preview on July 30, 2026 via the gh stack CLI extension and a stack map in the PR UI, which removes the rebase-and-retarget bookkeeping that made stacking a power-user habit. This walks through the commands, the failure modes, six use cases where stacking earns its cost, and the cases where it doesn't.
You've been there. A teammate opens a pull request titled "Add billing exports," and it's 1,400 lines across 38 files. A migration, a new service class, three API handlers, a React table, and a rename that touched everything else on the way through. You skim it for twenty minutes, leave two comments about naming, and approve it, because the alternative is spending your afternoon on it and you both know the sprint ends Friday.
Nobody reviewed that. A rubber stamp got applied to it.
The author knows it too, and they didn't want it this way. They wanted to ship the migration on Monday and get feedback early, but the migration alone did nothing on its own, and the service layer needed the migration, and the API needed the service, and by the time anything was demonstrable it was Thursday and it was 1,400 lines.
Stacked pull requests are the fix for exactly that shape of problem, and as of yesterday they're a native GitHub feature rather than a habit you had to buy a third-party tool to sustain.
What a stack actually is
A stack is a series of pull requests in one repository where each PR targets the branch of the PR below it, forming an ordered chain that lands on a single branch. The bottom PR targets your trunk, usually main. Everything above targets the layer beneath it.
main
└── 1/schema PR #101 → main [layer 1]
└── 2/service PR #102 → 1/schema [layer 2]
└── 3/api PR #103 → 2/service [layer 3]
└── 4/ui PR #104 → 3/api [layer 4]
The consequence that matters: each PR shows only the diff for its layer. The reviewer on PR #103 sees three API handlers and their tests, not the migration, not the React table. They can review it properly in fifteen minutes, because fifteen minutes is genuinely enough for three handlers.
The second consequence: you don't stop working. Layer 1 goes up for review Monday morning, and you start layer 2 on top of it Monday afternoon instead of sitting on your hands or, worse, starting layer 2 on main and dealing with the merge later.
This is not a new idea. It's how Meta's Phabricator worked, it's how Sapling works, and it's what Graphite has sold to teams for years. What's new is that you no longer need any of those to do it.
Why this was annoying until yesterday
You could always build a stack manually. Git doesn't care, and GitHub has always let you set a PR's base to any branch in the repo. The problem was never creating the stack. It was maintaining it.
Say a reviewer asks for a change on layer 1. You fix it, amend, and force-push. Now layer 2's branch points at the old layer 1 commit, so its PR diff suddenly shows your own layer 1 changes as if you'd made them twice. So you rebase layer 2 onto the new layer 1, then layer 3 onto the new layer 2, then layer 4. Four force-pushes, and if there's a conflict you resolve the same conflict at each level. Then layer 1 merges and you retarget layer 2's base to main by hand in the GitHub UI, and main has moved anyway so you rebase the whole thing again.
Do that twice and you understand why most engineers who tried stacking went back to the 1,400-line PR. The workflow was correct and the bookkeeping ate the benefit.
That bookkeeping is what GitHub automated. The public preview announcement went out on July 30, 2026, following a private preview in April. GitHub now performs the cascading rebase for you, retargets PRs automatically when a lower layer merges, and renders a stack map at the top of every PR in the chain so a reviewer can see where their layer sits.
Setting it up
You need gh 2.90.0 or later and Git 2.20 or later.
gh --version
gh auth login # if you haven't
gh extension install github/gh-stack
That's the whole install. If you drive your repo with coding agents, there's a companion skill so the agent knows the commands rather than guessing at raw git:
gh skill install github/gh-stack
Typing gh stack fifty times a day gets old, so set the alias up front. It defaults to gs:
gh stack alias
I'll use the full form below for clarity.
Building your first stack
Here's the billing exports feature from the intro, done properly. Four layers, each one a thing a person can review over a coffee.
Start from an up-to-date trunk and initialize:
git checkout main && git pull
gh stack init
init creates the tracking entry and asks you to name the first branch. The stack's trunk defaults to the repository's default branch; pass -b if you're stacking onto a release branch instead:
gh stack init -b release/2026.8
One thing init quietly does is enable git rerere. That's the git feature that records how you resolved a conflict and replays the resolution automatically next time the same conflict appears. In a stack you hit the same conflict once per layer, so rerere is the difference between resolving a conflict four times and resolving it once. It's the right default and you should leave it on.
Now write layer 1, the migration and nothing else:
# ... write the migration ...
git add .
git commit -m "billing: add exports table and indexes"
Layer 2 goes on top. gh stack add creates the next branch above the current one:
gh stack add 2/billing-export-service
# ... write the service class and its tests ...
git add .
git commit -m "billing: BillingExportService with CSV serialization"
There's a shorthand that stages, commits, and creates the next branch in one move, which is what you'll actually use once it's in your fingers:
gh stack add -Am "billing: export API handlers"
It lands on the current branch if that branch has no commits yet, and creates a new branch if it does, so you can just keep typing it as you go. Note that add has to run from the topmost branch of the stack. If you're sitting in the middle, gh stack top first.
Two more layers and the stack is built. Push the branches and open the PRs:
gh stack push
gh stack submit
submit creates or updates every PR with the correct base: layer 1 targets main, layer 2 targets layer 1, and so on. It prompts for a title per new branch, so pass --auto when you want it to take the commit subjects and get out of your way. Add --open to have it open the PRs in your browser.
Check your work:
gh stack view
You get every branch, its PR link, its status, and its recent commits. --short for the compact version, --json when you want to pipe it somewhere.
Moving around and fixing things
Navigation is the part that makes a stack feel like one object instead of four branches you have to remember the names of. Up moves away from trunk, down moves toward it:
gh stack down 2 # two layers toward main
gh stack up # one layer away from main
gh stack bottom # first layer
gh stack top # last layer
gh stack trunk # back to main
gh stack switch # interactive picker
You can also jump into a stack from a review context by PR number, PR URL, stack number, or branch name, which is how you go from a Slack link to the right local branch in one command:
gh stack checkout 4312
Now the case that used to be painful. A reviewer leaves a comment on layer 1: the migration is missing an index. You fix it in place:
gh stack bottom
# ... add the index to the migration ...
git commit --amend --no-edit
gh stack sync
gh stack sync fetches, runs the cascading rebase across every layer above, force-pushes each branch, and syncs PR state. What used to be four rebases and four force-pushes with a conflict resolved four times is one command with rerere covering the repeats.
sync also pulls trunk, so it's what you run when main has moved under you and CI has gone red on the bottom layer. Add --prune to clean up branches whose PRs have already merged.
When you want more control, gh stack rebase does the cascading rebase without the fetch-and-push, and takes --downstack / --upstack to limit the blast radius to one direction. If a rebase stops on a conflict, resolve it and continue, or bail:
gh stack rebase --continue
gh stack rebase --abort
Exit code 3 means a rebase conflict and exit code 7 means a rebase is already in progress, which is useful if you're wrapping any of this in a script or handing it to an agent.
The command worth knowing about before you need it is gh stack modify. It opens an interactive editor for restructuring the stack itself: drop a layer, fold two layers together, insert a new layer above or below, reorder, rename. This is what you reach for when review feedback is "layers 2 and 3 should really be one PR" or "pull the config change out into its own thing at the bottom." Restructuring a stack by hand is a rebase puzzle. Here it's a few keystrokes and a cascading rebase you didn't have to reason about.
Reviewing a stack
The reviewer's experience is the entire point, so it's worth being explicit about what they see.
On any PR in a stack, GitHub renders a stack map at the top of the page: every PR in the chain, its status, and one-click navigation to any layer. The diff below it is only that layer's changes. A reviewer who owns the database can review layer 1 and ignore the rest. Your frontend reviewer takes layer 4. They work in parallel, and neither of them has to scroll past the other's domain to find their own.
Approvals accumulate per layer. Layer 1 can be approved and merged while layer 4 is still being argued about, which means the migration is on main and running in staging days before the UI is settled.
Two behaviors that surprise people, both by design:
Branch protection applies to every layer. Rules like required CODEOWNER approval are enforced on mid-stack PRs too, not just the bottom one. Merge requirements for the whole stack are determined by the bottom PR's base, typically main. You don't get to slip a change past CODEOWNERS by putting it on layer 3.
CI runs on every layer. Checks configured to trigger on pull requests against your default branch run for all PRs in the stack. This is correct, since layer 3 should be green on its own, but it does mean a four-layer stack costs roughly four times the CI minutes of a single PR. On a repo with a 40-minute test suite that's a real number, and it's the main reason to keep stacks to a handful of layers rather than fifteen.
Merging
You have three options, and the flexibility here is better than what most third-party tooling offered.
Merge the whole stack at once, from the top PR:
gh stack merge 104
Merge a single layer, or a run of layers, and leave the rest open. When a lower PR merges, the PRs above it stay open and automatically retarget to the stack's base, and the remaining branches are automatically rebased. The manual retargeting dance is gone.
gh stack merge takes the usual method flags (--merge, --squash, --rebase, or --merge-method), and -y to skip confirmation. Squash-merging a stack is fine, and it's what most teams will want: each layer becomes one clean commit on main, which is exactly the history you wanted anyway.
Merge queue support was still rolling out progressively as of the July 30 announcement, so if your repo depends on a merge queue, check whether it's live for you before you move a team onto stacks.
Six use cases where stacking earns its cost
Stacking is not free. It's a handful of extra commands and it asks you to think about boundaries up front. Here's where that trade pays.
1. Migration, then the code that uses it. The canonical case. A schema change is high-risk, reviewed by different people, and deployed on a different timeline than the code that reads it. Stacked, the migration merges Monday and bakes in staging for two days while the service layer is still in review. Squashed into one PR, the migration ships the same second as the UI, and if the export query is slow you're rolling back the whole feature to fix an index.
2. The mechanical change and the judgment call. You need to rename Account to Workspace across 60 files, and then change how workspace permissions resolve. Together, that's an unreviewable diff where the 4 lines that matter are buried in 800 lines of find-and-replace. Layer 1 is the rename, skimmable in two minutes because it's obviously mechanical. Layer 2 is 40 lines of permission logic that gets the scrutiny it deserves. This split alone justifies the workflow.
3. Unblocking yourself. You finish layer 1 at 11am. Your reviewer is in a timezone eight hours away. Without stacking you either wait, or start the next piece on main and reconcile later. With stacking you gh stack add and keep going, and their feedback lands on layer 1 tomorrow with gh stack sync carrying it up through everything you built in the meantime.
4. Isolating the risky layer. In a five-part change there's usually one part that's genuinely dangerous: the cache invalidation, the retry logic, the thing that touches money. Give it its own layer and it gets its own review thread, its own approval, and its own revert. Reverting a layer is a normal revert of a normal squashed commit. Reverting "the risky third of PR #4312" is an afternoon.
5. Dependency upgrades and codemods. Bumping a major version usually means one commit of automated codemod output plus a series of hand-fixes for the places the codemod couldn't handle. As a stack: layer 1 is the version bump plus codemod, layers 2 through N are one hand-fix per subsystem, each routed to the team that owns it via CODEOWNERS. Each of those teams reviews only their file, in parallel, on a PR that isn't 3,000 lines long.
6. Agent-authored changes. This is the one I care most about right now, and it's the reason stacking stopped being optional at our shop. A coding agent will happily produce a correct-looking 900-line diff in twelve minutes. The bottleneck moved: writing code got cheap, and reviewing it did not. A 900-line agent diff has the same review problem as a 1,400-line human diff, plus the specific hazard that it's confident and internally consistent even where it's wrong.
Splitting agent output into a stack restores the property you lost. Prompt for one layer at a time, review it, then build the next on top. Or let the agent produce the whole change and use gh stack modify to carve it into layers afterward, which is a genuinely good use of that command. If your agent has the gh-stack skill installed, it can do the carving itself. Pair that with an automated first-pass review on each layer and a human still has to think, but only about a diff sized for thinking. We wrote about the durable version of this pattern in the GitHub code review agent post.
Picking the layers, which is the actual skill
The CLI is twenty minutes to learn. Choosing boundaries is the part that takes practice, and it's what separates a stack that helps from four PRs that annoy everybody.
The test I use: each layer should be independently correct and independently revertible. If layer 2 merged on its own, would main still build, still pass tests, still behave? If the answer is no, your boundary is wrong.
Concretely, that gives you a few rules:
- Order by dependency, not by file type. "All the backend, then all the frontend" is a bad split if the backend layer includes an endpoint nothing calls yet and the frontend layer breaks without it. "Schema, then the service that reads it, then the endpoint that exposes it, then the UI that calls it" is a good one, because each layer is complete at its own level.
- Put the boring stuff at the bottom. Renames, moves, formatting, generated files. They merge fast because they're skimmable, and getting them out of the way early keeps the interesting layers small.
- Dead code at intermediate layers is fine. Layer 2's service class has no callers until layer 3. That's not a smell, it's the point. Feature-flag the entry point at the top layer if you need the whole thing dark until it's ready.
- Three to five layers is the sweet spot. Two isn't worth the ceremony. Beyond about six, the sync-and-CI overhead starts to outweigh the review benefit, and a reviewer navigating nine layers has the same overwhelm problem you were solving.
- Don't stack across a design disagreement. If layer 1 might get rejected on approach, building four layers on top of it means four layers of rework. Get agreement on the foundation before you stack on it.
When not to use a stack
Skip it when:
- The change is genuinely small. A 60-line bug fix is a PR. Splitting it into three PRs is theater.
- The layers aren't actually ordered. Three independent fixes are three independent PRs against
main, reviewed and merged in any order. A stack imposes a false dependency and makes the second one wait on the first for no reason. - You're contributing from a fork. Stacked PRs require all branches to be in the same repository. Cross-fork stacks are not supported, so this workflow is for teams with push access, not open-source drive-by contributions.
- Your team uses GitHub Desktop. Stacked PRs aren't supported there. If half your team lives in that GUI, they'll be doing this in a terminal or not at all, and that's a rollout question before it's a technical one.
- Your CI is expensive and slow. Every layer runs the full suite. Four layers, a 40-minute suite, and a few sync cycles is a lot of compute. Worth it for a genuinely large change, wasteful for a medium one.
gh stack, Graphite, or by hand
Three options now, and the calculus changed yesterday.
Native gh stack | Graphite (gt) | Manual base branches | |
|---|---|---|---|
| Cost | Free, in the box | Paid above a team size | Free |
| Cascading rebase | gh stack sync, plus server-side | gt sync / auto-restack | You, four times, by hand |
| Auto-retarget on merge | Yes | Yes | No, you retarget in the UI |
| Stack view | Stack map in the PR page | Graphite's own web app | Nothing |
| Restructure a stack | gh stack modify | gt fold / gt move | Interactive rebase and prayer |
| Review UI | Native GitHub | Graphite's reviewer, or GitHub | Native GitHub |
Graphite has been the good answer to this problem for years and its CLI is mature, with gt create, gt modify, gt submit, gt sync covering the same ground plus a purpose-built review app on top. If your team already runs on it and likes it, there's no urgency to move. But the reason most teams didn't stack was that stacking required adopting an external tool, its own account model, and a second review surface. That reason is gone, and for a team starting today the native path is the default: your reviewers stay on github.com, there's nothing to buy, and the stack map shows up for people who never installed anything.
Manual base branches remain fine for the occasional two-PR split. For anything deeper, the maintenance is the reason stacking never stuck.
Rolling it out on a team
A few things worth doing deliberately rather than discovering:
- Start with one person and one real change. Stacking is a habit, not a policy. Someone doing it visibly on a change everyone can see teaches it faster than a doc.
- Agree on layer naming.
1/schema,2/service,3/apisorts correctly and tells a reviewer where they are before they open anything. Any convention works as long as it's ordered. - Check your CI cost before you scale it. Run one four-layer stack and look at the minutes. If your suite is slow, this is the moment to consider trimming what runs on non-trunk-targeting PRs.
- Tell reviewers the rule. Review your layer, approve your layer, do not wait for the stack to be complete. Reviewers who wait for the whole stack recreate exactly the bottleneck you were removing.
- Verify merge queue support if you depend on it. It was still rolling out at public preview, and a repo that requires a merge queue behaves differently than one that doesn't.
Frequently Asked Questions (FAQ)
Do stacked pull requests require paying for anything?
No. Stacked pull requests are a native GitHub feature, in public preview since July 30, 2026, and the gh stack CLI extension is free. You need GitHub CLI 2.90.0 or later and Git 2.20 or later. This replaces the main reason teams previously bought a third-party tool like Graphite for the workflow.
What happens to the PRs above a layer when it merges? They stay open and automatically retarget to the stack's base, and the remaining branches are automatically rebased. If you merge a mid-stack PR, everything below it merges too, and everything above it retargets and stays open. The manual retargeting in the GitHub UI that used to make stacking tedious is handled for you.
Can I use stacked pull requests when contributing from a fork? No. Stacked pull requests require all branches to live in the same repository, and cross-fork stacks are not supported. This makes the workflow a fit for teams with push access to a shared repo, not for open-source contributions from a personal fork. Stacked PRs are also not supported in GitHub Desktop.
Does branch protection apply to mid-stack pull requests? Yes. Branch protection rules, including CODEOWNER approvals, are enforced on every pull request in the stack, not just the bottom one, and merge requirements are determined by the bottom PR's base. CI checks configured to run on pull requests against your default branch also run on every layer, so budget for roughly N times the CI minutes on an N-layer stack.
How do I handle review feedback on the bottom layer without breaking the layers above?
Check out the bottom layer with gh stack bottom, make the fix, amend or add a commit, then run gh stack sync. That runs a cascading rebase over every layer above, force-pushes each branch, and syncs PR state. gh stack init enables git rerere, so a conflict you resolve at one layer is replayed automatically at the layers above instead of being resolved by hand four times.
How many layers should a stack have?
Three to five for most changes. Two rarely justifies the ceremony, and past six the CI cost and sync overhead start eating the review benefit, plus reviewers get the same overwhelmed feeling you were trying to eliminate. The better constraint than a layer count is the revert test: if a layer can't merge on its own and leave main correct, it isn't a layer.
Can coding agents create stacks?
Yes, and it's one of the better reasons to adopt the workflow. Install the companion skill with gh skill install github/gh-stack and an agent can build a stack directly, or produce a large change that you then split with gh stack modify. Since agents generate reviewable-looking code faster than humans can review it, splitting agent output into layers is often the only way a human review of that code stays real.
The part that actually matters
The tooling story here is small: an extension, nine or ten commands, a nice stack map. The workflow story is the one worth your attention.
Large pull requests don't get reviewed. They get approved. Everyone knows this and everyone keeps opening them, because until the bookkeeping got automated, the alternative cost more than the rubber stamp did. That calculation just changed, and it changed at the same moment that AI-assisted development started producing large diffs faster than any team can honestly review them.
Pick your next feature that would have been an 800-line PR. Split it into four. Watch what your reviewers actually say when they can see what they're looking at.
Related reading
We built and shipped five open-source vertical AI agents. Every single one had the same class of defect: absent or unreadable input rendered as a confident, clean answer. Here is what that bug looks like, why tests miss it, and what actually catches it.
AI coding agents in large codebases fail because the context that makes a change correct rarely lives in the file they're editing, and repository intelligence, instruction files, commit history, and subsystem scoping are how you fix that.
Explore how Antigravity IDE and the Antigravity CLI are reshaping software engineering with persistent, autonomous agent workspaces.
Let's build something great.
Have a project in mind? We are an elite software and AI development studio ready to bring your ideas to production. Let's talk about your roadmap.