GitHub Stacked PRs (github.github.com)

by ezekg 528 comments 900 points
Read article View on HN

528 comments

[−] adamwk 31d ago
As someone who used phabricator and mercurial, using GitHub and git again feels like going back to the stone ages. Hopefully this and jujutsu can recreate stacked-diff flow of phabricator.

It’s not just nice for monorepos. It makes both reviewing and working on long-running feature projects so much nicer. It encourages smaller PRs or diffs so that reviews are quick and easy to do in between builds (whereas long pull requests take a big chunk of time).

[−] smallmancontrov 31d ago
I'm so glad git won the dvcs war. There was a solid decade where mercurial kept promoting itself as "faster than git*†‡" and every time I tried it wound up being dog slow (always) or broken (some of the time). Git is fugly but it's fast, reliable, and fugly, and I can work with that.
[−] alwillis 31d ago

> I'm so glad git won the dvcs war. There was a solid decade where mercurial kept promoting itself as "faster than git".

It wasn't the Mercurial team saying it was faster than Git; that was Facebook after contributing a bunch of patches after testing Mercurial on their very large mono-repo in 2014 [1]:

For our repository, enabling Watchman integration has made Mercurial’s status command more than 5x faster than Git’s status command. Other commands that look for changed files–like diff, update, and commit—also became faster.

In fact they liked Mercurial so much they essentially cloned it to create their own dvcs, Sapling [2]. (An aside: Facebook did all of this because it was taking too long getting new engineers up to speed with Git. Shocker.)

Today, most of the core of Mercurial has been rewritten in Rust; when Facebook did their testing, Mercurial was nearly 100% Python. That's where the "Mercurial is slow" thing came from; launching a large Python 2.x app took a while back in the day.

I was messing with an old Mercurial repo recently… it was like a breath of fresh air. If I can push to GitHub using Mercurial… sign me up.

[1]: https://engineering.fb.com/2014/01/07/core-infra/scaling-mer...

[2]: https://sapling-scm.com/

[−] Gabrys1 31d ago
You can push to GitHub using Sapling. I wish Sapling open source was given more love, as the experience for non-Facebookers is subpar. No bash completion outside the box, no distro packages, no good help pages, random issues interacting with a Git repo...
[−] Ericson2314 31d ago
Sapling and JJ can sort it out, the outside world will only care for one of them.
[−] withinboredom 31d ago
Sounds like what my teachers used to say: “a personal problem”. Literally nobody outside FB knows what they’re missing and until they fix that, literally nobody cares.
[−] smallmancontrov 31d ago
No, the "hg is fast" marketing claim that retreated to "hg is Big-O fast and you are dumb for caring about constant terms and factors even if they clearly dominate your use case" predates 2014 and the Facebook patches. These talking points were old in 2010. Mercurial was always dog slow and always gaslighting about it.

I'm glad BigCo made tools to serve their needs, but their needs aren't my needs or most peoples' needs.

> Mercurial has been rewritten in Rust

I'm glad they saw the light eventually! Ditto for the rest of the Rust Tooling Renaissance.

[−] steveklabnik 31d ago
What is kind of funny here is that you're right locally. At the same time, the larger tech companies (Meta and Google, specifically) ended up building off of hg and not git because (at the time, especially) git cannot scale up to their use cases. So while the git CLI was super fast, and the hg CLI was slow, "performance" means more than just CLI speed.

I was never a fan of hg either, but now I can use jj, and get some of those benefits without actually using it directly.

[−] eqvinox 31d ago
I remember using darcs, but the repos I was using it with were so small as to performance really not mattering…
[−] littlecranky67 31d ago
I might be the outlier, but am I the only one who doesn't care much about the speed of git? I've been using git since 2011 as my main vcs for personal and professional work as a freelancer contractor. Whenever I "wait" for git, it is either limited by the bandwidth (git clone) or by the amount of commit hooks that I implemented for linting, verification etc. The percentage of time actually spent in git internal execution must be a tiny fraction of my day to day usage. What IS affecting me (and my the teams I work in) is usability and UX experience. I.e. if people would screw up stuff (no matter if in git or mercurial) we spent far more time fixing this - I don't think the impmentation speed would matter here.

The only case I can imagine is when doing a full checkout of a big repo, but even there, there is --depth which is quite practical.

[−] raincole 31d ago
This matches my experience 100%. I was about to write a similar comment before I see yours.
[−] forrestthewoods 31d ago
Mercurial has a strictly superior API. The issue is solely that OG Mercurial was written in Python.

Git is super mid. It’s a shame that Git and GitHub are so dominant that VCS tooling has stagnated. It could be so so so much better!

