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).
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.
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.
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.
Same here. Don't understand why Github hasn't supported this until now. I'm tired of reviewing PRs with thousands of lines of changes, which are getting worse nowadays with vibe coding.
What does Facebook use internally these days. I'm amazed that the state of review tools is still at or behind what we had a decade ago for the most part.
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/
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?
It's basically trying to bring the stacked diff workflow pioneered by Phabricator to GitHub.
The idea is that it allows you to better handle working on top of stuff that's not merged yet, and makes it easier for reviewers to review pieces of a larger stack of work independently.
It's really useful in larger corporate environments.
I've used stacked PRs when doing things like upgrading react-native in a monorepo. It required a massive amount of changes, and would be really hard to review as a single pull request. It has to be landed all at once, it's all or nothing. But being able to review it as smaller independent PRs is helpful.
Stacking PRs is also useful even when you don't need to merge the entire stack at once.
I'm not in a large corporate environment, but that also means we're not always a well oiled machine, and sometimes i am writing faster than the reviewer can review for a period of time -- and i really need the stacking then too.
Constantly rewriting git history with squashes, rebases, manual changes, and force pushes has always seemed like leaving a loaded gun pointed at your foot to me.
Especially since you get all of the same advantages with plain old stream on consciousness commits and merges using:
I find rebases are only a footgun because the standard git cli is so bad at representing them - things like --force being easier to write than --force-with-lease, there being no way to easily absorb quick fixes into existing commits, interdiffs not really being possible without guesswork, rebases halting the entire workflow if they don't succeed, etc.
I've switched over pretty much entirely to Jujutsu (or JJ), which is an alternative VCS that can use Git as its backend so it's still compatible with Github and other git repos. My colleagues can all use git, and I can use JJ without them noticing or needing to care. JJ has merges, and I still use them when I merge a set of changes into the main branch once I've finished working on it, but it also makes rebases really simple and eliminates most of the footguns. So while I'm working on my branch, I can iteratively make a change, and then squash it into the commit I'm working on. If I refactor something, I can split the refactor out so it's in a separate commit and therefore easiest to review and test. When I get review feedback, I can squash it directly into the relevant commit rather than create a new commit for it, which means git blame tends to be much more accurate and helpful - the commit I see in the git blame readout is always the commit that did the change I'm interested in, rather than maybe the commit that was fixing some minor review details, or the commit that had some typo in it that was fixed in a later commit after review but that relationship isn't clear any more.
And while I'm working on a branch, I still have access to the full history of each commit and how it's changed over time, so I can easily make a change and then undo it, or see how a particular commit has evolved and maybe restore a previous state. It's just that the end result that gets merged doesn't contain all those details once they're no longer relevant.
the best implementation i've worked with was SuperSmartLog (SSL) at Meta, which was open-sourced at interactive smartlog (https://sapling-scm.com/docs/addons/isl/). There are also extension for it in VSCode, etc.
Surprisingly it never gained the adoption it deserved.
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.
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.
> 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.
...or you don't bother with all that and simply do:
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.
Stacked PRs can be created via the UI, API, or CLI.
You can also run a combination of these. For ex, use another tool like jj to develop locally, push up the branches, and use the gh CLI to batch create a stack of n PRs, without touching local state.
CLI is great because now I can tell my AI agent to do it. “Fix all dependabot security issues (copy logs) and run tests to validate functionality. Create each dependency as its own stack (or commit) so that contributors may review each library update easily.”
Huh interesting, my mental model is unable to see any difference between them.
I mean a branch is just jamming a flag into a commit with a polite note to move the flag along if you're working on it. You make a long trail, leave several flags and merge the whole thing back.
Of course leaving multiple waypoints only makes sense if merging the earlier parts makes any sense, and if the way you continue actually depends on the previous work.
If you can split it into several small changes made to a central branch it's a lot easier to merge things. Otherwise you risk making a new feature codependent on another even if there was no need to.
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..
Yes, we handle this both in the CLI and server using git rebase --onto
git rebase --onto <new_commit_sha_generated_by_squash> <original_commit_sha_from_tip_of_merged_branch> <branch_name>
So for ex in this scenario:
PR1: main <- A, B (branch1)
PR2: main <- A, B, C, D (branch2)
PR3: main <- A, B, C, D, E, F (branch3)
When PR 1 and 2 are squash merged, main now looks like:
S1 (squash of A+B), S2 (squash of C+D)
Then we run the following:
git rebase --onto S2 D branch3
Which rewrites branch3 to:
S1, S2, E, F
This operation moves the unique commits from the unmerged branch and replays them on top of the newly squashed commits on the base branch, avoiding any merge conflicts.
Conflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main.
No idea if this feature fixes this.
Edit: Hopefully `gh stack sync` does the rebasing correctly (rebase --onto with the PR A's last commit as base)
> Conflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main.
Yeah, and I kind of see how git gets confused because the squashed commits essentially disappear. But I don't know why the rebase can't be smart when it sees that file content between the eventual destination commit (the squash) is the same as the tip of the branch (instead of rebasing one commit at a time).
I'm not sure I follow your workflow exactly. If PR B is merged, then I'd expect PR A to already be merged (I'd normally branch off of A to make B.)
That said, after the squash merge of A and git fetch origin, you want something like git rebase --update-refs --onto origin/main A C (or whatever the tip of the chain of branches is)
The --update-refs will make sure pr B is in the right spot. Of course, you need to (force) push the updated branches. AFAICT the gh command line tool makes this a bit smoother.
I agree that this is annoying and unintuitive. But I don’t see the simplest solution here, so:
All you need to do is pull main, then do an interactive rebase with the next branch in your stack with ‘git rebase -i main’, then drop all the commits that are from the branch you just merged.
If I'm following correctly, the conflicts arise from other commits made to main already - you've implicitly caught branch A up to main, and now you need catch branch B up to main, for a clean merge.
I don't see how there is any other way to achieve this cleanly, it's not a git thing, it's a logic thing right?
I've no issue with the logic of needing to update feature branches before merging, that's pretty bread and butter. The specific issue with this workflow is that the "update branch" button for PR B is grayed out because there are these hallucinated conflicts due to the new squash commit.
The update branch button works normally when I don't stack the PRs, so I don't know. It just feels like a half baked feature that GitHub automatically changes the PR target branch in this scenario but doesn't automatically do whatever it takes for a 'git merge origin/main' to work.
No, it's a Git thing arising from squash commits. There are workflows to make it work (I've linked the cleanest one I know that works without force pushing), but ultimately they're basically all hacks. https://www.patrickstevens.co.uk/posts/2023-10-18-squash-sta...
Oh that's annoying, seems to me there wouldn't have been an issue if you just merged B into A after merging A into main, or the other way around but that already works fine as you pointed out.
I mean if you've got a feature set to merge into dev, and it suddenly merges into main after someone merged dev into main then that's very annoying.
You "just" need to know the original merge-base of PR B to fix this. github support is not really required for that. To me that's the least valuable part of support for stacked PRs since that is already doable yourself.
The github UI may change the target to main but your local working branch doesn't, and that's where you `rebase --onto` to fix it, before push to origin.
It's appropriate for github to automatically change the target branch, because you want the diff in the ui to be representative. IIRC gitlab does a much better job of this but this is already achievable.
What is actually useful with natively supported stacks is if you can land the entire stack together and only do 1 CI/actions run. I didn't read the announcement to see if it does that. You typically can't do that even if you merge PR B,C,D first because each merge would normally trigger CI.
EDIT: i see from another comment (apparently from a github person) that the feature does in fact let you land the entire stack and only needs 1 CI run. wunderbar!
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.
It's easier to pile on a lot of changes with AI assisted workflows. And reviewing all that is definitely a challenge just because of the volume of changes. I've actually stopped pretending I can review everything in detail because it makes me a bottleneck in the process. Anything that makes reviewing easier is welcome.
To me, stacked PRs seems overly complicated. It seems to boil down to propagating git rebases through stacks of interdependent branches.
I'm fine with that as long as I don't have to deal with people force pushing changes and routinely rewriting upstream history. It's something you probably should do in your own private fork of a repository that you aren't sharing with anyone. Or if you are, you need to communicate clearly. But if the goal is to produce a stack of PRs that in the end merge cleanly, stacked PRs might be a good thing.
As soon as you have multiple collaborators working on a feature branch force pushing can become a problem and you need to impose some rules. Because otherwise you might end up breaking people's local branches and create work for them. The core issue here is that in many teams, people don't actually fork the main repository and have push access to the main repository. Which emulates the central repository model that people were used to twenty years ago. Having push access is not normal in most OSS projects. I've actually gotten the request from some rookie developers that apparently don't get forking to "please give me access to your repository" on some of my OSS projects.
A proper pull request (whether stacked or not) to an OSS project needs to be clean. If you want to work on some feature for weeks you of course need mechanisms to stay on top of up stream changes. OSS maintainers will probably reject anything that looks overly messy to merge. That's their job.
The tooling for that already exists, since a PR can consist of multiple Git commits and you can look at them separately in the UI. I don't know whether agents are any good at navigating that, but if not, they won't do any better with stacked PRs. Stacked PRs do create some new affordances for the review process, but that seems different from what you're looking for.
Looking at multiple commits is not a good workflow:
* It amounts to doing N code reviews at once rather than a few small reviews which can be done individually
* Github doesn't have any good UI to move between commits or to look at multiple at once. I have to find them, open them in separate tabs, etc.
* Github's overall UX for reviewing changes, quickly seeing a list of all comments, etc. is just awful. Gerrit is miles ahead. Microsoft's internal tooling was better 16 years ago.
* The more commits you have to read through at once the harder it is to keep track of the state of things.
I have had a lot of success with Claude and jj, telling it to take the stack of work it's done and build me a new stack on top of trunk that's centered around ease of reviewing.
Maybe there’s a git trick I don’t know, but I’ve found making small branches off each other painful. I run into trouble when I update an earlier branch and all the dependent branches get out of sync with it. When those earlier branches get rebased into master it becomes a pain to update my in-progress branches as well
If I understood you correctly, you want to propagate changes in a branch to other branches that depend on it? Then --update-refs is for you[1]. That way, you only need to update the "latest" branch.
Stacking branches for any extended period of time is definitely a poor mixing of the concepts of branches and commits. If you have a set of changes you need to keep in order, but you also need to maintain multiple silos where you can cleanly allow the code to diverge, that divergence constitutes the failure of your efforts to keep the changes in order.
Until you can make it effortless, maintaining a substantial commit structure and constantly rebasing to add changes to the proper commit quickly turns into more effort than just waiting to the end and manually editing a monster diff into multiple sensible commits. But we take the challenge and tell ourselves we can do better if we're proactive.
This just reeks to me of bad practice. Why use this as opposed to breaking your change into smaller PRs and merging them individually behind a feature flag or similar? With this, you have a marginally better UX for reviewing through the Github website, but the underlying issues are the same. The change being introduced is not sufficiently testable by itself, or it's (somehow) too tightly coupled to other parts of the UI/codebase that it can't be split. You still need to test for integration issues at every point of the stack, and some architecture issues or points of code reuse can't be seen from stacked changes like this.
Not for me, but I'm glad it fits other people's workflows. I just hope it doesn't encourage people to try make poorly reasoned changes!
When I've reached for stacked PRs (in the past, not using this feature) it's precisely because I've split my change into smaller PRs being merged individually.
I've just written those smaller PRs at once, or in quick enough succession that the previous PRs weren't merged before the later ones were ready. And the later ones relied on the previous ones because that's how working on a feature works.
The earlier PRs are absolutely reviewable and testable without relying on the later ones. The later ones are just treating the earlier ones as part of the codebase. I.e. everything here looks like two different PRs except the timing.
An obvious example would be "implement API for a feature" and then "implement UI that uses that API". Two different PRs. The second fundamentally relies on the first.
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
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.
The teams that I have worked with still apply the philosophy you’re describing, but they consider PRs to be the “commit”, i.e. the smallest thing that is sane to apoly individually.
Then the commits in the PR are not held to the standard of being acceptable to apply, and they are squashed together when the PR is merged.
This allows for a work flow in which up until the PR is merged the “history of developing the PR” is preserved but once it is merged, the entire PR is applied as one change to the main branch.
This workflow combined with stacked PRs allows developers to think in terms of the “smallest reviewable and applicable change” without needing to ensure that during development their intermediate states are safe to apply to main.
Exactly! A stack of PRs is really the same beast as a branch of commits.
The traditional tools (mailing-lists, git branches, Phabricator) represented each change as a difference between an old version of the code and the proposed new version. I believe Phabricator literally stored the diff. They were called “diffs” and you could make a new one by copying and pasting into a <textarea> before pressing save*.
The new fangled stuff (GitHub and its clones) recorded your change as being between branches A and B, showed you the difference on the fly, and let you modify branch B. After fifteen years of this we are now seeing the option for branch A to be something other than main, or at least for this to be a well supported workflow.
In traditional git land, having your change as a first class object — an email or printout or ph/D1234 with the patch included — was the default workflow!
Still not sure this is the right solution. My problem is if one of your first stages gets rejected in review or requires significant changes, it invalidates so much work that comes after it. I've always when possible preferred to get small stuff merged in to production as it happens rather than build an entire feature and put it up for review.
this works much better in Phabricator because commits to diffs are a 1:1 relationship, diffs are updated by amending the commit, etc., the Github implementation does seem a bit like gluing on an additional feature.
Right, a PR is "just" a set of commits (all must be in the same branch) that are intended to land atomically.
Stacked PRs are not breaking up a set of commits into divisible units. Like you said, you can already do that yourself. They let you continue to work off of a PR as your new base. This lets you continue to iterate asynchronously to a review of the earlier PRs, and build on top of them.
You often, very often, need to stage your work into reviewer-consumable units. Those units are the stack.
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?)
> 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
As we have it designed currently, you would have to wait for CI to pass on the bottom two and then you can merge the bottom two in one step. The top of the stack would then get rebased, which will likely trigger another CI run.
Thanks for the callout - we'll update those docs to make it clear multiple PRs can be merged at once.
It was announced in like November of last year, so it's certainly taken some time. The announcement was by some senior management at GitHub, so it has some degree of buy-in.
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.
Rebasing after merging a base branch becomes a pain though, when you do this. IMO the CLI will be nice to automate the process of rebasing each branch on its parent.
The CLI is completely optional, you can create stacked PRs purely via the UI.
Also the rationale for having a chain of branches pointing to each other was so the diff in a PR shows just the relevant changes from the specific branch, not the entire set of changes going back to the parent/trunk.
+1 this isn’t something new, it’s been possible all along in native git if you’re willing to do branch management and rebasing yourself. Just without the fancy UI / stack map.
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.
Imagine you have some task you are working on, and you wish to share your progress with people in bite sized chunks that they can review one at a time, but you also don’t want to wait for their reviews before you continue working on your task.
Using a stacked set of PRs you can continue producing new work, which depends on the work you’ve already completed, without waiting for the work you’ve already completed to be merged, and without putting all your work into one large PR.
in Phabricator you either abandon the original diffs entirely, or you amend them. you don't just stack more commits with meaningless messages like "WIP", "lint fix", etc. on top.
> 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.
this is now what stacked diffs are for. stacked diffs doesn't mean putting up code that isn't ready. for example you are updating some library that needs an API migration, or compiler version that adds additional stricter errors. you need to touch hundreds of files around the repository to do this. rather than putting up one big diff (or PR) you stack up hundreds of them that are trivial to review on their own, they land immediately (mitigating the risk of merge conflicts as you keep going) then one final one that completes the migration.
I also branch out, and rebase. Also, keep updating and rebasing until merged. It’s tedious when PR take ages for approval, as I keep creating new branches on top of each other.
So, when I saw this announcement seemed interesting but don’t see the point of it yet.
I think the core conceptual difference between a stacked diff and PRs as we use them in open source is the following:
A PR is basically a cyberspatial concept saying "I, as a dog on the internet, am asking you to accept my patches" like a mailing list - this encourages trying to see the truth in the whole. A complete feature. More code in one go because you haven't pre-agreed the work.
Stacks are for the opposite social model. You have already agreed what you'll all be working on but you want to add a reviewer in a harmonious way. This gives you the option to make many small changes, and merge from the bottom
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.
Curious how / how well it deals with conflicts in the different branches that are part of the stack. Is there some support for managing that, or what happens when two of the branches don't rebase / merge cleanly?
Is this going to be a part of triage task? If so, it makes sense. Whether a human developer or an AI made a big PR, AI goes review it and if necessary makes stacked PRs. I don’t see any human contributors using this feature to be honest because it’s an extra work and they should have found a better way to suggest a large PR.
I think the only thing I miss from GitLab was being able to make merge requests depend on other merge requests, even across repositories. So I could make a backend repository MR depend on a library repository MR, and even enable auto-merge that’d fire when the backend MR was reviewed and the dependency was also merged.
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).
It looks like in the UI if you base a PR on another branch you can just check a box to make it a stack. So I don't think you have to know the full shape of the stack in advance unless you're using the cli.
I use jj to stack branches so i'll just be using the UI to do github pr stacks.
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?
> If you have 10 commits for frontend and 10 for backend
In this model, you tend to want to amend, rather than add more commits. And so:
> they might start with 5 for backend, then 5 commits to each branch to iron out the interface and communication,
You don't add more commits here, you modify the commits in your stack instead.
> Now I need to rebase my frontend branch to sit on B6?
Yes, when you change something lower in the stack, the things on top need to be rebased. Because your forge understands that they're stacked, it can do this for you. And if there's conflicts, let you know that you need to resolve them, of course.
But in general, because you are amending the commits in the stack rather than adding to it, you don't need to move anything around.
> And wouldn't this separation normally be obvious e.g. by paths?
In the simplest case, sure. But for more complex work, that might not be the case. Furthermore, you said you have five commits for each; within those sets of five, this separation won't exist.
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`
I think it is conceptually the same but multiple PRs gives you the tools to manage the commits properly which GitHub is missing. You can't do the equivalent of `git rebase -i` in the GitHub UI to squash a fixup into a previous commit. Having each change in it's own PR enables that workflow using the existing GitHub UI.
Stacked PRs track changes through updates and can be integrated progressively as they get validated.
They also allow reviewing commits individually, which is very frustrating to do without dedicated support (unless you devolve back to mailing list patch stacks).
I don't think this is it. The main driver is that several operations in GH are scoped around a PR, not a commit. So the reason you need stacked PRs is that the layer of tooling above `git` is designed to work on logical groups of commits called a PR.
Commits are immutable and you never know which feedback goes stale when you add another commit.
I'm not a huge fan, since stacked PRs mean the underlying issues don't get addressed (reviews clearly taking too long, too much content in there), but it seems they want something that works for their customers, right now, as they work in real life.
It would be the same if commits were meaningful things. But typically they're not. The PR is the meaningful thing. A commit is something like "fix the lint error".
CI runs on each PR, you get a whole PR message and discussion/review interface for each PR. Each PR can itself consist of multiple commits. You can have stacked PRs from different authors (though from another comment it sounds like they may not have implemented that).
It's a big improvement (assuming they've done it right).
Phabricator is open source and has been for years. It has had a bumpy ride over the last few years though. Although I guess having written that I assume the internal meta one is much better
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.)
> a chain of small, focused pull requests that build on each other — each one independently reviewable.
I have never understood what this even means.
Either changes are orthogonal (and can be merged independently), or they’re not. If they are, they can each be their own PR. If they’re not, why do you want to review them independently?
If you reject change A and approve change B, nothing can merge, because B needs A to proceed. If you approve change A and reject change B, then the feature is only half done.
Is it just about people wanting to separate logical chunks of a change so they can avoid get distracted by other changes? Because that seems like something you can already do by just breaking a PR into commits and letting people look at one of those at a time.
I’ve tried my best to give stacked-diff proponents the benefit of the doubt but none of it actually makes sense to me.
The canonical example here is a feature for a website that requires both backend and frontend work. The frontend depends on the backend, but the backend does not depend on the frontend. This means that the first commit is "independent" in the sense that it can land without the second, but the second is not, hence, a stack. The root of the stack can always be landed independently of what is on top of it, while the rest of the stack is dependent.
> If they’re not, why do you want to review them independently?
For this example, you may want review from both a backend engineer and a frontend engineer. That said, see this too though:
> that seems like something you can already do by just breaking a PR into commits and letting people look at one of those at a time.
If you do this in a PR, both get assigned to review the whole thing. Each person sees the code that they don't care about, because they're grouped together. Notifications go to all parties instead of the parties who care about each section. Both reviews can proceed independently in a stack, whereas they happen concurrently in a PR.
> If you approve change A and reject change B, then the feature is only half done.
It depends on what you mean by "the feature." Seen as one huge feature, then yes, it's true that it's not finished until both land. But seen as two separate but related features, it's fine to land the independent change before the dependent one: one feature is finished, but the other is not.
If the layers of a stack have a disjoint set of reviewers things are viewed in separation which might lead to issues if there is no one reviewing the full picture.
> If you do this in a PR, both get assigned to review the whole thing. Each person sees the code that they don't care about, because they're grouped together.
There are two separate issues you’re bringing up:
- Both groups being “assigned” the PR: fixable with code owners files. It’s more elegant than assigning diffs to people: groups of people have ownership over segments of the codebase and are responsible for approving changes to it. Solves the problem way better IMO.
- Both groups “seeing” all the changes: I already said GitHub lets you view single commits during PR review. That is already a solved problem.
And I didn’t even bring up the fact that you can just open a second PR for the frontend change that has the backend commit as the parent. Yes, the second PR is a superset of the first, but we’ve already established that (1) the second change isn’t orthogonal to the first one and can’t be merged independently anyway, and (2) reviewers can select only the commits that are in the frontend range. Generally you just mark the second PR as draft until the first one merges (or do what Gitlab does and mark it as “depends on” the first, which prevents it from merging until the first one is done.) The first PR being merged will instantly make the second PR’s diff collapse to just the unique changes once you rebase/merge in the latest main, too.
All of this is to explain how we can already do pretty much all of this. But in reality, it’s silly to have people review change B if change A hasn’t landed yet. A reviewer from A may completely throw the whole thing out and tell you to start over, or everything could otherwise go back to the drawing board. Making reviewers look at change B before this is done, is a potential for a huge waste of time. But then you may think reviewers from change B may opt to make the whole plan go back to the drawing board too, so what makes A so special? And the answer is it’s both a bad approach: just make the whole thing in one PR, and discuss it holistically. Code owners files are for assigning ownership, and breaking things into separate commits is to help people look at a subset of the changes. (Or just, like, have them click on the folder in the source tree they care about. This is not a problem that needs a whole new code review paradigm.)
- the first PR in the stack creates a search index.
- the second one adds a search API handler.
- the last few do the UI.
these are all related. you are right that you can do this by breaking a change into commits, and effectively that is what i do with jujutsu. when i submit my commits to the UI, they form a PR stack. the commits are individually reviewable and updatable in this stacking model.
gh's model is inherently different in that they want you to create a new branch for every new change, which can be quite a nuisance.
> - the first PR in the stack creates a search index.
> - the second one adds a search API handler.
> - the last few do the UI.
So you're saying you're going to merge (and continuously integrate, perhaps to production) a dangling, unused search index, consuming resources with no code using it, just to make your review process easier?
It's very depressing that review UX is so abysmal that you have to merge features before they're done just to un-fuck it.
Why can't the change still be a big branch that is either all merged or not... and people can review it in chunks? Why do we require that the unit of integration equals the unit of review?
The perverse logic always goes something like this:
"This PR is too big, break it up into several"
Why?
"It's easier to review small, focused changes"
Why can't we do that in one PR?
"Because... well, you see GitHub's UI makes it really hard to ..."
And that ends up being the root-cause answer. I should be able to make a 10,000 line change in a single commit if I want, and reviewers should be able to view subsets of it however they want: A thread of discussion for the diffs within the `backend` folder. A thread of discussion for the diffs within the `frontend` folder, etc etc. Or at the very least I should be able to make a single branch with multiple commits based on topic (and under no obligation for any of them to even compile, let alone be merge-able) and it should feel natural to review each commit independently. None of this should require me to contort the change into allowing integration partially-completed work, just to allow the review UX to be manageable.
you're upgrading the repository from language version 1 to 2, version 2 adds new compiler errors that rejects some old code, or the library has removed some old deprecated API the repository was still using in some places—the key here being that it can't be something that needs to be completely atomic.
you have hundreds or thousands of files to fix. that is unreviewable as a single commit, but as a per-file, per-library, per-oncall, etc. commit it is not that bad.
> you have hundreds or thousands of files to fix. that is unreviewable as a single commit, but as a per-file, per-library, per-oncall, etc. commit it is not that bad
Why is it intrinsically unreviewable as a single commit? Why can't the discussion/review system allow scoping discussions to a single folder of the change, or a single library, or a particular code-owner's "slice" of the repo, etc? The answer to this question is always unsatisfactory to me. It always ends up being "because GitHub's UI makes it hard to <foo>" and it's just taken as an immutable law of the universe that we're stuck with that UI's limitations.
If a change is huge, find some basis by which to discuss it in smaller chunks. That basis doesn't have to be the PR itself (such that you have to make smaller PR's to make discussion manageable.) It can be a subdirectory of the diff. A wildcard-match over the source files. Whatever the case needs to be, the idea is still that the discussion UX shouldn't make reviewing large changes painful.
Why do we tolerate the fact that GitHub doesn't let you say "approved for changes in `frontend/*`" or "approved for the changes I'm a code-owner of", and have the PR check system mark the PR as approved once all slices have been approved? Why do we tolerate that a thousand-file change is "unreviewable"? Instead we have to change our unit of integration, allowing partially-complete work to be merged, just because the review UX sucks.
Why would you waste time faffing about building B on top of a fantasy version of A? Your time is probably better spent reviewing your colleague’s feature X so they can look at your A.
Couldn’t they do that in one PR? Seriously, couldn’t you just say “hey Alice, could you review the A parts of this PR” and “hey Bob, could you review the B parts”, then only merge once both of them approve? Even GitHub, for all its faults, supports code owners files, such that this can even be policy.
> 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.
Interesting to see how their CLI compares with GitLab's CLI interface for stacked diffs (the only support they offer at the moment): https://docs.gitlab.com/user/project/merge_requests/stacked_.... Most things are the same (up/down/top/bottom vs. next/prev/first/last, init vs. create), but both feel quite limiting. I've heard of other systems such as Gerrit that offer better native support, but have not tried out any for myself.
The `gh stack` CLI sounds essential for people using git, but I hope it doesn't become required, as people using things like jj/sl should be able to work with stacks. `gs submit`/`gs push` being the interface is fine, but `gs init` and `gs add` should be optional.
They confirmed below that you should be able to use this with jj just fine, just like you can already use gh to create a PR that you've authored with jj: https://news.ycombinator.com/item?id=47759426
People have been building stacked PR workflows on top of GitHub for a while now. It's great to see that the message seems to have finally landed at GitHub, but what is actually new here in GitHub itself (i.e., not counting the gh CLI tool)?
There seems to be a native stack navigation widget on the PR page, which is certainly a welcome addition.
The most important question though is whether they finally fixed or are going to fix the issues that prevent submitting stacked PRs from forks. I don't see any indication about that on the linked page.
Pretty cool to see stacks being given due attention. Also check out git-spice, which works with Gitlab (possibly others). Personally I use git-spice in place of all the conventional git commands.
My main question about this is does it keep review history properly after a rebase to restack PRs? Eg if I have reviewed PR for branch A and now its been rebased onto B by this tool and then more changes are made to A, does "review changes since" work in A's PR? This has been the main thing stopping me from wanting to use rebase to stack PRs and if they've fixed this somehow then I'm interested.
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.
Yeah, not sure what this solves that doing multiple PRs from one feature branch into another doesn't solve. But building behavior that into AI agents wouldn't be cool enough, I guess.
Wow i really need this, we had a refactor our monorepo (dotnet 8 -> 10 and angular 19 -> 21) which resulted in many small changes (like refactoring to signals, moving components to standalone) and we try to group changes into commits by what was fixed, but this had the downside of some commits beeing huge while others small, this would have helped us alot grouping commits together and having cleaner commit messages.
One mistake I see across many organizations is that sometimes they overthink how much order should matter.
Sure, your application has a dependency on that database, but it doesn't necessarily mean you can't deploy the application before having a database. If possible, make it acceptable for your application to stay in a crashloop until your database is online.
I agree with you and further will add that modularity+atomicity are the ideal state for the vast majority of software applications… but in reality, most organizations can not afford to rewrite their software to the extent required to achieve this, if it wasn’t planned from the start.
This is probably driven to be more usable with AI agents, but smaller prs can create more code as they need to enforce more backwards compability, this can also lead to more code or more maintenance work.
Honestly I don’t see the benefit of smaller prs, except driving vanity scores?
Even though moments where I would reach for it are rare, this is a very welcome feature. In times when I could have used it, it was not difficult to emulate via more branches, consistent naming, referencing the PRs, etc. Not difficult, but definitely tedious, and always left me feeling less organized than I like to feel.
Very much looking forward to getting this on Renovate - we require squash-merge via Merge Queue (with no per-PR override available in GitHub, despite asking) and so when I've got multiple changes, it's a lot of wrangling and rebasing
If this works as smoothly as it sounds, that'll significantly reduce the overhead!
Just when I’ve gotten used to having 3 or more PRs in parallel with a local octopus working tree with jj. Maybe my colleagues will see the light at least.
great, I'll directly compare it to graphite.com - the main point really is the user interface in my opinion. Still a bit sceptical whether github can out-deliver here, but happy to be proven wrong!
Has anyone already tried that was a graphite user before?
“You cannot merge a PR in the middle of the stack before the PRs below it are merged.”
Huh? Some stacks need to land all at once and need to be reviewed (and merged) from the top down. It’s not uncommon, in my org at least, to review an entire stack and merge 3 into 2 and then 2 into 1 and then 1 into main. If 2 merges before 3, you just rebase 3 onto 1.
At first I thought this was a user submitted project due to the subdomain of github.com but then realize the subdomain is also github. Is this an official channel for this sort of thing? Surprised this isn't on the official blog.
There’s a special case where certain official orgs can continue to use github.com instead of github.io for their Pages domain, and that’s how you end up with:
For sure. If you are in a monorepo this solution works but if you have distinct microservice repositories it would help to coordinate pr #827 on repo-A and pr #1092 on repo-B are related and need to ship in a certain order.
How would that work? Commits in different repos aren't ordered relative to one another. I suppose you could have a "don't let me merge this PR until after this other PR is merged" feature, but you could do that with a GitHub Action; it doesn't really need dedicated backend or UI support.
In practical terms: I manually write a list of PRs, and maintain that list in the description of each of the PRs. Massive duplication. But it clearly shows the merge train.
I'm surprised no-one has commented on the "sign up for the waitlist" button being a Microsoft Office form that wants your email address and GitHub handle. This feels like an elaborate phishing attack more than a serious feature announcement.
> This is a docs site that was made to share the spec and CLI for private preview customers that ended up getting picked up. This will move to GitHub docs once it’s in public preview.
The biggest challenge for us are PRs that need to be coordinated across multiple repos. API + client for example. It doesn't sound like stacked PRs solve that problem, right? Description specifically states single repo.
How does this work with a tool like jujutsu that provides native support for stacking and preferably don't have a separate tool mucking about with state?
Using a platform-specific tool feels like vendor lock-in. Given the recent trends, behavior, and activity of GitHub, it sure seems like this is the opposite of what the world should be going for generally.
Also if someone could help me understand: Are these so-called stacked commits not possible with multiple commits on a single branch? I prefer to write my commits as atomic, independent, related changes, on a single branch, with both Git and Mercurial. I am apparently missing something: why can't a better UI simply show a multi-change PR?
The CLI is not required and you can push up your bookmarks as branches and open stacked PRs via the UI. You can also use the gh CLI to just create the stacked PRs on github.com (essentially an API wrapper), without using it to manage local state.
In the tool I wrote, you have a single branch with linear history. PRs in the chain are demarcated via commit messages. You then don't need any special rebase / sync commands -- you can use regular `git rebase -i` to reorder commits or edit a commit in the middle of a stack. Literally the only special command I need is "push this branch to github as multiple PRs".
Anyway I hope that alongside the branch-based you've built tool in `gh` that there will be an API that I can target.
Yeah features need to be released as GA (general availability) before they can be included in GHES. I don't have a definitive timeline, but it will likely be end of this year or early next.
github.github.com? Not the first time github does something highly weird with their domains (like publishing docs from a subdomain of their public github pages service)
I think they have a culture of circumventing 'official' channels and whoever is in charge of a thing is whoever publishes the thing.
I think it's a great way to train users to get phished by github impostors, if tomorrow we see an official download from official.github.com or even official-downloads.github.io, sure it's phishy, but it's also something that github does.
It's also 100% the kind of issues that, if it happens, the user will be blamed.
I would recommend github to stop doing this stuff and have a centralized domain to publish official communications and downloads from. Github.github.com? Come on, get serious.
TL;DR: DO NOT DOWNLOAD ANYTHING from this site, (especially not npm/npx/pnpm/bun/npjndsa) stuff. It's a Github Pages site, just on a subdomain that looks official, theoretically it might be no different from an attacker to obtain access to dksabdkshab.github.com than github.github.com. Even if it is official, would you trust the intern or whoever managed to get a subdomain to not get supply chained? github.github.com just think about it.
in github's defense. This is a bit more nuanced, less objectively wrong domain posture issue. It will only matter if one security mechanism (subdomain control) fails.
The quoted microsoft examples are way worse. I see this with outbound email systems a lot, which is especially dangerous because email is a major surface of attack.
If only there were some way to logically break up large pull requests into smaller pieces... Some way of creating a checkpoint with a diff including your changes, and some kind of message explaining the context behind the change... some way to "commit" a change to the record of the repository...
Part of the idea behind stacked PRs is to keep your commits focused and with isolated changes that are meaningful.
A stacked PR allows you to construct a sequence of PRs in a way that allows you to iterate on and merge the isolated commits, but blocks merging items higher in the stack until the foundational changes are merged.
I've done this manually by building a big feature branch and asking an LLM to extract out functionality for a portion of it.
For the former, it would seem to split based on frontend/backend, etc. rather than what semantically makes the most sense and for the latter it would include changes I don't want and forget some I do want. But I haven't tried this a lot.
Not really. Without seeing the entire changeset for a PR, you'd have to mentally keep track of what the current state of everything is unless you're a commit minimalist and presquash.
I feel like we already have enough abstractions in this space. Having any constraints at all in your tools is actually a good thing. PRs on top of ordinary git was a good step. This seems like one too many.
I honestly don't even get the PR addiction. Github has shaped devs workflows way too much. My best experience with git was when I realized that I can just have an blatantly simple workflow and explain it even to the junior-est dev in a few minutes. The reliance on github is somehow telling me that people stopped thinking about things they can actually control.
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).
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.
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.
8 replies →
I remember using darcs, but the repos I was using it with were so small as to performance really not mattering…
1 reply →
This matches my experience 100%. I was about to write a similar comment before I see yours.
I just used it because I preferred the UX.
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!
35 replies →
Git is not remotely fast for large projects.
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.
Most editors have some kind of way to review github PRs in your editor. VSCode has a great one. I use octo.nvim since I use neovim.
6 replies →
I miss the Phabricator review UI so much.
Same here. Don't understand why Github hasn't supported this until now. I'm tired of reviewing PRs with thousands of lines of changes, which are getting worse nowadays with vibe coding.
What does Facebook use internally these days. I'm amazed that the state of review tools is still at or behind what we had a decade ago for the most part.
3 replies →
Me too. And I'm speaking from using it at Rdio 15 years ago.
Nothing since (Gerrit, Reviewboard, Github, Critique) has measured up...
1 reply →
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/
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-...
Good. That's the point.
3 replies →
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?
It's basically trying to bring the stacked diff workflow pioneered by Phabricator to GitHub.
The idea is that it allows you to better handle working on top of stuff that's not merged yet, and makes it easier for reviewers to review pieces of a larger stack of work independently.
It's really useful in larger corporate environments.
I've used stacked PRs when doing things like upgrading react-native in a monorepo. It required a massive amount of changes, and would be really hard to review as a single pull request. It has to be landed all at once, it's all or nothing. But being able to review it as smaller independent PRs is helpful.
Stacking PRs is also useful even when you don't need to merge the entire stack at once.
> stacked diff workflow pioneered by Phabricator
Ahem, pioneered by gerrit. But actually, I'm almost certain even that wasn't original art. I think gerrit just brought it to git.
https://www.gerritcodereview.com/about.html
8 replies →
I'm not in a large corporate environment, but that also means we're not always a well oiled machine, and sometimes i am writing faster than the reviewer can review for a period of time -- and i really need the stacking then too.
What if main/master moves in between reviews?
4 replies →
Constantly rewriting git history with squashes, rebases, manual changes, and force pushes has always seemed like leaving a loaded gun pointed at your foot to me.
Especially since you get all of the same advantages with plain old stream on consciousness commits and merges using:
git merge --no-ff
git log --first-parent
git bisect --first-parent
I find rebases are only a footgun because the standard git cli is so bad at representing them - things like --force being easier to write than --force-with-lease, there being no way to easily absorb quick fixes into existing commits, interdiffs not really being possible without guesswork, rebases halting the entire workflow if they don't succeed, etc.
I've switched over pretty much entirely to Jujutsu (or JJ), which is an alternative VCS that can use Git as its backend so it's still compatible with Github and other git repos. My colleagues can all use git, and I can use JJ without them noticing or needing to care. JJ has merges, and I still use them when I merge a set of changes into the main branch once I've finished working on it, but it also makes rebases really simple and eliminates most of the footguns. So while I'm working on my branch, I can iteratively make a change, and then squash it into the commit I'm working on. If I refactor something, I can split the refactor out so it's in a separate commit and therefore easiest to review and test. When I get review feedback, I can squash it directly into the relevant commit rather than create a new commit for it, which means git blame tends to be much more accurate and helpful - the commit I see in the git blame readout is always the commit that did the change I'm interested in, rather than maybe the commit that was fixing some minor review details, or the commit that had some typo in it that was fixed in a later commit after review but that relationship isn't clear any more.
And while I'm working on a branch, I still have access to the full history of each commit and how it's changed over time, so I can easily make a change and then undo it, or see how a particular commit has evolved and maybe restore a previous state. It's just that the end result that gets merged doesn't contain all those details once they're no longer relevant.
2 replies →
Until someone merges master into their feature branch rather than rebasing it. (And then that branch later gets merged.)
1 reply →
I agree. PR merges for me are bisect points. That's when changes are introduced. Individual commits don't even always build.
And I don't rebase or squash because I need provenance in my job.
We’ve got this over on Tangled. :) https://tangled.org
You are describing gerrit.
https://www.gerritcodereview.com/
the best implementation i've worked with was SuperSmartLog (SSL) at Meta, which was open-sourced at interactive smartlog (https://sapling-scm.com/docs/addons/isl/). There are also extension for it in VSCode, etc.
Surprisingly it never gained the adoption it deserved.
Workflows can vary, but what I like:
PR/MR is an "atomic" change (ideally the smallest change that can be landed separately - smallest makes it easier to review, bisect and revert)
Individual commits (or what "versions" are in Phabricator) are used for the evolution of the PR/MR to achieve that change.
But really I have 2 use cases for the commits:
1. the PR/MR is still too big, so I split it into individual commits (I know they will land together)
2. I keep the history of the evolution of the PR/MR in the commits ("changed foo to bar cause its a better approach")
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.
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-...
I think the CLI is useful for pushing. What do you use to push all the rebased child branches?
My git config for pushing is set to push.default=current. For rebased stacks I have an alias that does this:
In other words, I force push all branches that have a matching upstream by changing my config on the fly.
> 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.
...or you don't bother with all that and simply do:
- gh stack init
- gh stack push
- gh stack submit
Sure, that's possible. I can also use the GitHub app and use a Git abstraction where I don't have to understand Git at all.
The point is that I want to use Git, a tool and skill that is portable to other platforms.
1 reply →
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.
My only complaint off the bat is the reliance on the GH CLI, which I don't use either. But maybe by the time it's GA they'll have added UI support.
You can in fact do this from the web UI: https://github.github.com/gh-stack/guides/ui/#creating-a-sta...
2 replies →
Stacked PRs can be created via the UI, API, or CLI.
You can also run a combination of these. For ex, use another tool like jj to develop locally, push up the branches, and use the gh CLI to batch create a stack of n PRs, without touching local state.
1 reply →
It seems partially exposed in the UI with that dropdown. There's an 'add' and 'unstack' button.
Probably relies on some internal metadata.
CLI is great because now I can tell my AI agent to do it. “Fix all dependabot security issues (copy logs) and run tests to validate functionality. Create each dependency as its own stack (or commit) so that contributors may review each library update easily.”
Wait 10 minutes and you’re done.
1 reply →
Why don't you use the CLI?
Huh interesting, my mental model is unable to see any difference between them.
I mean a branch is just jamming a flag into a commit with a polite note to move the flag along if you're working on it. You make a long trail, leave several flags and merge the whole thing back.
Of course leaving multiple waypoints only makes sense if merging the earlier parts makes any sense, and if the way you continue actually depends on the previous work.
If you can split it into several small changes made to a central branch it's a lot easier to merge things. Otherwise you risk making a new feature codependent on another even if there was no need to.
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..
Yes, we handle this both in the CLI and server using git rebase --onto
So for ex in this scenario:
When PR 1 and 2 are squash merged, main now looks like:
Then we run the following:
Which rewrites branch3 to:
This operation moves the unique commits from the unmerged branch and replays them on top of the newly squashed commits on the base branch, avoiding any merge conflicts.
Conflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main.
No idea if this feature fixes this.
Edit: Hopefully `gh stack sync` does the rebasing correctly (rebase --onto with the PR A's last commit as base)
> Conflicts spawn most likely because PR A was squashed, and once you squash Git doesn't know that PR B's ancestors commits are the same thing as the squashed commit on main.
Yeah, and I kind of see how git gets confused because the squashed commits essentially disappear. But I don't know why the rebase can't be smart when it sees that file content between the eventual destination commit (the squash) is the same as the tip of the branch (instead of rebasing one commit at a time).
1 reply →
I'm not sure I follow your workflow exactly. If PR B is merged, then I'd expect PR A to already be merged (I'd normally branch off of A to make B.)
That said, after the squash merge of A and git fetch origin, you want something like git rebase --update-refs --onto origin/main A C (or whatever the tip of the chain of branches is)
The --update-refs will make sure pr B is in the right spot. Of course, you need to (force) push the updated branches. AFAICT the gh command line tool makes this a bit smoother.
I agree that this is annoying and unintuitive. But I don’t see the simplest solution here, so:
All you need to do is pull main, then do an interactive rebase with the next branch in your stack with ‘git rebase -i main’, then drop all the commits that are from the branch you just merged.
I typically prefix my commit messages with the ticket number to make it easier to spot the commits to drop.
If I'm following correctly, the conflicts arise from other commits made to main already - you've implicitly caught branch A up to main, and now you need catch branch B up to main, for a clean merge.
I don't see how there is any other way to achieve this cleanly, it's not a git thing, it's a logic thing right?
I've no issue with the logic of needing to update feature branches before merging, that's pretty bread and butter. The specific issue with this workflow is that the "update branch" button for PR B is grayed out because there are these hallucinated conflicts due to the new squash commit.
The update branch button works normally when I don't stack the PRs, so I don't know. It just feels like a half baked feature that GitHub automatically changes the PR target branch in this scenario but doesn't automatically do whatever it takes for a 'git merge origin/main' to work.
1 reply →
No, it's a Git thing arising from squash commits. There are workflows to make it work (I've linked the cleanest one I know that works without force pushing), but ultimately they're basically all hacks. https://www.patrickstevens.co.uk/posts/2023-10-18-squash-sta...
2 replies →
Oh that's annoying, seems to me there wouldn't have been an issue if you just merged B into A after merging A into main, or the other way around but that already works fine as you pointed out.
I mean if you've got a feature set to merge into dev, and it suddenly merges into main after someone merged dev into main then that's very annoying.
You "just" need to know the original merge-base of PR B to fix this. github support is not really required for that. To me that's the least valuable part of support for stacked PRs since that is already doable yourself.
The github UI may change the target to main but your local working branch doesn't, and that's where you `rebase --onto` to fix it, before push to origin.
It's appropriate for github to automatically change the target branch, because you want the diff in the ui to be representative. IIRC gitlab does a much better job of this but this is already achievable.
What is actually useful with natively supported stacks is if you can land the entire stack together and only do 1 CI/actions run. I didn't read the announcement to see if it does that. You typically can't do that even if you merge PR B,C,D first because each merge would normally trigger CI.
EDIT: i see from another comment (apparently from a github person) that the feature does in fact let you land the entire stack and only needs 1 CI run. wunderbar!
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.
It's easier to pile on a lot of changes with AI assisted workflows. And reviewing all that is definitely a challenge just because of the volume of changes. I've actually stopped pretending I can review everything in detail because it makes me a bottleneck in the process. Anything that makes reviewing easier is welcome.
To me, stacked PRs seems overly complicated. It seems to boil down to propagating git rebases through stacks of interdependent branches.
I'm fine with that as long as I don't have to deal with people force pushing changes and routinely rewriting upstream history. It's something you probably should do in your own private fork of a repository that you aren't sharing with anyone. Or if you are, you need to communicate clearly. But if the goal is to produce a stack of PRs that in the end merge cleanly, stacked PRs might be a good thing.
As soon as you have multiple collaborators working on a feature branch force pushing can become a problem and you need to impose some rules. Because otherwise you might end up breaking people's local branches and create work for them. The core issue here is that in many teams, people don't actually fork the main repository and have push access to the main repository. Which emulates the central repository model that people were used to twenty years ago. Having push access is not normal in most OSS projects. I've actually gotten the request from some rookie developers that apparently don't get forking to "please give me access to your repository" on some of my OSS projects.
A proper pull request (whether stacked or not) to an OSS project needs to be clean. If you want to work on some feature for weeks you of course need mechanisms to stay on top of up stream changes. OSS maintainers will probably reject anything that looks overly messy to merge. That's their job.
The tooling for that already exists, since a PR can consist of multiple Git commits and you can look at them separately in the UI. I don't know whether agents are any good at navigating that, but if not, they won't do any better with stacked PRs. Stacked PRs do create some new affordances for the review process, but that seems different from what you're looking for.
Looking at multiple commits is not a good workflow:
* It amounts to doing N code reviews at once rather than a few small reviews which can be done individually
* Github doesn't have any good UI to move between commits or to look at multiple at once. I have to find them, open them in separate tabs, etc.
* Github's overall UX for reviewing changes, quickly seeing a list of all comments, etc. is just awful. Gerrit is miles ahead. Microsoft's internal tooling was better 16 years ago.
* The more commits you have to read through at once the harder it is to keep track of the state of things.
6 replies →
I have had a lot of success with Claude and jj, telling it to take the stack of work it's done and build me a new stack on top of trunk that's centered around ease of reviewing.
I once threatened Claude have to learn JJ after doing some crazy git rebase gymnastics. The problem is clearly that I don't know jj
1 reply →
Maybe there’s a git trick I don’t know, but I’ve found making small branches off each other painful. I run into trouble when I update an earlier branch and all the dependent branches get out of sync with it. When those earlier branches get rebased into master it becomes a pain to update my in-progress branches as well
If I understood you correctly, you want to propagate changes in a branch to other branches that depend on it? Then --update-refs is for you[1]. That way, you only need to update the "latest" branch.
[1] https://andrewlock.net/working-with-stacked-branches-in-git-...
Stacking branches for any extended period of time is definitely a poor mixing of the concepts of branches and commits. If you have a set of changes you need to keep in order, but you also need to maintain multiple silos where you can cleanly allow the code to diverge, that divergence constitutes the failure of your efforts to keep the changes in order.
Until you can make it effortless, maintaining a substantial commit structure and constantly rebasing to add changes to the proper commit quickly turns into more effort than just waiting to the end and manually editing a monster diff into multiple sensible commits. But we take the challenge and tell ourselves we can do better if we're proactive.
3 replies →
If you visit the webpage it gives you integration instructions for agents
This just reeks to me of bad practice. Why use this as opposed to breaking your change into smaller PRs and merging them individually behind a feature flag or similar? With this, you have a marginally better UX for reviewing through the Github website, but the underlying issues are the same. The change being introduced is not sufficiently testable by itself, or it's (somehow) too tightly coupled to other parts of the UI/codebase that it can't be split. You still need to test for integration issues at every point of the stack, and some architecture issues or points of code reuse can't be seen from stacked changes like this.
Not for me, but I'm glad it fits other people's workflows. I just hope it doesn't encourage people to try make poorly reasoned changes!
When I've reached for stacked PRs (in the past, not using this feature) it's precisely because I've split my change into smaller PRs being merged individually.
I've just written those smaller PRs at once, or in quick enough succession that the previous PRs weren't merged before the later ones were ready. And the later ones relied on the previous ones because that's how working on a feature works.
The earlier PRs are absolutely reviewable and testable without relying on the later ones. The later ones are just treating the earlier ones as part of the codebase. I.e. everything here looks like two different PRs except the timing.
An obvious example would be "implement API for a feature" and then "implement UI that uses that API". Two different PRs. The second fundamentally relies on the first.
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
Yep, very happy with graphite at work.
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.
The teams that I have worked with still apply the philosophy you’re describing, but they consider PRs to be the “commit”, i.e. the smallest thing that is sane to apoly individually.
Then the commits in the PR are not held to the standard of being acceptable to apply, and they are squashed together when the PR is merged.
This allows for a work flow in which up until the PR is merged the “history of developing the PR” is preserved but once it is merged, the entire PR is applied as one change to the main branch.
This workflow combined with stacked PRs allows developers to think in terms of the “smallest reviewable and applicable change” without needing to ensure that during development their intermediate states are safe to apply to main.
Exactly! A stack of PRs is really the same beast as a branch of commits.
The traditional tools (mailing-lists, git branches, Phabricator) represented each change as a difference between an old version of the code and the proposed new version. I believe Phabricator literally stored the diff. They were called “diffs” and you could make a new one by copying and pasting into a <textarea> before pressing save*.
The new fangled stuff (GitHub and its clones) recorded your change as being between branches A and B, showed you the difference on the fly, and let you modify branch B. After fifteen years of this we are now seeing the option for branch A to be something other than main, or at least for this to be a well supported workflow.
In traditional git land, having your change as a first class object — an email or printout or ph/D1234 with the patch included — was the default workflow!
*Or some other verb meaning save.
It’s useful for large PRs in large repos with many contributors. It reduces the burden for reviewers.
Still not sure this is the right solution. My problem is if one of your first stages gets rejected in review or requires significant changes, it invalidates so much work that comes after it. I've always when possible preferred to get small stuff merged in to production as it happens rather than build an entire feature and put it up for review.
this works much better in Phabricator because commits to diffs are a 1:1 relationship, diffs are updated by amending the commit, etc., the Github implementation does seem a bit like gluing on an additional feature.
Right, a PR is "just" a set of commits (all must be in the same branch) that are intended to land atomically.
Stacked PRs are not breaking up a set of commits into divisible units. Like you said, you can already do that yourself. They let you continue to work off of a PR as your new base. This lets you continue to iterate asynchronously to a review of the earlier PRs, and build on top of them.
You often, very often, need to stage your work into reviewer-consumable units. Those units are the stack.
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...>
> 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
As we have it designed currently, you would have to wait for CI to pass on the bottom two and then you can merge the bottom two in one step. The top of the stack would then get rebased, which will likely trigger another CI run.
Thanks for the callout - we'll update those docs to make it clear multiple PRs can be merged at once.
The vibecoded frontend makes the product look like a side project.
Is it?
It was announced in like November of last year, so it's certainly taken some time. The announcement was by some senior management at GitHub, so it has some degree of buy-in.
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.
Rebasing after merging a base branch becomes a pain though, when you do this. IMO the CLI will be nice to automate the process of rebasing each branch on its parent.
Agreed. I do have tooling for a rebase + push flow, but it simply calls native git commands.
The CLI is completely optional, you can create stacked PRs purely via the UI.
Also the rationale for having a chain of branches pointing to each other was so the diff in a PR shows just the relevant changes from the specific branch, not the entire set of changes going back to the parent/trunk.
Curious how you're thinking about it?
+1 this isn’t something new, it’s been possible all along in native git if you’re willing to do branch management and rebasing yourself. Just without the fancy UI / stack map.
Yes! Maybe that feature will come next.
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.
Still feels like an alpha version right now. I'm sure they will add it later.
Graphite (which they seem to be inspired by) has frozen branches exactly for that use case:
https://graphite.com/blog/introducing-frozen-branches
This API leaves a comment, on the commit; not quite the same thing since in GH, several operations are tied to PRs and not to commits.
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?
We use this feature extensively at $dayjob.
Imagine you have some task you are working on, and you wish to share your progress with people in bite sized chunks that they can review one at a time, but you also don’t want to wait for their reviews before you continue working on your task.
Using a stacked set of PRs you can continue producing new work, which depends on the work you’ve already completed, without waiting for the work you’ve already completed to be merged, and without putting all your work into one large PR.
I've always done this by just creating new PRs that are based on the branch of the old one. Maybe this is a UI improvement?
1 reply →
in Phabricator you either abandon the original diffs entirely, or you amend them. you don't just stack more commits with meaningless messages like "WIP", "lint fix", etc. on top.
> 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.
this is now what stacked diffs are for. stacked diffs doesn't mean putting up code that isn't ready. for example you are updating some library that needs an API migration, or compiler version that adds additional stricter errors. you need to touch hundreds of files around the repository to do this. rather than putting up one big diff (or PR) you stack up hundreds of them that are trivial to review on their own, they land immediately (mitigating the risk of merge conflicts as you keep going) then one final one that completes the migration.
I also branch out, and rebase. Also, keep updating and rebasing until merged. It’s tedious when PR take ages for approval, as I keep creating new branches on top of each other.
So, when I saw this announcement seemed interesting but don’t see the point of it yet.
I think the core conceptual difference between a stacked diff and PRs as we use them in open source is the following:
A PR is basically a cyberspatial concept saying "I, as a dog on the internet, am asking you to accept my patches" like a mailing list - this encourages trying to see the truth in the whole. A complete feature. More code in one go because you haven't pre-agreed the work.
Stacks are for the opposite social model. You have already agreed what you'll all be working on but you want to add a reviewer in a harmonious way. This gives you the option to make many small changes, and merge from the bottom
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.
Curious how / how well it deals with conflicts in the different branches that are part of the stack. Is there some support for managing that, or what happens when two of the branches don't rebase / merge cleanly?
Is this going to be a part of triage task? If so, it makes sense. Whether a human developer or an AI made a big PR, AI goes review it and if necessary makes stacked PRs. I don’t see any human contributors using this feature to be honest because it’s an extra work and they should have found a better way to suggest a large PR.
Seems to mainly be useful for monorepos as currently designed. Or, to replace a long-lived feature/refactor branch.
Whatbmakes you say that? Devs use stacked PRs in small and large repos today.
Their examples show combined backend and frontend changes on the same monorepo in different PRs.
As far as splitting work into different PRs that need coordinated merging, I've only ever encountered that when it's a long lived refactor / feature.
I think the only thing I miss from GitLab was being able to make merge requests depend on other merge requests, even across repositories. So I could make a backend repository MR depend on a library repository MR, and even enable auto-merge that’d fire when the backend MR was reviewed and the dependency was also merged.
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.
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).
It looks like in the UI if you base a PR on another branch you can just check a box to make it a stack. So I don't think you have to know the full shape of the stack in advance unless you're using the cli.
I use jj to stack branches so i'll just be using the UI to do github pr stacks.
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?
> If you have 10 commits for frontend and 10 for backend
In this model, you tend to want to amend, rather than add more commits. And so:
> they might start with 5 for backend, then 5 commits to each branch to iron out the interface and communication,
You don't add more commits here, you modify the commits in your stack instead.
> Now I need to rebase my frontend branch to sit on B6?
Yes, when you change something lower in the stack, the things on top need to be rebased. Because your forge understands that they're stacked, it can do this for you. And if there's conflicts, let you know that you need to resolve them, of course.
But in general, because you are amending the commits in the stack rather than adding to it, you don't need to move anything around.
> And wouldn't this separation normally be obvious e.g. by paths?
In the simplest case, sure. But for more complex work, that might not be the case. Furthermore, you said you have five commits for each; within those sets of five, this separation won't exist.
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`
how is this different than viewing a PR one commit at a time?
I think it is conceptually the same but multiple PRs gives you the tools to manage the commits properly which GitHub is missing. You can't do the equivalent of `git rebase -i` in the GitHub UI to squash a fixup into a previous commit. Having each change in it's own PR enables that workflow using the existing GitHub UI.
Split into individual PRs, which works better for how a lot of companies do code review.
Stacked PRs track changes through updates and can be integrated progressively as they get validated.
They also allow reviewing commits individually, which is very frustrating to do without dedicated support (unless you devolve back to mailing list patch stacks).
Each commit can be merged independently as they're reviewed.
I don't think this is it. The main driver is that several operations in GH are scoped around a PR, not a commit. So the reason you need stacked PRs is that the layer of tooling above `git` is designed to work on logical groups of commits called a PR.
1 reply →
One of the advertised features of this is being able to merge all the PRs at once. Which would also be the case for multiple commits in a single PR.
2 replies →
Commits are immutable and you never know which feedback goes stale when you add another commit.
I'm not a huge fan, since stacked PRs mean the underlying issues don't get addressed (reviews clearly taking too long, too much content in there), but it seems they want something that works for their customers, right now, as they work in real life.
> Commits are immutable
I guess this is why you're getting downvoted. Commits can be edited.
1 reply →
It would be the same if commits were meaningful things. But typically they're not. The PR is the meaningful thing. A commit is something like "fix the lint error".
CI runs on each PR, you get a whole PR message and discussion/review interface for each PR. Each PR can itself consist of multiple commits. You can have stacked PRs from different authors (though from another comment it sounds like they may not have implemented that).
It's a big improvement (assuming they've done it right).
I loved using sapling / mercurial so much at work that I ended up using the sapling SCM vsc extension at home all the time for personal work.
Only downside is that Phabricator is not open source so viewing it in most things sucks. Hoping now I can get a much better experience
Phabricator is open source and has been for years. It has had a bumpy ride over the last few years though. Although I guess having written that I assume the internal meta one is much better
> 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.)
> a chain of small, focused pull requests that build on each other — each one independently reviewable.
I have never understood what this even means.
Either changes are orthogonal (and can be merged independently), or they’re not. If they are, they can each be their own PR. If they’re not, why do you want to review them independently?
If you reject change A and approve change B, nothing can merge, because B needs A to proceed. If you approve change A and reject change B, then the feature is only half done.
Is it just about people wanting to separate logical chunks of a change so they can avoid get distracted by other changes? Because that seems like something you can already do by just breaking a PR into commits and letting people look at one of those at a time.
I’ve tried my best to give stacked-diff proponents the benefit of the doubt but none of it actually makes sense to me.
The canonical example here is a feature for a website that requires both backend and frontend work. The frontend depends on the backend, but the backend does not depend on the frontend. This means that the first commit is "independent" in the sense that it can land without the second, but the second is not, hence, a stack. The root of the stack can always be landed independently of what is on top of it, while the rest of the stack is dependent.
> If they’re not, why do you want to review them independently?
For this example, you may want review from both a backend engineer and a frontend engineer. That said, see this too though:
> that seems like something you can already do by just breaking a PR into commits and letting people look at one of those at a time.
If you do this in a PR, both get assigned to review the whole thing. Each person sees the code that they don't care about, because they're grouped together. Notifications go to all parties instead of the parties who care about each section. Both reviews can proceed independently in a stack, whereas they happen concurrently in a PR.
> If you approve change A and reject change B, then the feature is only half done.
It depends on what you mean by "the feature." Seen as one huge feature, then yes, it's true that it's not finished until both land. But seen as two separate but related features, it's fine to land the independent change before the dependent one: one feature is finished, but the other is not.
If the layers of a stack have a disjoint set of reviewers things are viewed in separation which might lead to issues if there is no one reviewing the full picture.
1 reply →
> If you do this in a PR, both get assigned to review the whole thing. Each person sees the code that they don't care about, because they're grouped together.
There are two separate issues you’re bringing up:
- Both groups being “assigned” the PR: fixable with code owners files. It’s more elegant than assigning diffs to people: groups of people have ownership over segments of the codebase and are responsible for approving changes to it. Solves the problem way better IMO.
- Both groups “seeing” all the changes: I already said GitHub lets you view single commits during PR review. That is already a solved problem.
And I didn’t even bring up the fact that you can just open a second PR for the frontend change that has the backend commit as the parent. Yes, the second PR is a superset of the first, but we’ve already established that (1) the second change isn’t orthogonal to the first one and can’t be merged independently anyway, and (2) reviewers can select only the commits that are in the frontend range. Generally you just mark the second PR as draft until the first one merges (or do what Gitlab does and mark it as “depends on” the first, which prevents it from merging until the first one is done.) The first PR being merged will instantly make the second PR’s diff collapse to just the unique changes once you rebase/merge in the latest main, too.
All of this is to explain how we can already do pretty much all of this. But in reality, it’s silly to have people review change B if change A hasn’t landed yet. A reviewer from A may completely throw the whole thing out and tell you to start over, or everything could otherwise go back to the drawing board. Making reviewers look at change B before this is done, is a potential for a huge waste of time. But then you may think reviewers from change B may opt to make the whole plan go back to the drawing board too, so what makes A so special? And the answer is it’s both a bad approach: just make the whole thing in one PR, and discuss it holistically. Code owners files are for assigning ownership, and breaking things into separate commits is to help people look at a subset of the changes. (Or just, like, have them click on the folder in the source tree they care about. This is not a problem that needs a whole new code review paradigm.)
10 replies →
we have been stacking on tangled.org for a while now, you can see a few examples of stacks we have made here: https://tangled.org/tangled.org/core/pulls?state=merged&q=st...
for example, this stack adds a search bar: https://tangled.org/tangled.org/core/pulls/1287
- the first PR in the stack creates a search index.
- the second one adds a search API handler.
- the last few do the UI.
these are all related. you are right that you can do this by breaking a change into commits, and effectively that is what i do with jujutsu. when i submit my commits to the UI, they form a PR stack. the commits are individually reviewable and updatable in this stacking model.
gh's model is inherently different in that they want you to create a new branch for every new change, which can be quite a nuisance.
have written more about the model here: https://blog.tangled.org/stacking/
> - the first PR in the stack creates a search index.
> - the second one adds a search API handler.
> - the last few do the UI.
So you're saying you're going to merge (and continuously integrate, perhaps to production) a dangling, unused search index, consuming resources with no code using it, just to make your review process easier?
It's very depressing that review UX is so abysmal that you have to merge features before they're done just to un-fuck it.
Why can't the change still be a big branch that is either all merged or not... and people can review it in chunks? Why do we require that the unit of integration equals the unit of review?
The perverse logic always goes something like this:
"This PR is too big, break it up into several"
Why?
"It's easier to review small, focused changes"
Why can't we do that in one PR?
"Because... well, you see GitHub's UI makes it really hard to ..."
And that ends up being the root-cause answer. I should be able to make a 10,000 line change in a single commit if I want, and reviewers should be able to view subsets of it however they want: A thread of discussion for the diffs within the `backend` folder. A thread of discussion for the diffs within the `frontend` folder, etc etc. Or at the very least I should be able to make a single branch with multiple commits based on topic (and under no obligation for any of them to even compile, let alone be merge-able) and it should feel natural to review each commit independently. None of this should require me to contort the change into allowing integration partially-completed work, just to allow the review UX to be manageable.
1 reply →
you're upgrading the repository from language version 1 to 2, version 2 adds new compiler errors that rejects some old code, or the library has removed some old deprecated API the repository was still using in some places—the key here being that it can't be something that needs to be completely atomic.
you have hundreds or thousands of files to fix. that is unreviewable as a single commit, but as a per-file, per-library, per-oncall, etc. commit it is not that bad.
> you have hundreds or thousands of files to fix. that is unreviewable as a single commit, but as a per-file, per-library, per-oncall, etc. commit it is not that bad
Why is it intrinsically unreviewable as a single commit? Why can't the discussion/review system allow scoping discussions to a single folder of the change, or a single library, or a particular code-owner's "slice" of the repo, etc? The answer to this question is always unsatisfactory to me. It always ends up being "because GitHub's UI makes it hard to <foo>" and it's just taken as an immutable law of the universe that we're stuck with that UI's limitations.
If a change is huge, find some basis by which to discuss it in smaller chunks. That basis doesn't have to be the PR itself (such that you have to make smaller PR's to make discussion manageable.) It can be a subdirectory of the diff. A wildcard-match over the source files. Whatever the case needs to be, the idea is still that the discussion UX shouldn't make reviewing large changes painful.
Why do we tolerate the fact that GitHub doesn't let you say "approved for changes in `frontend/*`" or "approved for the changes I'm a code-owner of", and have the PR check system mark the PR as approved once all slices have been approved? Why do we tolerate that a thousand-file change is "unreviewable"? Instead we have to change our unit of integration, allowing partially-complete work to be merged, just because the review UX sucks.
Feature B depends on feature A, but you don't need B to understand A. Why wouldn't you create separate PRs?? It is faster to review and deploy.
Of course you would create separate PRs.
Why would you waste time faffing about building B on top of a fantasy version of A? Your time is probably better spent reviewing your colleague’s feature X so they can look at your A.
>If you reject change A and approve change B, nothing can merge
The feature is also half done in this case. The author can fix up the concerns the reviewer had in A and then both can be merged at the same time.
Couldn’t they do that in one PR? Seriously, couldn’t you just say “hey Alice, could you review the A parts of this PR” and “hey Bob, could you review the B parts”, then only merge once both of them approve? Even GitHub, for all its faults, supports code owners files, such that this can even be policy.
1. Finally. Pull requests are consanguine and bizarre.
2. I'm not a huge fan of having to use a secondary tool that isn't formally a layer around git / like jj as opposed to github
> 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.
This _is_ a solution to slow reviews. Smaller reviews are faster to get in. And many small reviews take less time to review than one large review.
Plus there's no review that's instant. Being able to continue working is always better.
Thank goodness. It was a pain to do this manually
Interesting to see how their CLI compares with GitLab's CLI interface for stacked diffs (the only support they offer at the moment): https://docs.gitlab.com/user/project/merge_requests/stacked_.... Most things are the same (up/down/top/bottom vs. next/prev/first/last, init vs. create), but both feel quite limiting. I've heard of other systems such as Gerrit that offer better native support, but have not tried out any for myself.
The `gh stack` CLI sounds essential for people using git, but I hope it doesn't become required, as people using things like jj/sl should be able to work with stacks. `gs submit`/`gs push` being the interface is fine, but `gs init` and `gs add` should be optional.
They confirmed below that you should be able to use this with jj just fine, just like you can already use gh to create a PR that you've authored with jj: https://news.ycombinator.com/item?id=47759426
People have been building stacked PR workflows on top of GitHub for a while now. It's great to see that the message seems to have finally landed at GitHub, but what is actually new here in GitHub itself (i.e., not counting the gh CLI tool)?
There seems to be a native stack navigation widget on the PR page, which is certainly a welcome addition.
The most important question though is whether they finally fixed or are going to fix the issues that prevent submitting stacked PRs from forks. I don't see any indication about that on the linked page.
Pretty cool to see stacks being given due attention. Also check out git-spice, which works with Gitlab (possibly others). Personally I use git-spice in place of all the conventional git commands.
My main question about this is does it keep review history properly after a rebase to restack PRs? Eg if I have reviewed PR for branch A and now its been rebased onto B by this tool and then more changes are made to A, does "review changes since" work in A's PR? This has been the main thing stopping me from wanting to use rebase to stack PRs and if they've fixed this somehow then I'm interested.
this was released about 7 years ago, no?
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.
Yeah, not sure what this solves that doing multiple PRs from one feature branch into another doesn't solve. But building behavior that into AI agents wouldn't be cool enough, I guess.
Wow i really need this, we had a refactor our monorepo (dotnet 8 -> 10 and angular 19 -> 21) which resulted in many small changes (like refactoring to signals, moving components to standalone) and we try to group changes into commits by what was fixed, but this had the downside of some commits beeing huge while others small, this would have helped us alot grouping commits together and having cleaner commit messages.
One mistake I see across many organizations is that sometimes they overthink how much order should matter.
Sure, your application has a dependency on that database, but it doesn't necessarily mean you can't deploy the application before having a database. If possible, make it acceptable for your application to stay in a crashloop until your database is online.
I agree with you and further will add that modularity+atomicity are the ideal state for the vast majority of software applications… but in reality, most organizations can not afford to rewrite their software to the extent required to achieve this, if it wasn’t planned from the start.
This is probably driven to be more usable with AI agents, but smaller prs can create more code as they need to enforce more backwards compability, this can also lead to more code or more maintenance work.
Honestly I don’t see the benefit of smaller prs, except driving vanity scores?
Like I’m not saying you should
Even though moments where I would reach for it are rare, this is a very welcome feature. In times when I could have used it, it was not difficult to emulate via more branches, consistent naming, referencing the PRs, etc. Not difficult, but definitely tedious, and always left me feeling less organized than I like to feel.
Very much looking forward to getting this on Renovate - we require squash-merge via Merge Queue (with no per-PR override available in GitHub, despite asking) and so when I've got multiple changes, it's a lot of wrangling and rebasing
If this works as smoothly as it sounds, that'll significantly reduce the overhead!
Meta has something similar to this using mercurial. It was awesome.
Freaking finally.
I’ve been trying to convince my boss to buy Graphite for this, seems like Github is getting their a* in gear after Cursor bought them.
If Jetbrains ever implements support for them in IntelliJ I will be in Heaven.
man, why is this waitlisted. this should have been a feature 10 years ago
Just when I’ve gotten used to having 3 or more PRs in parallel with a local octopus working tree with jj. Maybe my colleagues will see the light at least.
Interesting to see how this integrates with any release flows that use a lot of tags or feature flagging.
This needs to be supported on `git` level first imho, not by a forge vendor.
What would this being supported by git mean to you?
great, I'll directly compare it to graphite.com - the main point really is the user interface in my opinion. Still a bit sceptical whether github can out-deliver here, but happy to be proven wrong!
Has anyone already tried that was a graphite user before?
What's difference between stacked PRs and merge trains in gitlab?
Merge trains are an integration method. In GitHub that’s called merge queues.
Stacked PRs are a development method, for managing changes which are separate but dependent on one another (stacked).
The two are orthogonal they can be used together or independently (or not at all).
Ahh so stacked diffs? https://docs.gitlab.com/user/project/merge_requests/stacked_...
1 reply →
the Website for the release statement look soooooo bad
cherry picking is so fragile, this is at least a step in the right direction
“You cannot merge a PR in the middle of the stack before the PRs below it are merged.”
Huh? Some stacks need to land all at once and need to be reviewed (and merged) from the top down. It’s not uncommon, in my org at least, to review an entire stack and merge 3 into 2 and then 2 into 1 and then 1 into main. If 2 merges before 3, you just rebase 3 onto 1.
At first I thought this was a user submitted project due to the subdomain of github.com but then realize the subdomain is also github. Is this an official channel for this sort of thing? Surprised this isn't on the official blog.
It's in private preview. Probably they'll put it in the main docs and such once it's open to everyone.
it's a GitHub Pages site in the @GitHub org from a repo called gh-stack (i.e. the repo is at https://github.com/github/gh-stack/).
There’s a special case where certain official orgs can continue to use github.com instead of github.io for their Pages domain, and that’s how you end up with:
https://github.github.com/gh-stack/
from the code:
Should Pages owned by this user be regarded as “Official GitHub properties”?
def github_owned_pages? GitHub.github_owned_pages.include?(login) end
# Orgs/users that are owned by GitHub and should be allowed to use # `github.com` URLs. # # Returns an Array of String User/Organization logins. ...
It's their 'GitHub' org GitHub Pages domain - it's just .com instead of .io (not to be confused with their gh.io link shortener)
Cool. Now let me do it across multiple repos. I often end up with 10 or 20 PRs across a half dozen repos that need to be merged in specific order.
For sure. If you are in a monorepo this solution works but if you have distinct microservice repositories it would help to coordinate pr #827 on repo-A and pr #1092 on repo-B are related and need to ship in a certain order.
Exact problem we've run into at work. We've ended up having to write external merge coordination in order to not break our dev deployments.
Who hurt you?
Their manager who suggested that everything be a microservice, but everything depends on each other.
1 reply →
Microservices, by the sound of the original comment
2 replies →
How would that work? Commits in different repos aren't ordered relative to one another. I suppose you could have a "don't let me merge this PR until after this other PR is merged" feature, but you could do that with a GitHub Action; it doesn't really need dedicated backend or UI support.
> How would that work?
In practical terms: I manually write a list of PRs, and maintain that list in the description of each of the PRs. Massive duplication. But it clearly shows the merge train.
I'm surprised no-one has commented on the "sign up for the waitlist" button being a Microsoft Office form that wants your email address and GitHub handle. This feels like an elaborate phishing attack more than a serious feature announcement.
This feels like a workaround for git's contradictory ergonomics.
Aside:
> This is a docs site that was made to share the spec and CLI for private preview customers that ended up getting picked up. This will move to GitHub docs once it’s in public preview.
(https://x.com/matthewisabel)
Wondering how all of those startups that implement this for GitHub feel right now.
sherlocked
what happened to the old gerrit reviews, I loved its handling on incremental patchsets. github is primitive by comparison.
The biggest challenge for us are PRs that need to be coordinated across multiple repos. API + client for example. It doesn't sound like stacked PRs solve that problem, right? Description specifically states single repo.
They do not no. Afaik GitHub has little to nothing that is cross repository.
Can we merge from GitHub UI without rewriting the commit already?
[dead]
[dead]
How does this work with a tool like jujutsu that provides native support for stacking and preferably don't have a separate tool mucking about with state?
Using a platform-specific tool feels like vendor lock-in. Given the recent trends, behavior, and activity of GitHub, it sure seems like this is the opposite of what the world should be going for generally.
Also if someone could help me understand: Are these so-called stacked commits not possible with multiple commits on a single branch? I prefer to write my commits as atomic, independent, related changes, on a single branch, with both Git and Mercurial. I am apparently missing something: why can't a better UI simply show a multi-change PR?
The CLI is not required and you can push up your bookmarks as branches and open stacked PRs via the UI. You can also use the gh CLI to just create the stacked PRs on github.com (essentially an API wrapper), without using it to manage local state.
It's a matter of taste, but I much prefer the workflow in the tool I hacked together for this, https://github.com/jlebar/git-pr-chain.
In the tool I wrote, you have a single branch with linear history. PRs in the chain are demarcated via commit messages. You then don't need any special rebase / sync commands -- you can use regular `git rebase -i` to reorder commits or edit a commit in the middle of a stack. Literally the only special command I need is "push this branch to github as multiple PRs".
Anyway I hope that alongside the branch-based you've built tool in `gh` that there will be an API that I can target.
Yup, there will be an API for stacks, just like there is one for regular PRs.
Any idea if/when this would be coming to GHE? I know the release cycle is way different but curious about your thoughts.
Yeah features need to be released as GA (general availability) before they can be included in GHES. I don't have a definitive timeline, but it will likely be end of this year or early next.
github.github.com? Not the first time github does something highly weird with their domains (like publishing docs from a subdomain of their public github pages service)
I think they have a culture of circumventing 'official' channels and whoever is in charge of a thing is whoever publishes the thing.
I think it's a great way to train users to get phished by github impostors, if tomorrow we see an official download from official.github.com or even official-downloads.github.io, sure it's phishy, but it's also something that github does.
It's also 100% the kind of issues that, if it happens, the user will be blamed.
I would recommend github to stop doing this stuff and have a centralized domain to publish official communications and downloads from. Github.github.com? Come on, get serious.
TL;DR: DO NOT DOWNLOAD ANYTHING from this site, (especially not npm/npx/pnpm/bun/npjndsa) stuff. It's a Github Pages site, just on a subdomain that looks official, theoretically it might be no different from an attacker to obtain access to dksabdkshab.github.com than github.github.com. Even if it is official, would you trust the intern or whoever managed to get a subdomain to not get supply chained? github.github.com just think about it.
https://news.ycombinator.com/item?id=47614038
in github's defense. This is a bit more nuanced, less objectively wrong domain posture issue. It will only matter if one security mechanism (subdomain control) fails.
The quoted microsoft examples are way worse. I see this with outbound email systems a lot, which is especially dangerous because email is a major surface of attack.
If only there were some way to logically break up large pull requests into smaller pieces... Some way of creating a checkpoint with a diff including your changes, and some kind of message explaining the context behind the change... some way to "commit" a change to the record of the repository...
Part of the idea behind stacked PRs is to keep your commits focused and with isolated changes that are meaningful.
A stacked PR allows you to construct a sequence of PRs in a way that allows you to iterate on and merge the isolated commits, but blocks merging items higher in the stack until the foundational changes are merged.
What can stacked PRs do that a series of well-organized commits in a single branch can't?
1 reply →
There are tools that use LLMs to do this.
I've done this manually by building a big feature branch and asking an LLM to extract out functionality for a portion of it.
For the former, it would seem to split based on frontend/backend, etc. rather than what semantically makes the most sense and for the latter it would include changes I don't want and forget some I do want. But I haven't tried this a lot.
So much effort has been spent beating git until it's just CVS with bells on.
The stacked diffs flow is much closer to the kernel flow for git than the traditional GitHub PR flow is.
Yeah, I feel like just being able to review a PR commit-by-commit with a nice interface would just suffice.
Not really. Without seeing the entire changeset for a PR, you'd have to mentally keep track of what the current state of everything is unless you're a commit minimalist and presquash.
2 replies →
For me that would mean avoiding tiny commits, and I wouldn't want to do that
What might that be?
I feel like we already have enough abstractions in this space. Having any constraints at all in your tools is actually a good thing. PRs on top of ordinary git was a good step. This seems like one too many.
I honestly don't even get the PR addiction. Github has shaped devs workflows way too much. My best experience with git was when I realized that I can just have an blatantly simple workflow and explain it even to the junior-est dev in a few minutes. The reliance on github is somehow telling me that people stopped thinking about things they can actually control.