“Stacked PRs” is the practice of breaking up a large change into smaller, individually reviewable PRs which can depend on each other, forming a DAG:

I wrote about this in my post about git-branchless, but I think the practice itself deserves its own post.

Why am I so enthusiastic about stacked PRs?

  • They encourage sending out smaller PRs. This, in it of itself, has many demonstrable benefits. In particular, smaller PRs are:
    • … Easier & quicker to review, since the reviewer has fewer changes to sign-off on.
    • … Easier to rollback if they cause breakage.
  • Stacking PRs keeps the author unblocked.
    • Authors don’t need to wait on a particular change to be merged before starting to build something on top of those changes.
    • This makes the authors less sensitive to the review time of any particular PR, since they can keep writing new changes as older changes are still in review.
  • Stacking PRs allows you to easily manage dependent changes.
    • Since stacking PRs allows you to create a DAG of dependent changes, this natually allows you to manage code changes that need to be submitted in a particular order.

Critical features for implementing stacked PRs:

  • The ability to sync the tree of PRs with an upstream easily, while maintaining the DAG structure.
    • Similarly: The ability to push up the local contents of PRs up to the centralized tool being used for code review.
  • The ability to incorporate changes into a PR, and easily propagate/merge those changes into its dependants.
  • The ability to rearrange PRs.
    • For example: changing a PR’s parent, rearranging the order of PRs in the “stack”, adding a new PR between 2 existing PRs, etc.
  • The ability to create a PR that presents it’s diff against another PR. That is, the displayed diff when reviewing code uses it’s “parent” PR as the base.
  • The ability to quickly switch between PRs.
  • The ability to view the size of a PR, to advertise that it’s an easily reviewable small PR. (h/t FeepingCreature )
  • The ability to view the PR DAG both locally, and within the code review tool.

Stacked Pull Requests vs. Stacked Commits

“Pull Request” has a specific meaning in Git, though it’s come to mean something colloquially equivalent to “change list” or “patch” over time. For the “stacking” pattern, the important thing is that atomic units of code changes can be (1) ordered as a DAG and (2) reviewed independently. This post should really be named something like “In Prase of Stacked ‘Individually Reviewable Units of Code’”, but that’s much less catchy.

The alternative to “Stacked Pull Requests” is “Stacked Commits”. The difference is mostly pragmatic: stacked PRs use branches, and can have multiple commits in a single atomic change; stacked commits use a single commit as the unit of atomic change.

Github encourages the Stacked PR approach, whereas tools like Gerrit encourage stacked commits. Personally, I’m agnostic on which is the better approach. I’m more familiar using Stacked Commits, but it does feel like you’re fighting against git with this approach – since you eschew the use of branches.

Either approach benefits mightily from the use of specialized tooling. The vanilla git CLI can be used for either approach, but you’ll spend more of your time doing low-level commit/branch modification operations than I think is wise.

Helper Tools

  • git-machete – Tagline: “Probably the sharpest git repository organizer & rebase/merge workflow automation tool you’ve ever seen”
    • Currently, this is my favorite tool for Stacked PRs.
  • git-branchless – Tagline: “High-velocity, monorepo-scale workflow for Git”
    • This is my favorite tool for Stacked Commits. It’s built with performance in mind too, which helps when operating on large monorepos.
  • git-stack – Tagline: “Stacked branch management for Git”
    • I haven’t used this one enough to have an opinion on it, but it seems useful for working with Stacked PRs – since it has the approach that “Branches are the unit of work and review”

See also

See discussion on Lobste.rs.