You've got a feature that touches five different files and needs coordination across the backend, API contract, and frontend. Submitting one massive pull request means reviewers can't dive deep without overwhelming context. Stacked pull requests offer a way to break that work into logically dependent reviews that ship faster. In this guide, we'll show you how to structure, manage, and ship stacked pull requests without losing your mind when earlier PRs change.
Understanding the stack: each PR depends on the one before
A stacked pull request is a chain of branches where each branch builds on top of the previous one. Instead of branching from main, your second PR branches from your first PR, your third from your second, and so on. This creates a dependency chain that reviewers understand at a glance.
Here's the structure:
main- └─
feature/auth-middleware(PR #1) - └─
feature/auth-middleware-tests(PR #2) - └─
feature/auth-middleware-integration(PR #3)
When you're working on a major feature, this approach forces you to think in smaller, reviewable chunks. Your first PR introduces the middleware structure. Your second adds comprehensive tests. Your third integrates it into the request pipeline. Each step is complete and testable on its own, yet they form a coherent whole.
To set this up manually:
git checkout main
git pull origin main
git checkout -b feature/auth-middleware
# Make changes, commit, push
git push origin feature/auth-middleware
# Now create PR #1, wait for approval (don't merge yet)
# Create the second PR on top of the first
git checkout -b feature/auth-middleware-tests
# Make more changes, commit, push
git push origin feature/auth-middleware-tests
# Now create PR #2 with a base of feature/auth-middleware, not mainThe key difference: when creating PR #2 on GitHub, you'll set its base branch to feature/auth-middleware, not main. This tells reviewers and CI that this PR depends on the previous one.
Rebasing the stack when earlier PRs change
The pain point of stacked PRs is that when your first PR gets approved and merges, your second and third PRs are now based on a branch that no longer exists. You need to rebase them onto main to keep the stack clean.
Let's say PR #1 merges with review feedback applied. Now your PR #2 is technically outdated—its commits are stacked on top of a branch that was merged and deleted. You need to rebase PR #2 onto main, then PR #3 onto the new PR #2, and so on down the stack.
Manual rebase for a three-PR stack:
# Start with the second PR branch
git checkout feature/auth-middleware-tests
# Rebase it onto main (which now includes PR #1's merged commits)
git rebase main
# If conflicts exist, resolve them
# git add .
# git rebase --continue
# Force-push to update the remote branch
git push origin feature/auth-middleware-tests --force-with-lease
# Now move to the third PR
git checkout feature/auth-middleware-integration
# Rebase it onto the updated second PR
git rebase feature/auth-middleware-tests
# Resolve any conflicts
# git add .
# git rebase --continue
# Force-push the third PR
git push origin feature/auth-middleware-integration --force-with-leaseUse --force-with-lease instead of --force. It's safer because it won't overwrite work from teammates if they've also pushed to the branch. If someone else pushed changes you didn't know about, the command fails safely instead of obliterating their work.
Rebasing requires discipline. It's easy to mess up the order or forget to update a middle PR. This is where tooling helps.
Tools like Graphite help manage stacks
Manually rebasing a five-PR stack every time an earlier PR changes is tedious and error-prone. Tools designed for stacked workflows handle this automatically.
Graphite is the most mature option in this space. It's a CLI tool and GitHub integration that tracks your stack, automates rebasing, and provides a visual dashboard of dependencies.
With Graphite, the workflow looks like:
# Install Graphite
brew install withgraphite/tap/graphite
# Initialize in your repo
graphite repo init
# Create your stack
git checkout main
graphite create feature/auth-middleware
# Make changes and commit
graphite submit
# Create the next PR in the stack
graphite create feature/auth-middleware-tests
graphite submit
# When PR #1 is approved, rebase the entire stack at once
graphite syncGraphite's sync command rebases all dependent branches onto their parent branches and main. It handles the tedious rebase orchestration so you don't have to remember which branch to rebase in which order. You can also use graphite log to visualize your entire stack at once, which is invaluable for larger feature work.
Other tools in this space include git-stack, Stacked PRs for VS Code, and some teams build custom integrations. The common benefit: they eliminate manual rebase mistakes and keep you focused on writing code.
Not all teams use specialized tools. Some prefer the discipline of manual rebasing to keep dependencies explicit. Evaluate your team's comfort level with Git and the size of typical feature work. A two-PR stack is manageable by hand; a six-PR stack strongly favors automation.
Keep each step independently reviewable
The entire purpose of stacking is to enable focused, high-quality reviews. If you ignore this principle, you lose the benefit.
Each PR in your stack should be able to stand alone conceptually. That doesn't mean it doesn't depend on previous code—of course it does. But the reviewer should understand what that specific PR accomplishes without scrolling through unrelated logic.
Bad stack structure:
- PR #1: "Add auth" (390 lines, mixes middleware, utils, and tests)
- PR #2: "Fix bugs in auth" (180 lines, touches the same files as PR #1)
- PR #3: "Integrate auth" (220 lines)
A reviewer looking at PR #1 is immediately confused: are tests expected to pass? Why are utilities mixed with middleware? Is PR #2 fixing bugs that should have been in PR #1, or is it a separate concern? This defeats the point.
Good stack structure:
- PR #1: "Implement auth middleware" (120 lines, middleware only)
- PR #2: "Add auth middleware tests" (95 lines, comprehensive test coverage)
- PR #3: "Integrate auth into request pipeline" (85 lines, integration and wiring)
Each PR is focused, testable, and understandable. A reviewer of PR #1 evaluates the middleware design. A reviewer of PR #2 checks that tests are comprehensive. A reviewer of PR #3 checks that integration is correct.
Guidelines for reviewable PRs in a stack:
- Single concern per PR: One PR adds a feature, one adds tests, one adds integration, one documents. Don't mix unrelated logic in the same PR.
- Tests should pass at each step: If you're adding functionality in PR #1, its tests should pass in PR #1 (even if they depend on prior stack PRs). Don't defer test validation to a later PR.
- Keep line counts reasonable: Aim for 200–400 lines of actual changes per PR. If you're at 600+, split it further.
- Document dependencies in the PR description: Write "This PR depends on #X" or "Builds on the middleware from #Y" so reviewers know context without guessing.
Common mistakes
Mistake 1: Merging PRs out of order. If PR #1 and PR #2 are stacked, and PR #2 gets approved first, resist the urge to merge it early. Merge PR #1 first, then rebase and merge PR #2. Merging out of order creates a confusing history and can break dependent branches.
Mistake 2: Forgetting to update the base branch. When creating PR #2, many developers accidentally set the base to main instead of feature/auth-middleware. The PR will then show all changes from both PRs, defeating the purpose. Double-check the base branch dropdown before submitting.
Mistake 3: Not communicating the stack to reviewers. If you have five PRs stacked but only link to the latest one in Slack, reviewers won't know where to start. Share the chain: "Review in order: #A → #B → #C." Provide context upfront.
Mistake 4: Rebasing too aggressively. If PR #1 is still under review and you rebase PR #2 onto main every time PR #1 gets a new commit, you're creating churn. Rebase the stack when PRs merge or when you're pulling in major external changes. During active review, stability matters more than staying perfectly up-to-date.
Mistake 5: Mixing large refactors with feature work. Stacked PRs shine for feature additions but struggle with interwoven refactors. If PR #1 renames a function and PR #2 uses that function, they're harder to review in isolation. Consider: should the refactor be a separate, unrelated PR? Should it happen after the feature ships?
Key takeaways
- Stacked pull requests break large features into logically dependent, reviewable chunks, speeding up both review and iteration.
- Each PR in the stack depends on the previous one and has its own base branch, not main.
- When earlier PRs merge, rebase the stack using
git rebaseand--force-with-lease, or use tools like Graphite to automate the process. - The entire value of stacking is lost if each PR isn't independently reviewable and focused on a single concern.
- Always set the correct base branch, communicate the stack to reviewers, and resist merging PRs out of order.
- Stacked PRs work best for features and additions; use caution with refactors that span multiple PRs in the stack.






Comments
Loading comments…