Sitemap
Akshathaholla

Akshathaholla

Stacked Pull Requests

4 min readSep 28, 2026

--

A way of shipping one large change as a sequence of small, dependent reviews

The one big PR problem

Most teams ship features on a single long-lived branch: work for a few days, open one pull request, and wait. The PR grows to forty files before anyone sees it. Reviewers skim instead of read. Feedback lands three days later, main has moved on underneath you, and now there’s a rebase to do before anyone can even discuss the comments. Nothing about this is anyone’s fault — it’s just what happens when a whole feature is reviewed as one object.

Stacked PRs are a different unit of review. Instead of one branch and one PR, the change is split into an ordered sequence of small branches, each one built directly on top of the last. Each PR shows only what it adds. Reviewers read a diff in minutes instead of an afternoon, and — because the pieces are ordered, not just small — the reviewer always has exactly the context the change needs and nothing more.

What actually makes it a stack

Each branch in a stack has the branch below it as its base — not main. PR #402 is opened against PR #401, not against main; PR #403 is opened against #402, and so on. Because the base is the previous branch rather than main, the platform can compute each PR's diff against only its immediate parent — so what a reviewer sees is genuinely just that layer's contribution, even though every branch also contains everything below it.

Example, a token-rotation feature split four ways:

main
└─ #401 auth: add token refresh
└─ #402 auth: rotate refresh tokens
└─ #403 api: use rotated tokens
└─ #404 ui: show token expiry banner

You don’t have to wait for #401 to merge before starting #402 — you branch off your own unmerged work and keep going. That’s the part that actually saves time: review and the next unit of work happen in parallel instead of in sequence.

The daily loop: restacking

The cost of that parallelism shows up the moment review feedback lands on a lower PR. Fix something in #401, and its commit history changes — which means #402, #403, and #404 are all now based on a version of #401 that no longer exists. Every branch above the edit has to be rebased onto the new version of the one below it. This is called restacking, and in a stack of any real depth it happens constantly — every round of review comments on any layer triggers it for every layer above.

Done by hand, that’s checking out each branch in order, rebasing, resolving whatever conflicts fall out, and force-pushing — four times, for a four-deep stack, for one round of feedback on the bottom layer. Git 2.38 added rebase --update-refs specifically for this: it moves every branch pointer in the stack together during a single rebase, rather than one at a time. Dedicated stacking tools go further and turn the whole sequence into one command.

Merging bottom-up

A stack always merges in the order it was built. Once #401 is approved and merged into main, #402’s base moves to main — it’s now the new bottom of the stack, carrying only its own diff. Repeat for each layer as approvals land. A merge queue that understands stacks will do this automatically: merge a layer, retarget everything above it, re-run checks, and merge the next one the moment it’s green — so the stack stays in sync without anyone manually walking it.

Who’s built for it

Git itself — rebase — update-refs moves a whole stack in one rebase. No visualization, no PR bookkeeping — you still track the order yourself.

GitHub base-branch chains — Point each PR’s base at the PR below it. GitHub shows only the incremental diff and retargets the base once the lower PR merges.

Get Akshathaholla’s stories in your inbox

Join Medium for free to get updates from this writer.

Graphite — CLI and web layer built around the stack as the unit of work — create, submit, restack, and merge an entire stack with single commands, with a visual map of the whole thing.

Sapling — Meta’s source-control client, stack-first from the ground up — commands operate on the stack directly rather than on named branches.

git-branchless — Adds a commit-based “smartlog” and safe, undo-able rebase tooling on top of plain Git, for stacking without a hosted service.

spr — Lean CLI that maps a linear stack of local commits to one GitHub PR per commit, updating each one on every push.

Phabricator / Differential — Where “stacked diffs” started, inside Meta, before Git-native tooling existed. Still the reference point people cite.

What you get

  • Each PR is small enough that someone can actually review it carefully
  • Review and the next unit of work happen at the same time, not in sequence
  • Smaller changes mean bugs are easier to catch before they slip through
  • Git history that reads as a deliberate sequence, not one squashed blob

What it costs

  • Restacking discipline isn’t optional — skip it and branches quietly drift apart
  • A reviewer who only sees one small piece might miss the bigger picture
  • Good tooling isn’t everywhere — many teams still have to manage this by hand
  • Everyone on the team needs to be comfortable with force-pushing safely

When to use it

Stacking is worth the extra effort when you’re working on something that takes several days, or when the work naturally happens in steps — where step two only makes sense after step one is done. In those cases, you can already see how to split the work before you even start.

It’s not worth it for a quick one-line fix, for throwaway experiments you don’t plan to keep, or for a team that isn’t comfortable with git and doesn’t have tools to manage the stack. In those cases, the extra coordination work outweighs the benefit of smaller reviews.

--

--