[−] bmitc 31d ago
Git is not remotely fast for large projects.
[−] Leynos 31d ago
I just used it because I preferred the UX.
[−] kardianos 31d ago
I continue to use gerrit explicitly because I cannot stand github reviews. Yes, in theory, make changes small. But if I'm doing larger work (like updating a vendored dep, that I still review), reviewing files is... not great... in github.
[−] calebio 31d ago
I miss the Phabricator review UI so much.
[−] nerdypepper 31d ago
tangled.org supports native stacking with jujutsu, unlike github's implementation, you don't need to create a new branch per change: https://blog.tangled.org/stacking/
[−] choi0330 31d ago
You should definitely try out https://github.com/hokwangchoi/pilegit. It's platform-agnostic and I use for my workflow with Phabricator, Github, Gitlab and Gitea. No learning curves for cross-platform operations!
[−] eru 31d ago
Oh, phabricator. I hated that tool with a passion. It always destroyed my carefully curated PR branch history.

See https://stackoverflow.com/questions/20756320/how-to-prevent-...

[−] jenadine 31d ago
I might be missing something, but what I need is not "stacked PR" but a proper UI and interface to manage single commit:

- merge some commits independently when partial work is ready.

- mark some commit as reviewed.

- UI to do interactive rebase and and squash and edit individual commits. (I can do that well from the command line, but not when using the GitHub interface, and somehow not everyone from my team is familiar with that)

- ability to attach a comment to a specific commit, or to the commit message.

- better way to visualize what change over time in each forced push/revision (diff of diff)

Git itself already has the concept of commit. Why put this "stacked PR" abstraction on top of it?

Or is there a difference I don't see?

[−] akersten 31d ago
Does it fix the current UX issue with Squash & Merge?

Right now I manually do "stacked PRs" like this:

main <- PR A <- PR B (PR B's merge target branch is PR A) <- PR C, etc.

If PR B merges first, PR A can merge to main no problems. If PR A merges to main first, fixing PR B is a nightmare. The GitHub UI automatically changes the "target" branch of the PR to main, but instantly conflicts spawn from nowhere. Try to rebase it and you're going to be manually looking at every non-conflicting change that ever happened on that branch, for no apparent reason (yes, the reason is that PR A merging to main created a new merge commit at the head of main, and git just can't handle that or whatever).

So I don't really need a new UI for this, I need the tool to Just Work in a way that makes sense to anyone who wasn't Linus in 1998 when the gospel of rebase was delivered from On High to us unwashed Gentry through his fingertips..

[−] bsimpson 31d ago
Finally!

I never understood the PR=branch model GitHub defaulted to. Stacked commits (ala Phabricator/Gerrit) always jived more with how my brain reasons about changes.

Glad to see this option. I guess I'll have to install their CLI thing now.

[−] nathas 31d ago
Meanwhile, you still can't do fast-forward merges in GitHub :clown: https://github.com/orgs/community/discussions/4618

And it doesn't even rebase and merge correctly with fast-forward if there it's a clean set of commits! https://github.com/orgs/community/discussions/5524

[−] cleverdash 31d ago
As a solo dev I rarely need stacked PRs, but the underlying problem, keeping PRs small and reviewable, is real even when you're your own reviewer. I've found that forcing myself to break work into small branches before I start (rather than retroactively splitting a giant branch) is the actual discipline. The tooling just makes it less painful when you don't.

Curious whether this changes anything for the AI-assisted workflow. Right now I let Claude Code work on a feature branch and it naturally produces one big diff. Stacked PRs could be interesting if agents learned to split their own work into logical chunks.

[−] WhyNotHugo 31d ago
I really don't get the point of stacked PRs.

Just using git, you'd send a set of patches, which can be reviewed, tested and applied individually.

The PR workflow makes a patch series an undivisible set of changes, which must be reviewed, tested and applied in unison.

And stacked PRs tries to work around this issue, but the issue is how PRs are implemented in the first place.

What you really want is the ability to review individual commits/patches again, rather than work on entire bundles at once. Stacked PRs seems like a second layer of abstraction to work around issues with the first layer of abstractions.

[−] robertwt7 31d ago
There’s a startup callled Graphite dedicated to stacked PRs. I have been using them for a while now I always wonder why github doesn’t implement something similar to this. I probaly will try and switch to GitHub to see if it works flawlessly
[−] thcipriani 31d ago
Very cool that GitHub actually put stacks in the UI vs. GitLab's glab stack[0] (which looks just like the gh stack part of GitHub's thing).

One part that seems like it's going to feel a little weird is how merging is set up[1].

That is, if I merge the bottom of the stack, it'll rebase the others in the stack, which will probably trigger a CI test run. So, if I have three patches in the stack, and I want to merge the bottom two, I'd merge one, wait for tests to run on the other, merge the second vs. merge just those two in one step (though, without having used it, can't be sure about how this'd work in practice—maybe there's some way to work around this with restacking?)

[0]: <https://docs.gitlab.com/cli/stack/>

[1]: <https://github.github.com/gh-stack/guides/stacked-prs/#mergi...>

[−] fphilipe 31d ago
I've been doing stacked PRs for ~2 years now. Thus, I don't quite see the need for this CLI. Git has had some additions in the last few years that make this work natively – specifically the --update-refs flag[1] or the rebase.updateRefs config. Combined with git commit --fixup, rebase.autoStash, and rebase.autoSquash rebasing stacks becomes a breeze (as long as you work off from the tip of your stack). Add in git-absorb[2] and the heavy-lifting is taken care of.

My biggest gripe with GitHub when working with stacks – and something that's not clarified in these docs – is whether fast-forward merges are possible. Its "Merge with rebase" button always rewrites the commit. They do mention that the stack needs to be rebased in order to merge it. My workaround has been git merge --ff-only top-branch-of-stack to merge the entire stack locally into main (or anything in between actually) and then push. GitHub neatly recognizes that each PR in the stack is now in main and marks them all as merged. If there are subsequent PRs that weren't merged it updates the base branch.

Having said that, it's great to see GitHub getting a proper UI for this. It's also great that it understands the intent that branch B that goes on top of branch A is a stack and thus CI runs against. I just hope that it's not mandatory to use their CLI in order to create stacks. They do cover this briefly in the FAQ[3], but it might be necessary to use gh stack init --adopt branch-a branch-b branch-c. On the other hand, if that removes the need to manually create the N PRs for my stack, that's nice.

[1]: https://git-scm.com/docs/git-rebase#Documentation/git-rebase...

[2]: https://github.com/tummychow/git-absorb

[3]: https://github.github.com/gh-stack/faq/#will-this-work-with-...

[−] boomlinde 31d ago
IME the github workflow promotes bad commit hygiene by making squashing or rebasing as-is an either-or choice in the web GUI.

This will help some since you can more easily split PRs into units that make sense to squash at the end, but it still seems like not doing this on a per-commit basis is a disadvantage compared to Gerrit. With Gerrit I can use all the built-in Git rebase/squash/fixup tools to manage the commit stack and push everything in one go. I don't think there's a nearly as convenient a way to work with stacked branches in Git.

[−] dminik 31d ago
Maybe this is just a skill issue, but even with several attempts I just can't figure out why I would use stacked diffs/PRs. Though maybe that's because of the way I work?

I notice a lot of examples just vaguely mention "oh, you can have others review your previous changes while you continue working", but this one doesnt make sense to me. Often times, the first set of commits doesn't even make it to the end result. I'm working on a feature using lexical, and at this point I had to rewrite the damn thing 3 times. The time of other devs is quite valuable and I can't imagine wasting it by having them review something that doesn't even make it in.

Now, I have been in situations where I have some ready changes and I need to build something on top. But it's not something just making another branch on top + rebase once the original is merged wouldn't solve.

Is this really worth so much hype?

[−] metafeather 31d ago
I've been using git town[1] for years to managed stacked PR's alone with Github PR's[2] and juniors I have introduced it to have really found it a helpful mental model when developing features.

I hope the Gitub CLI will include syncing[3] 'stacks' locally with upstream in a similar way.

[1]: https://www.git-town.com/stacked-changes.html

[2]: https://github.com/marketplace/actions/git-town-github-actio...

[3]: https://www.git-town.com/commands/sync.html

[−] herpdyderp 31d ago
I thrive on stacked PRs but this sure seems like a weird way to implement support for it. Just have each branch point to their parent in the chain, the end. Just native Git. I've been longing for better GitHub support for this but the CLI is not where I need that support: just the UI.
[−] fweimer 31d ago
I find this puzzling. It does not seem to allow to stack PRs on top of other people's PRs?

There is already an option to enable review comments on individual commits (see the API endpoint here: https://docs.github.com/en/rest/guides/working-with-comments...). Self-stacking PRs seem redundant.

[−] AJRF 31d ago
I have never got a good answer to "can't you just make smaller PRs". This is convoluted tooling (needs its own CLI) for something you could achieve with just learning how git works.
[−] j3g6t 31d ago
Super excited to give this a whirl - i've been messing with graphite's gt command for stacking and it's been relatively decent but I didn't love needing to bring in another tool/service/account when I only care about the stacking behaviour. Was a fun experiment but nice I can simplify back onto gh and git
[−] ZeWaka 31d ago
Seems to mainly be useful for monorepos as currently designed. Or, to replace a long-lived feature/refactor branch.
[−] topaztee 31d ago
how is this different than viewing a PR one commit at a time?
[−] quibono 31d ago
GitLab's UI around MRs (PRs) is IMO miles better than what GH's been offering. Try creating a PR from branch A to main, and then rebasing A. GitLab handles this fine and can show you changes between the two revisions; GitHub is completely lost.
[−] eqvinox 31d ago

> How It Works

> The gh stack CLI handles the local workflow […]

That's not "how it works", that's "how you['re supposed to] use it"… for "how it works" I would've expected something like "the git branches are named foo1 foo2 and foo3 and we recognize that lorem ipsum dolor sit amet…"

…which, if you click the overview link, it says "The CLI is not required to use Stacked PRs — the underlying git operations are standard. But it makes the workflow simpler, and you can create Stacked PRs from the CLI instead of the UI." … erm … how about actually explaining what the git ops are? A link, maybe? Is it just the PRs having common history?

…ffs…

(In case it's not obvious: I couldn't care less for using a GH specific CLI tool.)

[−] fmbb 31d ago

> Large pull requests are hard to review, slow to merge, and prone to conflicts. Reviewers lose context, feedback quality drops, and the whole team slows down.

OK, yeah, I’m with you.

> Stacked PRs solve this by breaking big changes into a chain of small, focused pull requests that build on each other — each one independently reviewable.

I don’t get this part. It seems like you are just wasting your own time building on top of unreviewed code in branches that have not been integrated in trunk. If your reviews are slow, fix that instead of running ahead faster than your team can actually work.

[−] rs545837 31d ago
This is awesome honestly, Stacked PRs are one of those features that feels obvious in hindsight. Breaking a n-line PR into 3 focused layers where each one is independently reviewable is a huge win for both the author and reviewer. The native GitHub UI with the stack navigator is the right call too, and there's no reason this should require a third-party tool.

One thing I keep thinking about in this same direction: even within a single layer of a stack, line-level diffs are still noisy. You rename a function and update x call sites, the diff shows y changed lines. A reviewer has to mentally reconstruct "oh this is just a rename" from raw red/green text.

Semantic diffing (showing which functions, classes, methods were added/modified/deleted/moved) would pair really well with stacks. Each layer of the stack becomes even easier to review when the diff tells you "modified function X, added function Y" instead of just showing changed lines.

I've been researching something in this direction, https://ataraxy-labs.github.io/sem/. It does entity-level diffs, blame, and impact analysis. Would love to see forges like GitHub move in this direction natively. Stacked PRs solve the too much at once problem. Semantic diffs solve the "what actually changed" problem. Together they'd make code review dramatically better.

[−] inerte 31d ago
Looks interesting, but it seems you need to know the final shape of the stack before you start creating Pull Requests. So it's useful if you create Pull Request A, then immediately start working on something that builds on top of A, create a Pull Request for that (while A is still a PR), then you can do A->B->C

Here's something that would be useful: To break down an already big PR into multiples that make up a stack. So people can create a stack and add layers, but somehow re-order them (including adding something new at the first position).

[−] jrochkind1 31d ago
Well, I have been waiting for this for YEARS.

Every time I try to do it manually, I wind up screwing everthing up.

Very interested ot check it out.

[−] netheril96 31d ago
Does this work from a fork? That is, can I file a stacked PR to a project not owned by me, by creating branches in my forked project? Previously I asked AI about how to contribute stacked PR, it told me that I can only do it when I have push privileges to the repo, not from a fork, and the doc here is ambiguous.

----

OK, I found this from official docs, so this feature is now quite useless to me:

> Can stacks be created across forks?

> No, Stacked PRs currently require all branches to be in the same repository. Cross-fork stacks are not supported.

[−] siva7 31d ago
What a time to be alive. Stacked PRs are now a native feature of Github, even with first-class support for your ai agents. Vibeslop your whole Jira Backlog. Don't fear the merge anymore. Just make any feature branch a long-lived branch by stacking one upon another like bricks.

I'm old enough to have worked with SVN and young enough to have taught engineers to avoid stacking PR in Git. All wisdom has been lost and will probably be rediscovered in another time by another generation.

[−] alkonaut 31d ago
Let's say I have the canonical example of a stack from main via a backend-pr and a frontend-pr. When my stack is done I send it for review to one frontend reviewer and one backend reviewer.

Usually when you develop a "full stack" thing you continuously massage the backend into place while developing frontend stuff. If you have 10 commits for frontend and 10 for backend, they might start with 5 for backend, then 5 commits to each branch to iron out the interface and communication, and finally 5 commits on the frontend. Let's call these commits B1 through B10 and F1 through F10. Initially I have a backend branch based on main wuth commits B1 through B5.

Then I have a frontend branch based on B5 with commits F1 through F5. But now I need to adjust the backend again and I make change B6. Now I need to rebase my frontend branch to sit on B6? And then I make F6 there (And so on)?

And wouldn't this separation normally be obvious e.g. by paths? If I have a regular non-stack PR with 20 commits and 50 changed files, then 25 files will be in /backend and 25 in /frontend.

Sure, the reviewers who only review /frontend/* might now see half the commits being empty of relevant changes. But is that so bad?