[HN Gopher] GitHub Stacked PRs
___________________________________________________________________
GitHub Stacked PRs
Author : ezekg
Score : 864 points
Date : 2026-04-13 20:36 UTC (1 days ago)
(HTM) web link (github.github.com)
(TXT) w3m dump (github.github.com)
| ZeWaka wrote:
| Seems to mainly be useful for monorepos as currently designed.
| Or, to replace a long-lived feature/refactor branch.
| YesThatTom2 wrote:
| Whatbmakes you say that? Devs use stacked PRs in small and
| large repos today.
| ZeWaka wrote:
| 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.
| Hamuko wrote:
| 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.
| inetknght wrote:
| 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.
| throwaway9980 wrote:
| Who hurt you?
| dpcx wrote:
| Their manager who suggested that everything be a
| microservice, but everything depends on each other.
| simplyluke wrote:
| Microservices, by the sound of the original comment
| Arbortheus wrote:
| Microservices without a monorepo is hell
| Yokohiii wrote:
| They've opted for critical damage.
| whalesalad wrote:
| 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.
| ZeWaka wrote:
| 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.
| ameliaquining wrote:
| 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.
| inetknght wrote:
| > _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.
| noident wrote:
| 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...
| Hamuko wrote:
| Yeah, I feel like just being able to review a PR commit-by-
| commit with a nice interface would just suffice.
| ezekg wrote:
| 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.
| aunderscored wrote:
| How does that differ from this where you need to keep track
| of state and the whole change in the stack?
| ezekg wrote:
| If we're speaking strictly code review, because you can
| actually make sense of the changeset for the child PR by
| not including its unmerged parent's changeset.
| benatkin wrote:
| For me that would mean avoiding tiny commits, and I wouldn't
| want to do that
| jaredsohn wrote:
| 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.
| bombcar wrote:
| So much effort has been spent beating _git_ until it 's just
| CVS with bells on.
| steveklabnik wrote:
| The stacked diffs flow is much closer to the kernel flow for
| git than the traditional GitHub PR flow is.
| landr0id wrote:
| 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.
| noident wrote:
| What can stacked PRs do that a series of well-organized
| commits in a single branch can't?
| steveklabnik wrote:
| Stacked PRs tend to encourage a series of well-organized
| commits, because you review each commit separately, rather
| than together.
|
| What they do that the single branch cannot is things like
| "have a disjoint set of reviewers where some people only
| review some commits", and that property is exactly why it
| encourages more well-organized commits, because you are
| reviewing them individually, rather than as a massive
| whole.
|
| They also encourage amending existing commits rather than
| throwing fixup commits onto the end of a branch, which
| makes the original commit better rather than splitting it
| into multiple that aren't semantically useful on their own.
| a_e_k wrote:
| I think the point the GP was trying to make is that the
| GitHub UI ought to be able to allow you to submit a
| branch with multiple well-organized commits and review
| each commit separately with its own PR. The curation of
| the commits that you'd do for stacked PRs could just as
| easily be done with commits on a single branch; some of
| us _don 't_ just toss random WIP and fixup commits on a
| branch and leave it to GitHub to squash at the end. I.e.,
| it's the GitHub UI rather than Git that has been lacking.
|
| (FWIW, I'm dealing with this sort of thing at work right
| now - working on a complex branch, rewriting history to
| keep it as a sequence of clean testable and reviewable
| commits, with a plan to split them out to individual PRs
| when I finish.)
| steveklabnik wrote:
| > I think the point the GP was trying to make is that the
| GitHub UI ought to be able to allow you to submit a
| branch with multiple well-organized commits and review
| each commit separately with its own PR.
|
| That's what this feature is, conceptually. In practice,
| it does seem slightly more cumbersome due to the fact
| that they're building it on top of the existing, branch-
| based PR system, but if you want to keep it to one
| commit, you can (and that's how I've been working with
| PRs for a while now regardless, honestly).
|
| They confirmed in other comments here that you don't
| _have_ to use the CLI, just like you don 't have to use
| gh in general to make pull requests, it's just that they
| think the experience is nicer with it. This is largely a
| forge-side UI change.
| dontlikeyoueith wrote:
| > I think the point the GP was trying to make is that the
| GitHub UI ought to be able to allow you to submit a
| branch with multiple well-organized commits and review
| each commit separately with its own PR
|
| So the point he's trying to make is that Gituhub UI
| should support Stacked PRs but call them something else
| because he doesn't like the name?
| pertymcpert wrote:
| What might that be?
| bsimpson wrote:
| 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.
| ezekg wrote:
| 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.
| ZeWaka wrote:
| It seems partially exposed in the UI with that dropdown.
| There's an 'add' and 'unstack' button.
|
| Probably relies on some internal metadata.
| ameliaquining wrote:
| You can in fact do this from the web UI:
| https://github.github.com/gh-stack/guides/ui/#creating-a-
| sta...
| ezekg wrote:
| I must have missed that. Amazing! From a reviewer's POV,
| this will be so nice to at the very least remove diff noise
| for PRs built on top of another PR. I usually refrain from
| reviewing child PRs until the parent is merged and the
| child can be rebased, for the sole reason that the diffs
| are hard to review i.r.t. what came from where.
| NooneAtAll3 wrote:
| damn, I missed it as well
|
| presenting only cli commands in announcement wasn't a good
| choice
| SamuelAdams wrote:
| 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.
| sameenkarim wrote:
| We're shipping a skill file with the CLI:
| https://skills.sh/github/gh-stack/gh-stack
|
| Everyone will have their own way of structuring stacks, but
| I've found it great for the agent to plan a stack structure
| that mirrors the work to be done.
| bmitc wrote:
| Why don't you use the CLI?
| ezekg wrote:
| Because git is all I need.
| bmitc wrote:
| Then why are you using GitHub? :)
|
| My point is that Git is just a component of the GitHub
| tool, and the GitHub CLI is quite good and helps automate
| many things in GitHub. For example, even just using `gh
| browse` and `gh pr create --web` and `gh pr view --web`
| are fantastic tools.
| ezekg wrote:
| I don't need to automate anything in GitHub, I have a web
| browser for when I need to use GitHub. Installing and
| learning another CLI seems like a waste of my time for
| very, very little return.
| bmitc wrote:
| You would rather manually browse to the repo you're
| working on in the web interface rather than typing `gh
| browse`? I hate CLIs, in general, but the GitHub CLI has
| some very useful commands.
| sameenkarim wrote:
| 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.
| ezekg wrote:
| Amazing. Though this wasn't super obvious from the landing
| page or docs I read.
| contravariant wrote:
| 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_.
| pbrowne011 wrote:
| 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.
| whalesalad wrote:
| 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.
| ZeWaka wrote:
| 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)
| ameliaquining wrote:
| It's in private preview. Probably they'll put it in the main
| docs and such once it's open to everyone.
| varun_ch wrote:
| 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. ...
| topaztee wrote:
| how is this different than viewing a PR one commit at a time?
| simplyluke wrote:
| Split into individual PRs, which works better for how a lot of
| companies do code review.
| Macha wrote:
| Each commit can be merged independently as they're reviewed.
| Hamuko wrote:
| 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.
| Arainach wrote:
| That's possible but not mandatory. In the current UX you
| can only approve/submit all or none.
| CharlieDigital wrote:
| 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.
| 4b11b4 wrote:
| Right, the argument against: "how is this any different
| than splitting into single commits?" is simply: In general
| you want just one level above a commit which is the PR
| nickcw wrote:
| 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.
| stephbook wrote:
| 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.
| normie3000 wrote:
| > Commits are immutable
|
| I guess this is why you're getting downvoted. Commits can be
| edited.
| steveklabnik wrote:
| When you edit a commit, it creates a new commit. They are
| immutable. You can still find the old commit via the
| reflog, until it gets eventually gc'd.
|
| If I had to guess a reason they were downvoted (and I
| didn't downvote, to be clear), it's probably because people
| see stacked diffs as specifically solving "reviews clearly
| taking too long, too much content in there", and so it
| feels contradictory. Then again, as I said, I didn't
| downvote!
| normie3000 wrote:
| Thank you for the correction.
| mortar wrote:
| I agree with the sentiment but hash collisions can occur
| with different content which doesn't really make them
| immutable.
| masklinn wrote:
| 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).
| IshKebab wrote:
| 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).
| dboreham wrote:
| 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".
| chao- wrote:
| 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.
| adamwk wrote:
| 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).
| kardianos wrote:
| 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.
| tcoff91 wrote:
| 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.
| nine_k wrote:
| Can these tools e.g. do per-commit review? I mean, it's not
| the UI what's the problem (though it's not ideal), it's the
| whole idea of commenting the entire PR at once, partly
| ignoring the fact that the code in it changes with more
| commits pushed.
|
| Phabricator and even Gerrit are significantly nicer.
| dathanb82 wrote:
| Unless you have a "every commit must build" rule, why
| would you review commits independently? The entire PR is
| the change set - what's problematic about reviewing it as
| such?
| steveklabnik wrote:
| In stacked diffs system, each commit is expected to land
| cleanly, yes.
| verst wrote:
| But isn't that why you would squash before merging your
| PR? If you define a rule that PRs must be squashed you
| would still have the per commit build.
| steveklabnik wrote:
| Squash merge is an artifact of PRs encouraging you to add
| commits instead of amending them, due to GitHub not being
| able to show you proper interdiffs, and making comments
| disappear when you change a diff at that line. In that
| context, when you add fixup commits, sure, squashing
| makes sense, but the stacked diffs approach encourages
| you to create commits that look like you want them to
| look like directly, instead of requiring you to roll them
| up at the end.
| riffraff wrote:
| There's a certain set of changes which are just easier to
| review as stacked independent commits.
|
| Like, you can do a change that introduced a new API and
| one that updates all usages.
|
| It's just easier to review those independently.
|
| Or, you may have workflows where you have different
| versions of schemas and you always keep the old ones.
| Then you can do two commits (copy X to X+1; update X+1)
| where the change is obvious, rather than seeing a single
| diff which is just a huge new file.
|
| I'm sure there's more cases. It's not super common but it
| is convenient.
| Sebb767 wrote:
| > Unless you have a "every commit must build" rule, why
| would you review commits independently?
|
| Security. Imagine commit #1 introduces a security
| vulnerability (backdoor) and the features. Then #2
| introduces a non-obvious, harmless bug and closes the
| vulnerability introduced in #1 [0]. At some point, the
| bug will surface and rolling back commit #2 will be an
| easy fix, re-introducing your bug.
|
| Alternatively, one of the earlier commits might, for
| example, contain credential dumping code. Once that
| commit is mainlined, CI might either automatically run on
| it or will be able to be run on it since it's no longer
| marked as unsafe PR.
|
| [0] Think something like #1 introduces array access and
| #2 adds a bounds-check in a function a layer above - a
| reviewer with the whole context will see the bounds check
| and (possibly) consider it fine, but to someone rolling
| back a commit the necessity will not be obvious.
| adityaathalye wrote:
| Same team, and a rare hill I'm willing to die on.
|
| Rant incoming...
|
| Boy do I hate Github/Lab/Bucket style code reviews with a
| burning passion. Who the hell _loses_ code review history? A
| record of the very thing that made my code better? The "why"
| of it all, that I am guaranteed to forget tomorrow morning.
|
| Nobody would be using `--force` or `--force-with-lease` as a
| _normal_ part of development workflow, of their own volition,
| if they had read that part of the git-push manpage and been
| _horrified_ (as one should be).
|
| The magit key sequence for this abominable operation is `P
| "f-u"`. And every single time I am forced to do it, I read
| "f-u" as it ought to be read.
|
| Rebase-push _is_ the way to do it (patch sets in Gerrit).
|
| Rebase-force-push is _absolutely not_.
|
| You see, _any_ development workflow inevitably has to
| integrate changes from at least one other branch (typically
| latest develop or master), without destroying change history,
| nor review history. Gerrit makes this trivial.
|
| It's a bit difficult to convey exactly why I'm so rah-rah
| Gerrit, because it is a matter of day-to-day experience of
| - Well, a single commit of a few lines to maybe a hundred
| lines *is* the correct unit of code review, rebase, revert
| etc. Manually "Sizing PRs" to that review context size is
| utter BS. I have better things to do in life than to book-
| keep PR sizes. Make a single well-contained, revertible
| commit. Then keep making those. And now you have a commit
| history that is clean, that you can merge, bisect, and bulk-
| revert at will. Octopus merges are a good thing. `git-log` is
| *designed* to let us view changes in any sequence we wish,
| *including* the so-called "linear" history. `git log
| --online`. - Trivial for committer to send up
| reviews-preserving rebase-push responses to commit reviews
| (NO force-push, ever --- that's an "admin" action to *evict*
| / permanently wipe out disaster scenarios such as when
| someone accidentally commits and pushes out a plaintext
| secret or a giant blob of the executable of the source code
| etc.). - Fast-for-the-reviewer, per-commit, diff-
| based, inline-commenting code reviews. - The
| years-apart experience of being able to dig into any part of
| one's (immutable) software change history to offer a teaching
| moment to someone new to the team.
|
| ... to name a few key ones.
|
| (edit: add point about review size)
| adityaathalye wrote:
| Slapping this "stacked diff" business _on top of_ something
| so broken as Github /lab/bucket is a concrete example of...
| https://en.wikipedia.org/wiki/Lipstick_on_a_pig
| smallmancontrov wrote:
| 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.
| Leynos wrote:
| I just used it because I preferred the UX.
| forrestthewoods wrote:
| 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!
| awesome_dude wrote:
| Whatever your opinion on one tool or another might be - it
| does seem weird that the "market" has been captured by what
| you are saying is a lesser product.
|
| IOW, what do you know that nobody else does?
| jrochkind1 wrote:
| Welcome to VHS and Betamax. the superior product does not
| always win the market.
| Per_Bothner wrote:
| Not always, but in this case the superior product (i.e.
| VHS) won. At initial release, Beta could only record an
| hour of content, while VHS could record 2 hours. Huge
| difference in functionality. The quality difference was
| there, but pretty modest.
| jrochkind1 wrote:
| I suppose one lesson could be that there are different
| dimensions of superiority, different products may be
| superior in different ways.
|
| Of course, products also can win market dominance for
| reasons external to the product's quality itself
| (marketing, monopoly lock-in, other network effects,
| consumer preferences on something other than product
| quality itself, etc).
| esafak wrote:
| That worse is better, and some people don't know better
| or care.
| dwattttt wrote:
| "better" in that sentence is very specific. Worse is also
| worse, and if you're one of the people for whom the
| "better" side of a solution doesn't apply, you're left
| with a mess that people celebrate.
| guelo wrote:
| Network effects and marketing can easily prevent better
| tools from winning.
| awesome_dude wrote:
| I mean, in the fickle world that is TECH, I am struggling
| to believe that that's what's happened.
|
| I personally went from .latest.latest.latest.use.this
| (naming versions as latest) to tortoise SVN (which I
| struggled with) to Git (which I also was one of those
| "walk around with a few memorised commands" people that
| don't actually know how to use it) to reading the fine
| manual (well 2.5 chapters of it) to being an evangalist.
|
| I've tried Mercurial, and, frankly, it was just as black
| magic as Git was to me.
|
| That's network effects.
|
| But my counter is - I've not found Mercurial to be any
| better, not at all.
|
| I have made multiple attempts to use it, but it's just
| not doing what I want.
|
| And that's why I'm asking, is it any better, or not.
| WolfeReader wrote:
| Mercurial has a more consistent CLI, a really good
| default GUI (TortoiseHg), and the ability to remember
| what branch a commit was made on. It's a much easier tool
| to teach to new developers.
| awesome_dude wrote:
| Hmm, that feels a bit subjective - I'm not going to say X
| is easier than Y when I've just finished saying that I
| found both tools to have a lot of black magic happening.
|
| But what I will point out, for better or worse, people
| are now looking at LLMs as Git masters, which is
| effectively making the LLM the UI which is going to have
| the effect of removing any assumed advantage of whichever
| is the "superior" UX
|
| I do wish to make absolutely clear that I personally am
| not yet ready to completely delegate VCS work to LLMs -
| as I have pointed out I have what I like to think of as
| an advanced understanding of the tools, which affords me
| the luxury of not having an LLM shoot me in the foot,
| that is soley reserved as my own doing :)
| arw0n wrote:
| Networking effects are significantly strengthened by
| necessary user buy in. VC is hard, and every tool demands
| its users to spend a non-significant amount of time
| learning it. I would guess the time to move from black
| magic to understanding most of git is ~100h for most
| people.
|
| The thing is, to understand which one is actually better,
| you would have to give the same amount of investment in
| the second tool, which is not something most people are
| willing to do if the first tool is "good enough". That's
| how Python became the default programming language;
| people don't miss features they do not understand.
| Izkata wrote:
| A little over a decade ago, with only svn experience, I
| tried both mercurial and git. There was something about
| how mercurial handled branches that I found extremely
| confusing (don't remember what), while git clicked
| immediately - even without reading the manual.
|
| So at least for me, git was clearly better.
| ptx wrote:
| Mercurial later added bookmarks which work like Git
| branches. These make more sense to me as well.
| qsera wrote:
| Did bookmarks moved as you made commits, like a branch
| pointer in git does?
| ptx wrote:
| Yup: https://mercurial-scm.org/help/commands/bookmarks
| forrestthewoods wrote:
| Worse products win all the time. Inertia is almost
| impossible to overcome. VHS vs Betamax is a classic. iPod
| wasn't the best mp3 player but being a better mp3 player
| wasn't enough to claw market share.
|
| Google and Meta don't use Git and GitHub. Sapling and
| Phabricator much much better (when supported by a massive
| internal team)
| aaronbrethorst wrote:
| What was the better mp3 player than the iPod?
| corndoge wrote:
| sansa clip+
| mi_lk wrote:
| unironically Zune is goated in its own way
| CrimsonRain wrote:
| anything from Cowon. Always has been
| codethief wrote:
| Anything from iriver.
| jorams wrote:
| So far you've only gotten responses to "how can a worse
| product win?", and they are valid, but honestly the
| problem here is that Mercurial is not a better product in
| at least one very important way: branches.
|
| You can visit any resource about git and branches will
| have a prominent role. Git is very good at branches.
| Mercurial fans will counter by explaining one of the
| several different branching options it has available and
| how it is better than the one git has. They may very well
| be right. It also doesn't matter, because the fact that
| there's a discussion about what branching method to use
| really just means Mercurial doesn't solve branches. For
| close to 20 years the Mercurial website contained a guide
| that explained only how to have "branches" by having
| multiple copies of the repository on your system. It
| looks like the website has now been updated: it doesn't
| have any explanation about branches at all that I can
| find. Instead it links to several different external
| resources that don't focus on branches either. One of
| them mentions "topic", introduced in 2015. Maybe that's
| the answer to Git's branching model. I don't care enough
| to look into it. By 2015 Git had long since won.
|
| Mercurial is a cool toolbox of stuff. Some of them are
| almost certainly better than git. It's not a better
| product.
| LordDragonfang wrote:
| This is so strange, because, at a low level, a branch
| isn't even a "thing" in git. There is no branch object
| type in git, it's literally just a pointer to a commit,
| functionally no different from a tag except for the
| commands that interact with it.
| fc417fc802 wrote:
| Meanwhile mercurial has bookmarks. TBF I'm not sure when
| it got those but they've been around forever at this
| point. The purpose is served.
|
| I think there are (or perhaps were) some product issues
| regarding the specifics of various workflows. But at
| least some of that is simply the inertia of entrenched
| workflows and where there are actual downsides the (IMO
| substantial) advantages need to be properly weighed
| against them.
|
| Personally I think it just comes down to the status quo.
| Git is popular because it's popular, not because it's
| noticably superior.
| ezst wrote:
| > I think there are (or perhaps were) some product issues
| regarding the specifics of various workflows.
|
| I love jumping in discussions about git branching,
| because that's a very objective and practical area where
| git made the playing field worse. Less and less people
| feel it, because people old-enough to have used branch-
| powered VCSes have long forgotten about them, and those
| who didn't forget are under-represented in comparison to
| the newcomers who never have experienced anything else
| since git became a monopoly.
|
| Anyhow, let's pick django as a project that was using a
| VCS with branches before moving to git/github, and have a
| look at the repo history:
| https://github.com/django/django/commits/stable/6.0.x
|
| Yes, every commit is prefixed with the branch name.
| Because, unlike mercurial, git is incapable of storing
| this in its commit metadata. That's ridiculous, that's
| obscene, but that's the easiest way to do it with git.
| Tarq0n wrote:
| That's the definition of a tree though. Everything has a
| parent, no cycles allowed.
| xmcqdpt2 wrote:
| It's interesting that branches, which is a marquee
| feature of git, became less important at the same time as
| git ate all the other vcs. Outside of OS projects, almost
| all development is trunk based with continuous releases.
|
| Maybe branching was an important reason to adopt git but
| now we'd probably be ok with a vcs that doesn't even
| support them.
| krick wrote:
| Not sure if it's true. I mean, I do agree with the core
| of it, but how do you even do PRs and resolve conflicts,
| if there are no branches and a developer cannot
| efficiently update his code against the last (remote)
| version of master branch?
| qsera wrote:
| To me mercurials branching is closer to the development
| process and preserves more information, because it
| records the original branch a commit was made.
|
| Git does not have such concept. That is a trade off and
| that trade off works great for projects managed like
| Linux kernel. But for smaller projects where there is a
| limited number of people working, the information
| preserved by mercurial could be very valuable.
|
| It also had some really interesting ideas like change set
| evolution, which enabled history re-writing after a
| branch has been published. Don't know its current status
| and how well it turned out to be..
| dugmartin wrote:
| JWZ is still blocking traffic from HN so you can't click
| to read this but you can copy/paste it into a new tab or
| open it in a incognito window:
|
| https://www.jwz.org/doc/worse-is-better.html
| kasey_junk wrote:
| GitHub had a business model where public repos were free.
| BitBucket didn't.
|
| That's it. That's why git won, you could put up open
| source libs with one for free and not the other.
|
| Which is extra funny as the centralized service was the
| most important part of decentralized version control.
| seniorThrowaway wrote:
| >the centralized service was the most important part of
| decentralized version control.
|
| I've often thought this about github
| worldsayshi wrote:
| Maybe forgejo has a shot?
| PeterStuer wrote:
| Unfortunatly out-of-the-box llm agents only focus on
| github support, creating friction.
| worldsayshi wrote:
| So pi.dev + forgejo?
| ptx wrote:
| It doesn't seem to support Mercurial though (not to imply
| that you were implying that it did). All I can find in
| this proxy/mirror thing to integrate it by presenting the
| Mercurial repo as a Git server:
| https://peterlavalle.github.io/post/forgejo-actions/
| outworlder wrote:
| > The issue is solely that OG Mercurial was written in
| Python.
|
| Are we back to "programming language X is slow" assertions?
| I thought those had died long ago.
|
| Better algorithms win over 'better' programming languages
| every single time. Git is really simple and efficient. You
| could reimplement it in Python and I doubt it would see any
| significant slowness. Heck, git was originally implemented
| as a handful of low level binaries stitched together with
| shell scripts.
| forrestthewoods wrote:
| They died because everyone knows that Python is infact
| very very slow. And that's just totally fine for a vast
| number of glue operations.
|
| It's amusing you call Git fast. It's notoriously
| problematic for large repos such that virtually every
| BigTech company has made a custom rewrite at some point
| or another!
| jstimpfle wrote:
| Now that is interesting too, because git _is_ very fast
| for all I have ever done. It may not scale to Google
| monorepo size, it would ve the wrong tool for that. But
| if you are talking Linux kernel source scale, it
| asolutely, is fast enough even for that.
|
| For everything I've ever done, git was practically
| instant (except network IO of course). It's one of the
| fastest and most reliable tools I know. If it isn't fast
| for you, chances are you are on a slow Windows filesysrem
| additionally impeded by a Virus scanner.
| forrestthewoods wrote:
| The fact that Git has an extremely strong preference for
| storing full and complete history on every machine is a
| major annoyance! "Except for network IO" is not a valid
| excuse imho. Cloning the Linux kernel should take only a
| few seconds. It does not. This is slow and bad.
|
| The mere fact that Git is unable to handle large binary
| files makes it an unusable tool for literally every
| project I have ever worked on in my entire career.
| pabs3 wrote:
| Git handles large text files and large directories fairly
| poorly too.
| spockz wrote:
| Git-lfs exists for a while now. Does that fix your issue?
| Or do you mean that it doesn't support binary diffs?
| forrestthewoods wrote:
| Git LFS is a gross hack that results in pain and
| suffering. Effectively all games use Perforce because Git
| and GitLFS suck too much. It's a necessary evil.
| jstimpfle wrote:
| git clone --bare --depth=1
| https://github.com/torvalds/linux
|
| Takes 21 seconds on my work laptop, indeed a corporate
| Windows laptop with antivirus installed. Majority of that
| time is simply network I/O. The cloned repository is 276
| MB large.
|
| Actually checking the kernel out takes 90 seconds. This
| amounts to creating 99195 individual files, totaling 2 GB
| of data. Expect this to be ~10 times faster on a Linux
| file system.
|
| So what's your problem?
| kuschku wrote:
| I've rewritten a python tool in go, 1:1. And that turned
| something that was so slow that it was basically a toy,
| into something so fast that it became not just usable,
| but an essential asset.
|
| Later on I also changed some of the algorithms to faster
| ones, but their impact was much lower than the language
| change.
| bonesss wrote:
| I don't know if people think this way anymore, but Python
| gained traction to some degree as a prototyping language.
| Verify the logic and structures, then implement the
| costly bits or performance sensitive bits in a more
| expense-to-produce more performant language.
|
| Which is only to say: that rewrite away from python story
| can also work to show python doing its job. Risk
| reduction, scaffolding, MVP validation.
| Diggsey wrote:
| > git was originally implemented as a handful of low
| level binaries stitched together with shell scripts.
|
| A bunch of low level binaries stitched together with
| shell scripts is a lot faster than python, so not really
| sure what the point of this comparison is.
|
| Python is an extremely versatile language, but if what
| you're doing is computing hashes and diffs, and generally
| doing entirely CPU-bound work, then it's objectively the
| wrong tool, unless you can delegate that to a fast,
| native kernel, in which case you're not actually using
| Python anymore.
| eru wrote:
| Well, you can and people do use Python to stitch together
| low level C code. In that sense, you could go the early
| git approach, but use Python instead of shell as the
| glue.
| saghm wrote:
| Their point was that by offloading the bottlenecks to C,
| you've essentially conceded that Python isn't fast enough
| for them, which was the original point made above
| eru wrote:
| Fair point!
| bmitc wrote:
| You barely have to try to have Python be noticeably slow.
| It's the only language I have ever used where I was even
| aware that a programming language could be slow.
| 20k wrote:
| Python is by far the slowest programming language, an
| order of magnitude slower than other languages
|
| One of the reason mercurial lost the dvcs battle is
| because of its performance - even the mercurial folks
| admitted that was at least in part because of python
| ragall wrote:
| > I thought those had died long ago.
|
| No, it's always been true. It's just that at some point
| people got bored and tired of pointing it out.
| jmalicki wrote:
| Every time I've rewritten something from Python into
| Java, Scala, or Rust it has gotten around ~30x faster.
| Plus, now I can multithread too for even more speedups.
|
| Python is absurdly slow - every method call is a string
| dict lookup (slots are way underused), everything is all
| dicts all the time, the bytecode doesn't specialize at
| all to observed types, it is a uniquely horrible slow
| language.
|
| I love it, but python is almost _uniquely_ a slow
| language.
|
| Algorithms matter, but if you have good algorithms, or
| you're already linear time and just have a ton of data,
| rewriting something from a single-threaded Python program
| to a multithreaded rust program I've seen 500x speedups,
| where the algorithms were not improved at all.
|
| It's the difference between a program running overnight
| vs. in 30 seconds. And if there are problems, the
| iteration speed from that is huge.
| byroot wrote:
| > every method call is a string dict lookup
|
| Doesn't the Python VM have inline caches? [0]
|
| https://en.wikipedia.org/wiki/Inline_caching
| jmalicki wrote:
| I think that's a new thing from like python 3.12+ or
| something after I stopped using Python as much.
|
| It didn't used to.
|
| EDIT: python 3.11+: https://peps.python.org/pep-0659/
| eru wrote:
| > [...], it is a uniquely horrible slow language.
|
| To be fair, Python as implement today is horribly slow.
| You could leave the language the same but apply all the
| tricks and heroic efforts they used to make JavaScript
| fast. The language would be the same, but the
| implementations would be faster.
|
| Of course, in practice the available implementations are
| very much part of the language and its ecosystems;
| especially for a language like Python which is so defined
| by its dominant implementation of CPython.
| jmalicki wrote:
| Fair! I guess I didn't mean language as such, but as
| used.
|
| But a lot of the monkey-patching kind of things and
| dynamism of python also means a lot of those sorts of
| things have to be re-checked often for correctness, so it
| does take a ton of optimizations off the table. (Of
| course, those are rare corner cases, so compilers like
| pypy have been able to optimize for the "happy case" and
| have a slow fall-back path - but pypy had a ton of
| incompatibility issues and now seems to be dying).
| dtech wrote:
| Javascript has a lot of the same theoretical dynamism,
| yet V8 and WebkitCore were able to make it fast
| eru wrote:
| Yes, with heroic effort. It's really a triumph of
| compiler / vm engineers over language designers.
| xmcqdpt2 wrote:
| You don't even need to go all V8, you could just build
| something like LuaJIT and get most of the way there.
| LuaJIT is like 10k LOCs and V8 is 3M LOC.
|
| The real reason is that it is a deliberate choice by the
| CPython project to prefer extensibility and
| maintainability to performance. The result is that python
| is a much more hackable language, with much better C
| interop than V8 or JVM.
| mike_hearn wrote:
| Python has a JIT compiling version in GraalPy. If you
| have pure Python it works well. The problem is, a lot of
| Python code is just callouts to C++ ML libs these days
| and the Python/C interop boundary just assumes you're
| using CPython and requires other runtimes to emulate it.
| eru wrote:
| > Better algorithms win over 'better' programming
| languages every single time.
|
| That's often true, but not "every single time".
| surajrmal wrote:
| You must belong to the club of folks who use hashmaps to
| store 100 objects. It's amazing how much we've
| brainwashed folks to focus on algorithms and lose sight
| of how to actually properly optimize code. Being aware of
| how your code interacts with cache is incredibly
| important. There are many cases of using slower
| algorithms to do work faster purely because it's more
| hardware friendly.
|
| The reason that some more modern tools, like jj, really
| blow git out of the water in terms of performance is
| because they make good choices, such as doing a lot of
| transformations entirely in memory rather than via the
| filesystem. It's also because it's written in a language
| that can execute efficiently. Luckily, it's clear that
| modern tools like jj are heavily inspired by mercurial so
| we're not doomed to the ux and performance git binds us
| with.
| inejge wrote:
| > You must belong to the club of folks who use hashmaps
| to store 100 objects.
|
| Apparently I belong to the same club -- when I'm writing
| AWK scripts. (Arrays are hashmaps in a trenchcoat there.)
| Using hashmaps is not necessarily an indictment you
| apparently think it is, if the access pattern fits the
| problem and other constraints are not in play.
|
| > It's amazing how much we've brainwashed folks to focus
| on algorithms and lose sight of how to actually properly
| optimize code. Being aware of how your code interacts
| with cache is incredibly important.
|
| By the time you start worrying about cache locality you
| have left general algorithmic concerns far behind. Yes,
| it's important to recognize the problem, but for most
| programs, most of the time, that kind of problem simply
| doesn't appear.
|
| It also doesn't pay to be dogmatic about rules, which is
| probably the core of your complaint, although unstated.
| You need to know them, and then you need to know when to
| break them.
| saghm wrote:
| > You could reimplement it in Python and I doubt it would
| see any significant slowness
|
| I doubt it _wouldn 't_ be significantly slower. I can't
| disprove it's possible to do this but it's totally
| possible for you to prove your claim, so I'd argue that
| the ball is in your court.
| ezst wrote:
| > Are we back to "programming language X is slow"
| assertions? thought those had died long ago.
|
| Yes we are? The slow paths of mercurial have been
| rewritten in C (and more recently in Rust) and improved
| the perf story substantially, without taking away from
| the wild modularity and extensibility hg always had.
| windward wrote:
| Mercurial can't rebase without an extension, or force push.
| Are you using a definition of strictly superior that means
| it has fewer features?
| saagarjha wrote:
| When I ask for this people like to explain that these are
| bad features nobody should want.
| qsera wrote:
| And they would be right,
| https://news.ycombinator.com/item?id=47766632
| qsera wrote:
| Mercurial's model is different from Git that these things
| you list does not make sense there.
|
| Rebase does not make sense in Mercurial because it has
| the concept of fixed branches. A commit is permanently
| linked to the branch on which it was made. So you are
| supposed to use merges.
|
| Same with force-pushing.
| windward wrote:
| I know. It's an opinion about how to develop that a lot
| of people hold - a declining proportion, mind you, like
| Mecurial's declining market share - and it's one that
| they're able to represent in Git's model, with Git's
| features. They're even able to do it without exposing me
| to it. But the same isn't true in reverse. Strictly
| superior?
|
| Believe me, I tried to have an open mind about it. Then
| one day I was getting ready to go on a work trip with a
| half-finished feature on my work laptop, and realised
| there was simply no in-model way for backing that wip up
| to the repo. If I lost my laptop, I lost the progress.
| mercurial-scm fails at SCM.
| qsera wrote:
| >in-model way for backing that wip up to the repo.
|
| That is because you have this notion of a "clean
| history", (which IIUC prevented you from making this
| permanent wip commit) which in reality does not have a
| lot of use. For most project, "useful history" or "real
| history" is better than a "clean" history.
|
| That is what mercurial caters to.
| ezst wrote:
| > one that they're able to represent in Git's model, with
| Git's features. They're even able to do it without
| exposing me to it. But the same isn't true in reverse.
| Strictly superior?
|
| not sure what you mean to say, but for thoroughness'
| sake, no: git and mercurial concepts are not
| interchangeable, with git having mostly an inferior
| model.
|
| To give examples: git has no concept of branching (in the
| way every VCS but Git uses the term). A branch in git is
| merely a tag on the tip of a series meant to signify that
| all ancestors belong to the same lineage. This comes with
| the implication that this lineage information is totally
| lost when two branches merge (you can't tell which side
| of the merge corresponded to which lineage). The ugly and
| generalised workaround is to abuse commit message (e.g.
| "merge feat-ABC into main") to store an essential piece
| of the repository history that the VCS cannot take.
|
| Another example is phasing: mercurial records at commit
| level whether it was exchanged with others or not. That
| draws a clean line between the history that's always safe
| to rewrite, and which that is subject to conflicting
| merges if the person you shared those commits with also
| happened to rewrite them on their end.
|
| > Then one day I was getting ready to go on a work trip
| with a half-finished feature on my work laptop, and
| realised there was simply no in-model way for backing
| that wip up to the repo. If I lost my laptop, I lost the
| progress. mercurial-scm fails at SCM.
|
| Sorry to be blunt, but that's a skill issue: hg is no
| different than every other VCS in that regard. If you
| want your WIP changes to leave your laptop, you've got to
| push them somewhere, just like you would in git.
| ezst wrote:
| I'd like to fill up some inaccuracies in your response:
|
| - rebasing in Mercurial simply means chopping a subtree
| off of the history and re-attaching it to a different
| parent commit. In that sense, rebasing is a very useful
| and common history-rewriting operation. In fact, it's
| even simpler and more powerful/versatile than in git,
| because mercurial couldn't care less if the sub-tree you
| are rebasing belongs to a branch or not: it's just a DAG.
| It gets transplanted from A to B. A may or may not be
| your checked commit, or be the tip of a branch, doesn't
| matter.
|
| - that mercurial requires a configuration toggle before
| rebasing can be used (i.e. that the user need to enable
| the extension explicitly) is a way to encourage
| interested users to learn their tool, and grow its
| capabilities together with their knowledge. It's
| opinionated, it may be too much hand-holding for some,
| but there is an elegant simplicity in keeping the help
| pages and autocomplete commands just as complex as the
| user can take it.
| raincole wrote:
| This matches my experience 100%. I was about to write a
| similar comment before I see yours.
| steveklabnik wrote:
| 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.
| smallmancontrov wrote:
| Right, and I'm glad there are projects serving The
| Cathedral, but I live in The Bazaar so I'm glad The Bazaar
| won.
|
| The efforts to sell priest robes to fruit vendors were a
| little silly, but I'm glad they didn't catch on because if
| they had caught on they no longer would have been silly.
| dwattttt wrote:
| GNU Bazaar thoroughly lost, last release was 2016,
| Canonical retired it last year:
| https://en.wikipedia.org/wiki/GNU_Bazaar
| hypeatei wrote:
| GP is taking about this[0] but it's quite hilarious that
| a VCS exists with that name.
|
| 0: https://en.wikipedia.org/wiki/The_Cathedral_and_the_Ba
| zaar
| bonzini wrote:
| It's not a coincidence, it was called like that as a
| reference to facilitating distributed development.
| landr0id wrote:
| >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.
|
| Fun story: I don't really know what Microsoft's server-side
| infra looked like when they migrated the OS repo to git
| (which, contrary to the name, contains more than just stuff
| related to the Windows OS), but after a few years they
| started to hit some object scaling limitations where the
| easiest solution was to just freeze the "os" repo and roll
| everyone over to "os2".
| w0m wrote:
| didn't msft write an ~entire new file system specifically
| to scale git to the windows code base?
|
| I have fuzzy memories on reading about it.
| landr0id wrote:
| They wrote something that allowed them to virtualize Git
| -- can't remember the name of that. But it basically
| hydrated files on-demand when accessed in the filesystem.
|
| The problem was I think something to do with like the
| number of git objects that it was scaling to causing
| crazy server load or something. I don't remember the
| technical details, but definitely something involving the
| scale of git objects.
| jamesfinlayson wrote:
| I thought Microsoft made a number of improvements to git
| to allow it work with all of their internal repos.
| kqr wrote:
| I have heard that the Google monorepo is called google3
| but I don't know why. Maybe those things are common...
| roca wrote:
| It's not that.
| vasco wrote:
| Thanks for explaining!
| ongy wrote:
| It's the third attempt of building the mono repo.
|
| But not the 3rd mono repo on the same technology to avoid
| some scaling limit.
| mike_hearn wrote:
| Probably a lot of Googlers don't know. It's ancient
| history, was called google3 even in 2006 when I first
| joined.
|
| google1 = code written by Larry, Sergey and employee
| number 1 (Craig). A hacky pile of Python scripts, dumped
| fairly quickly.
|
| google2 = the first properly engineered C++ codebase.
| Protobufs etc were in google2. But the build system was
| some jungle of custom Makefiles, or something like that.
| I never saw it directly.
|
| google3 = the same code as google2 but with a new custom
| build system that used Python scripts to generate
| Makefiles. I suppose it required a new repository so they
| could port everything over in parallel with code being
| worked on in google2. P4 was apparently not that great at
| branches and google3 didn't use them. Later the same
| syntax for the build files was kept but turned into a new
| languages called Starlark and the Makefile generator went
| away in favor of Blaze, which directly interpreted them.
|
| At least, that's the story I vaguely recall.
| MASNeo wrote:
| "roll everyone over to os2"
|
| The IBM crowd may feel vindicated at last.
| miki123211 wrote:
| So 30 odd years later, MS went from working on OS/2 to
| working on OS2?
|
| I guess what's old is new again.
| dijit wrote:
| Small nit: Googles monorepo is based on Perforce.
|
| I think what happened is Google bought a license for source
| code and customised it.
| steveklabnik wrote:
| Yes, the server is based on Perforce, called Piper, but
| the CLI is based on mercurial. So locally you're doing hg
| and then when you create a CL, it translates it into what
| p4 needs.
| surajrmal wrote:
| Depends on what frontend tool you use. You can use
| either. These days you can also use jj. I'm not sure the
| backend resembles peforce any longer.
| unmole wrote:
| > Google bought a license for source code and customised
| it.
|
| That makes sense because vanilla Perforce is unbearably
| slow and impossible to scale.
|
| Last I checked, it was bought by Private Equity firms and
| actual product development had more or less stopped.
| eqvinox wrote:
| I remember using darcs, but the repos I was using it with
| were so small as to performance really not mattering...
| riffraff wrote:
| I remember darcs fondly but even with tiny repos (maybe 5-6
| people working on it) we hit the "exponential merge"
| issues.
|
| It worked just fine 99% of the time and then 1% it became
| completely unusable.
| dented42 wrote:
| I definitely miss Darcs. I still use it very occasionally,
| but only with very small repos.
| bmitc wrote:
| Git is not remotely fast for large projects.
| Cthulhu_ wrote:
| Define "large"; I've never ran into serious performance
| issues during the ~15 years I've used Git, which either
| means the projects I've worked in aren't actually _large_
| large, or Git is fast enough for most use cases.
| ezst wrote:
| not OP, and indeed git is fast-enough in many cases, but
| git not cutting it at Google and Facebook scale, combined
| with the versatility of mercurial (monkeypatching and
| extensions system) was the reason why they both invested
| heavily in mercurial instead of git.
|
| Among the tricks being used was remotefilelogs, which is
| a way to "hydrate" content locally on-demand, which was
| mimicked in git many years later with Microsoft's git-
| vfs. Same goes with binary/large files that git
| eventually got as git-lfs.
|
| It's funny to think that a big reason for git to be
| "fast" today is by playing catch-up with mercurial, which
| carries this "forever stigma" of being slow.
| alwillis wrote:
| > I'm so glad git won the dvcs war. There was a solid decade
| where mercurial kept promoting itself as "faster than git".
|
| It wasn't the Mercurial team saying it was faster than Git;
| that was Facebook after contributing a bunch of patches after
| testing Mercurial on their very large mono-repo in 2014 [1]:
|
| _For our repository, enabling Watchman integration has made
| Mercurial's status command more than 5x faster than Git's
| status command. Other commands that look for changed files-
| like diff, update, and commit--also became faster._
|
| In fact they liked Mercurial so much they essentially cloned
| it to create their own dvcs, Sapling [2]. (An aside: Facebook
| did all of this because it was taking too long getting new
| engineers up to speed with Git. Shocker.)
|
| Today, most of the core of Mercurial has been rewritten in
| Rust; when Facebook did their testing, Mercurial was nearly
| 100% Python. That's where the "Mercurial is slow" thing came
| from; launching a large Python 2.x app took a while back in
| the day.
|
| I was messing with an old Mercurial repo recently... it was
| like a breath of fresh air. If I can push to GitHub using
| Mercurial... sign me up.
|
| [1]: https://engineering.fb.com/2014/01/07/core-
| infra/scaling-mer...
|
| [2]: https://sapling-scm.com/
| Gabrys1 wrote:
| You can push to GitHub using Sapling. I wish Sapling open
| source was given more love, as the experience for non-
| Facebookers is subpar. No bash completion outside the box,
| no distro packages, no good help pages, random issues
| interacting with a Git repo...
| withinboredom wrote:
| Sounds like what my teachers used to say: "a personal
| problem". Literally nobody outside FB knows what they're
| missing and until they fix that, literally nobody cares.
| itsdesmond wrote:
| > Sounds like what my teachers used to say: "a personal
| problem".
|
| They don't sound like a very good teacher.
| withinboredom wrote:
| Judging by the amount of adults wandering around thinking
| their personal problems are everyone else's problem...
| they were pretty good teachers.
| Ericson2314 wrote:
| Sapling and JJ can sort it out, the outside world will
| only care for one of them.
| alwillis wrote:
| > Sapling and JJ can sort it out, the outside world will
| only care for one of them.
|
| I was immediately intrigued when I learned that JJ has
| revsets [1], just like Mercurial.
|
| [1]: https://docs.jj-vcs.dev/latest/revsets/
| smallmancontrov wrote:
| No, the "hg is fast" marketing claim that retreated to "hg
| is Big-O fast and you are dumb for caring about constant
| terms and factors even if they clearly dominate your use
| case" predates 2014 and the Facebook patches. These talking
| points were old in 2010. Mercurial was always dog slow and
| always gaslighting about it.
|
| I'm glad BigCo made tools to serve their needs, but their
| needs aren't my needs or most peoples' needs.
|
| > Mercurial has been rewritten in Rust
|
| I'm glad they saw the light eventually! Ditto for the rest
| of the Rust Tooling Renaissance.
| littlecranky67 wrote:
| I might be the outlier, but am I the only one who doesn't
| care much about the speed of git? I've been using git since
| 2011 as my main vcs for personal and professional work as a
| freelancer contractor. Whenever I "wait" for git, it is
| either limited by the bandwidth (git clone) or by the amount
| of commit hooks that I implemented for linting, verification
| etc. The percentage of time actually spent in git internal
| execution must be a tiny fraction of my day to day usage.
| What IS affecting me (and my the teams I work in) is
| usability and UX experience. I.e. if people would screw up
| stuff (no matter if in git or mercurial) we spent far more
| time fixing this - I don't think the impmentation speed would
| matter here.
|
| The only case I can imagine is when doing a full checkout of
| a big repo, but even there, there is --depth which is quite
| practical.
| windward wrote:
| Isn't it kind of like how you don't care much about the
| oxygen content of the air around you, but you'd miss it if
| it was gone? I've done development with Mercurial, simple
| processes were irritatingly slow, particularly if you stray
| from the better-supported opinionated path.
| ak217 wrote:
| I spent a long time educating teams of developers about
| git's usability quirks. I don't do that as much anymore -
| partly because the quirks have been worked out, partly
| because the developers have better guardrails and resources
| to learn from.
|
| This whole time (the past 15 years) git has been getting
| faster without most of us noticing, because big companies
| have been investing in speeding it up. The reason you don't
| notice or care is that they work on a very different scale.
| Thousands of users, thousands of PRs per day, millions of
| CI/CD jobs all hitting the repo.
|
| Now the cycle is repeating again because these numbers are
| shooting through the roof because of agentic coding.
| calebio wrote:
| I miss the Phabricator review UI so much.
| montag wrote:
| Me too. And I'm speaking from using it at Rdio 15 years ago.
|
| Nothing since (Gerrit, Reviewboard, Github, Critique) has
| measured up...
| Rodeoclash wrote:
| Thanks for your work on Rdio. I miss it. Were you around
| when that guy managed to spam plays to get fake albums to
| the top of the charts?
| sam_bristow wrote:
| 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.
| ivantop wrote:
| It's still phabricator
| sam_bristow wrote:
| Any idea if their internal version has improved
| dramatically since they stopped maintaining the public
| version?
| kqr wrote:
| I don't think they ever maintained the public project.
| Priestly spun off a company to do that.
| xixixao wrote:
| It has improved massively by 2023 when I left.
| treefry wrote:
| 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.
| surajrmal wrote:
| Why not use gerrit? It's pretty similar.
| Redoubts wrote:
| is it? it's insanely ugly and interactions seem bizarre
| eru wrote:
| 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-...
| illamint wrote:
| Good. That's the point.
| eru wrote:
| The point of what?
|
| I hope they fixed phabricator in the meantime.
| dbetteridge wrote:
| The point is the main branch reflects the "units" of
| change, not the individual commits to get there.
|
| One merged pr is a unit of change, at the end of the day
| the steps you took to produce it aren't relevant to
| others.
|
| My opinion of course, I'm open to understanding why
| preserving individual commits is beneficial
| eru wrote:
| You can get what you want from `git log --first-parent`
| without having to toss out information.
|
| See how the Linux kernel handles git history to see a
| good example of non-linear history and where it helps.
| They use merge commits, ie commits with more than one
| ancestor, all the time.
| saagarjha wrote:
| A unit of change is a commit. I have no idea why you'd
| think a PR is a unit of change.
| zip1234 wrote:
| Does your org approve specific commits or PRs overall?
| saagarjha wrote:
| PRs. This is largely why we don't do stacked PRs: they
| can just be a chain of commits in one PR
| KwanEsq wrote:
| My understanding was that that was more a function of how arc
| submitted stuff to Phabricator, rather than solely
| Phabricator itself. arc at submission time submitted a bunch
| of different commits as a single Phabricator DREV or whatever
| the terminology is/was (basically a DREV is the {domain}/D123
| webpage you'd do a review on). But other tools that submitted
| commits to Phabricator instances (and maybe even arc itself
| with the right flag?) submitted each commit as its own
| separate DREV, so each commit got its own separate /D{N} page
| and its own review, but all linked together in a stack. And
| then still landed as separate commits in the actual repo.
| This is how code submission works with Mozilla's use of
| Phabricator.
| eru wrote:
| Well, I didn't care whether it was phabricator or whatever
| it was using. It was bad.
| nerdypepper wrote:
| tangled.org supports native stacking with jujutsu, unlike
| github's implementation, you don't need to create a new branch
| per change: https://blog.tangled.org/stacking/
| choi0330 wrote:
| You should definitely try out
| https://github.com/hokwangchoi/pilegit. It's platform-agnostic
| and I use for my workflow with Phabricator, Github, Gitlab and
| Gitea. No learning curves for cross-platform operations!
| siva7 wrote:
| 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.
| jollyllama wrote:
| 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.
| cleverdash wrote:
| 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.
| dbbk wrote:
| If you visit the webpage it gives you integration instructions
| for agents
| ameliaquining wrote:
| 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.
| Arainach wrote:
| 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.
| Hamuko wrote:
| > _It amounts to doing N code reviews at once rather than a
| few small reviews which can be done individually_
|
| I truly do not comprehend this view. How is reviewing N
| commits different from/having to do less reviews reviewing
| N separate pull requests? It's the same constant.
| Arainach wrote:
| Small reviews allow moving faster for both the author and
| reviewer.
|
| A chain of commits:
|
| * Does not go out for review until the author has written
| all of them
|
| * Cannot be submitted even in partial form until the
| reviewer has read all of them
|
| Reviewing a chain of commits, as the reviewer I have to
| review them all. For 10 commits, this means setting aside
| an hour or whatever - something I will put off until
| there's a gap in my schedule.
|
| For stacked commits, they can go out for review when each
| commit is ready. I can review a small CL very quick and
| will generally do so almost as soon as I get the
| notification. The author is immediately unblocked. Any
| feedback I have can be addressed immediately before the
| author keeps building on top of it.
| tcoff91 wrote:
| Let's compare 2 approaches to delivering commits A, B, C.
|
| Single PR with commits A, B, C: You must merge all
| commits or no commits. If you don't approve of all the
| commits, then none of the commits are approved.
|
| 3 stacked PRs: I approve PR A and B, and request changes
| on PR C. The developer of this stack is on vacation. We
| can incrementally deliver value by merging PRs A and B
| since those particular changes are blocking some other
| engineer's work, and we can wait until dev is back to fix
| PR C.
| mike_hearn wrote:
| _> You must merge all commits or no commits_
|
| This seems to be the root of the problem. Nothing stops a
| reviewer merging some commits of a PR, except a desire to
| avoid the git CLI tooling (or your IDE's support,
| or....). The central model used in a lot of companies
| requires the reviewee to do the final merge, but this has
| never been how git was meant to be used and it doesn't
| have to be used that way. The reviewer can also do
| merges. Merge (of whichever commits) = approval, in that
| model.
| tcoff91 wrote:
| Yes, the root of the problem is the workflow of the
| company being centered around GitHub instead of Git
| itself.
|
| This feature helps improve GitHub so it's useful for
| companies that do this this way.
|
| At our company, only admin users can actually directly
| git push to main/master. Everything else HAS to be merged
| via github and pass through the merge queue.
|
| So this stacked PRs feature will be very helpful for us.
| tcoff91 wrote:
| It's crazy that you're getting downvoted for this take.
|
| This isn't reddit people. You're not supposed to downvote
| just because you disagree. Downvotes are for people who are
| being assholes, spamming, etc...
|
| If you disagree with a take, reply with a rebuttal. Don't
| just click downvote.
| steveklabnik wrote:
| Historically, hn etiquette is that it's fine to downvote
| for disagreement. This came from pg himself.
|
| That said, while he hasn't posted here for a long time,
| this is still in the guidelines:
|
| > Please don't post comments saying that HN is turning
| into Reddit. It's a semi-noob illusion, as old as the
| hills.
|
| https://news.ycombinator.com/newsguidelines.html
| tcoff91 wrote:
| Well, I stand corrected.
| adamwk wrote:
| 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
| flyingcircus3 wrote:
| 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.
| adamwk wrote:
| This is what I understood as well, but it sounded like GP
| had success doing it; so I was curious if there was a trick
| I didn't know about
| flyingcircus3 wrote:
| I take from GP that they try to make their branches
| small, and keep the cycle of development->review->merging
| small, so that the problem stacked PRs seeks to solve
| doesn't materialize in the first place.
|
| Stacked PRs in my experience has primarily been a request
| to merge in a particular order. If you're the only
| merger, as in GP's case, there's no need to request this
| of yourself.
| skydhash wrote:
| Whenever I send a big diff. I spend some time annotating
| with comment first to helps the reviewer. A good summary
| of the changes in the description, the I annotate the
| diff of the PR, explaining approaches, the design of a
| specific changes, tricky part of the code,
| boilerplate,... Trying to guess the context is where the
| review bottleneck is, so I present it alongside the code.
| leleat wrote:
| 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-...
| KptMarchewa wrote:
| I understand that's what the feature is trying to solve.
| steveklabnik wrote:
| 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.
| 4b11b4 wrote:
| 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
| steveklabnik wrote:
| It sometimes will hallucinate older CLI options, because jj
| has changed at various times, but it's pretty decent with
| it at this point. The harder part is that a lot of plugins
| hardcode git into them.
| jillesvangurp wrote:
| 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.
| recursivegirth wrote:
| I spend more time in planning and steering the AI
| implementation than I do on reviewing it's outputs.
|
| I do the obvious checks like tests and spin up a dev instance
| to make sure the feature works like I want it too, but very
| rarely am I reviewing every line of code these days.
| jamietanna wrote:
| 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!
| sailorganymede wrote:
| Thank goodness. It was a pain to do this manually
| fweimer wrote:
| 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.
| CharlieDigital wrote:
| 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.
| jannes wrote:
| 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
| bob1029 wrote:
| 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.
| Yokohiii wrote:
| 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.
| TZubiri wrote:
| 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.
| varun_ch wrote:
| https://news.ycombinator.com/item?id=47614038
| TZubiri wrote:
| 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.
| baq wrote:
| 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.
| enraged_camel wrote:
| 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.
| masklinn wrote:
| They do not no. Afaik GitHub has little to nothing that is
| cross repository.
| jenadine wrote:
| 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?
| tcoff91 wrote:
| 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.
| js2 wrote:
| > 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
| sunshowers wrote:
| To my knowledge, stacked diffs were first done in the Linux
| kernel as stacks of patches sent over email. From there
| they spread to Google and Facebook. (Source: I worked on
| Facebook's source control team from 2012-2018 and did a lot
| of work to enable stacked diffs there.)
| js2 wrote:
| Right, I was thinking from a web-based UI. The "pull
| request" term is from git (AFAIK), but git itself was
| built to accommodate the earlier concept of mailing
| patches around. (Source: I've been using version control
| since RCS/SCCS days and contributed here and there to git
| in its infancy. Also an early user/contributor to
| Gerrit.)
| Izkata wrote:
| > The "pull request" term is from git (AFAIK)
|
| Possibly from github. It got popularized there at least,
| encouraging forking code, and is why so many people say
| "pull request" when they mean "merge request".
| js2 wrote:
| GitHub took the "pull request" terminology from Git. A
| kernel developer (say) would have a bunch of changes
| ready in their local Git server and would request a pull
| from Linus, hence a pull request. There's literally a
| command for it:
|
| https://git-scm.com/docs/git-request-pull
|
| The command is so old it's still written in shell:
|
| https://github.com/git/git/blob/master/git-request-
| pull.sh
|
| It was first added July 27, 2005:
|
| https://github.com/git/git/commit/ab421d2c7886341c246544b
| c8d...
|
| https://lore.kernel.org/git/20050726073036.GJ6098@mythrya
| n2....
|
| But even then, it simply codified existing terminology.
|
| Ah, someone else did the research, so minimally BitKeeper
| had the "pull" command first and the term "pull request"
| falls naturally from that:
|
| https://rdnlsmith.com/posts/2023/004/pull-request-
| origins/
| p-e-w wrote:
| At some point, a derivative idea becomes so different
| from the original one that it's a novel idea in essence.
| Just like SMS is ultimately a derivative of cuneiform
| tablets, and yet it isn't in any meaningful sense.
| monster_truck wrote:
| Imagine gettting a cuneiform tablet by courier telling
| you that you have unpaid parking tickets in a state
| you've never driven in
| sunshowers wrote:
| I don't think mailing stacks of patches is that
| different? As someone who built this stuff it was pretty
| obvious to me that web-based patch stack management was a
| relatively small evolution over mailing lists. Tools like
| patchwork bridged the gap initially, and we were quite
| familiar with them.
| hokumguru wrote:
| Congrats and thank you. You helped build one of the best
| devex experiences I've ever had the pleasure of working
| with :)
| pabs3 wrote:
| Gerrit was forked from Rietveld. Not sure if Rietveld or
| Gerrit are better though.
|
| https://github.com/rietveld-codereview/rietveld
| https://en.wikipedia.org/wiki/Rietveld_(software)
| https://codereview.appspot.com/
| eptcyka wrote:
| What if main/master moves in between reviews?
| tcoff91 wrote:
| Rebase the stack onto main.
| jrochkind1 wrote:
| Before this feature when you were doing it manually, it was
| a huge problem. One of the points of this feature, is it
| automates rebasing the whole stack.
| mh2266 wrote:
| you just rebase it? what's the big deal?
|
| I don't use Github but I do work at one of the companies
| that popularized this workflows and it is extremely not a
| big deal. Pull, rebase, resolve conflicts if necessary,
| resubmit.
| adregan wrote:
| You head to the farthest branch in the chain, fetch the
| latest main, and run `git rebase --update-refs main` (I
| prefer interactive mode myself) and then force push all of
| the branches from start to the end.
|
| 1: https://git-scm.com/docs/git-rebase#Documentation/git-
| rebase...
| jrochkind1 wrote:
| 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.
| ttoinou wrote:
| Even with one developer on a repo this sounds like useful
| xixixao wrote:
| 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")
| mikeocool wrote:
| 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
| OJFord wrote:
| Until someone merges master into their feature branch rather
| than rebasing it. (And then that branch later gets merged.)
| sheept wrote:
| This shouldn't be a problem if you stick to commits and
| merges. --first-parent will skip past commits, including
| merge commits, in merged branches.
| OJFord wrote:
| Fair - but not if it's not their feature branch but their
| local master; they pull & merge the remote changes and
| then push the result.
| OptionOfT wrote:
| 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.
| MrJohz wrote:
| 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.
| muti wrote:
| +1 on this, I also switched to jj when working with any git
| repo.
|
| What's funny is how much better I understand git now, and
| despite using jj full time, I have been explaining concepts
| like rebasing, squashing, and stacked PRs to colleagues who
| exclusively use git tooling
| skydhash wrote:
| The magic of the git cli is that it gives you control.
| Meaning whatever you want to do can be done. But it only
| gives you the raw tools. You'll need to craft your own
| workflow on top of that. Everyone's workflow is different.
|
| > So while I'm working on my branch, I can iteratively make
| a[...]which means git blame tends to be much more accurate
| and helpful
|
| Everything here I can do easily with Magit with a few
| keystroke. And magit sits directly on top of git, just with
| interactivity. Which means if I wanted to I could write a
| few scripts with fzf (to helps with selection) and they
| would be quite short.
|
| > And while I'm working on a branch, I still have access to
| the full history of each commit...
|
| Not sure why I would want the history for a specific
| commit. But there's the reflog in git which is the ultimate
| undo tool. My transient workspace is only a few branches (a
| single one in most cases). And that's the few commits I
| worry about. Rebase and Revert has always been all I needed
| to alter them.
| rstuart4133 wrote:
| > But there's the reflog in git which is the ultimate
| undo tool.
|
| That one sentence outs you as someone who isn't familiar
| with JJ.
|
| Here is something to ponder. Despite claims to the
| contrary, there are many git commands that can destroy
| work, like `git reset --hard`. The reflog won't save you.
| However there is literally no JJ command that can't be
| undone. So no JJ command will destroy your work
| irretrievably.
| skydhash wrote:
| I've just tested that exact command and the reflog is
| storing the changes. It's different from the log command
| which displays the commit tree for the specified branch.
| The reflog stores information about operations that
| updates branches and other references (rebase, reset,
| amend, commit,...). So I can revert the reset, or a pull.
| rstuart4133 wrote:
| `git reset --hard` destroys uncommitted changes. There is
| no git command to recover those files. JJ has a similar
| command of course, but it saves the files to a hidden
| commit before changing them.
| saagarjha wrote:
| --force-with-lease is useless if you ever use tools that
| refresh git status.
| d0mine wrote:
| You are describing gerrit.
|
| https://www.gerritcodereview.com/
| ahmadyan wrote:
| 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.
| icy wrote:
| We've got this over on Tangled. :) https://tangled.org
| rtpg wrote:
| yeah interdiffing and being able to cherrypick in a review just
| there and getting it done is nice.
|
| Perhaps a future iteration of this feature will at least allow
| us to do something like merge just steps of it if they can be
| reordered.
| trashburger wrote:
| You want something like Gerrit.
| robertwt7 wrote:
| 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
| ghthor wrote:
| Yep, very happy with graphite at work.
| heeton wrote:
| Same, our team has been on it for a year and it's very good.
| acjohnson55 wrote:
| They recently got bought by Cursor.
| K0IN wrote:
| 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.
| DesiLurker wrote:
| what happened to the old gerrit reviews, I loved its handling on
| incremental patchsets. github is primitive by comparison.
| ghighi7878 wrote:
| What's difference between stacked PRs and merge trains in gitlab?
| masklinn wrote:
| 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).
| ghighi7878 wrote:
| Ahh so stacked diffs? https://docs.gitlab.com/user/project/me
| rge_requests/stacked_...
| IshKebab wrote:
| Yes, except with a proper UI. Also while you could do this
| on GitHub before in the same way that that Gitlab feature
| works, it didn't work cross-repo so in practice it wasn't
| an option for most open source code.
|
| I can't remember if Gitlab has the same limitations but I
| do remember trying to use Gitlab's stacked diffs and
| finding them to not work very well. Can't remember why tbh.
| inerte wrote:
| 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).
| tcoff91 wrote:
| 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.
| mattstir wrote:
| It appears the CLI is only half-baked so far. Given how many
| things they've borrowed from Graphite (a tool which adds this
| type of workflow), it should only be a matter of time until
| they add a `split` command. Graphite lets you split a large set
| of changes by commit or by hunk which is very handy.
| akersten wrote:
| 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..
| xixixao wrote:
| 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)
| akersten wrote:
| > 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).
| skydhash wrote:
| Because at first your have this main <- PR
| A <- PR B
|
| Then you'll have main, squashed A
| \ \-> PR A -> PR B
|
| The tip of B is the list of changes of both A and B, while
| the tip of main is now the squashed version of the changes
| of A. Unless a branch tracks the end of A in the PR B, It
| looks like more you want to apply A and B on top of A
| again.
|
| A quick analogy to math main is X A
| is 3 B is 5
|
| Before you have X + 3 + 5 which was equivalent to X + 8,
| but then when you squash A on on X, it looks like (X + 3) +
| (3 + 5) from `main`'s point of view, while from B, it
| should be X + (3 + 5). So you need to rebase B to remove
| its 3 so that it can be (X + 3) + 5.
|
| Branches only store the commits at the top. The rest is
| found using the parent metadata in each commits (a linked
| list. Squashing A does not remove its commits. It creates a
| new one, and the tip of `main` as its parent and set the
| new commit as the tip of `main`. But the list of commits in
| B still refer to the old tip of `main` as their ancestor
| and still includes the old commits of A. Which is why you
| can't merge the PR because it would have applies the
| commits of A twice.
| gregoryl wrote:
| 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?
| Smaug123 wrote:
| 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...
| heldrida wrote:
| This is actually a reasonable workflow. Although requires
| some preparation. I'll try it out!
| mckn1ght wrote:
| Yep that's how I do it if I have to deal with stacked
| PRs. I also just never use rebase once anything has
| happened in a PR review that incurs historical state,
| like reviews or other people checking out the branch
| (that I know of, anyways). I'll rebase while it's local
| to keep my branch histories tidy, but I'll merge from
| upstream once shared things are happening. There are a
| bunch of tools out there for merging/rebasing entire
| branch stacks, I use https://github.com/dashed/git-chain.
| akersten wrote:
| 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.
| skydhash wrote:
| > the "update branch" button for PR B is grayed out because
| there are these hallucinated conflicts due to the new
| squash commit
|
| Those are not hallucinated. PR B still contains all the old
| commits of A which means merging would apply them twice.
| The changes in PR B are computed according to the oldest
| commits belonging to PR B and main which is the parent of
| squashed A. That would essentially means applying A twice
| which is not good.
|
| As for updating PR B, PR B doesn't know where PR A (that
| are also in PR B) ends because PR A is not in main.
| Squashed A is a new commit and its diff corresponds to the
| diff of a range of commits in PR B (the old commits of PR
| A), not the whole B. There's a lot of metadata you'd need
| to store to be able to update PR B.
| akersten wrote:
| I guess to me, I'm looking at it from the perspective of
| diffing the repo between the squashed commit on main and
| the tip of the incoming PR. If there are merge conflicts
| during the rebase in files that _don 't appear in that
| diff_, I consider that a hallucination, because those
| changes _must_ already in the target branch and no matter
| what happened to those files along the way to get there,
| it will always be a waste of my time to see them during
| an interactive rebase.
|
| I don't think we need to store any additional metadata to
| make the rebase just slightly more smarter and able to
| skip over the "obvious" commits in this way, but I'm also
| just a code monkey, so I'm sure there are Reasons.
| skydhash wrote:
| You're looking at it from the perspective of a human
| reasoning. But a computer is a simple machine (what it
| can do, not how it does it). What seems obvious to you
| could be a complicated algorithm.
|
| Git store all its information as a directed acyclic graph
| (a tree) of commits. The leaves of that tree have names,
| and are what we called branches. Each commit points to a
| tree (also a tree data structure) where the nodes are
| blobs (files) and sub trees. But that tree only stores
| the files that has been changed since the last commit.
| Git does not store diffs. Diffs are computed as needed.
|
| This why the common ancestor commit is important. From
| there, a version of the working directory is computed for
| each branch (main-with-squashed-A and PR B). Files that
| have not been changed since PR A are ok, but everything
| else will be different, especially if you've modified the
| same lines.
|
| Squashed A is a brand new commit with a new tree that PR
| B does not know about. You need to recompute PR B on top
| of Squashed A, (which will create new commits for PR B).
| pastel8739 wrote:
| 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.
| adregan wrote:
| I typically prefix my commit messages with the ticket number
| to make it easier to spot the commits to drop.
| claytonjy wrote:
| This is what i often do, but i have never been able to get
| many coworkers onboard. In my experience I'd say less than 5%
| of all software folk i've worked with are willing to do an
| interactive rebase; everyone else finds it too scary
| contravariant wrote:
| 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.
| sameenkarim wrote:
| 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.
| puelocesar wrote:
| That's how I've been working for years now. Does anyone know
| how this gh stacks work internally? Does it do the same thing
| under the hood?
|
| I'm conflicted about it, seems like a good convenience, but I
| wouldn't want my team to get dependent on an exclusive
| feature of a single provider
| steveklabnik wrote:
| (You're replying to someone from product at GitHub, they're
| explaining that's how it works there)
| jd3 wrote:
| We dealt with this headache for 7+ years at my former
| employer. Thanks so much for this.
| patrickthebold wrote:
| 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.
| jiveturkey wrote:
| 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!
| Phlogistique wrote:
| I made a tool that adresses this precise problem:
| https://github.com/scortexio/autorestack-action/
|
| It does some merge magic so that PR B shows the correct diff;
| and does so without needing to force push, so on your side you
| can just "git pull" and continue working.
|
| Of course I expect this repo to become obsolete when GitHub
| makes their native stacking public.
| teaearlgraycold wrote:
| Wondering how all of those startups that implement this for
| GitHub feel right now.
| zaps wrote:
| sherlocked
| mc-serious wrote:
| 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?
| atq2119 wrote:
| 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.
| Liskni_si wrote:
| They haven't fixed the fork issue, the FAQ clarifies this. I
| suspect the target audience is squash merging corpos. Everyone
| else can just do normal PRs with atomic commits reviewed
| individually...
| scottfits wrote:
| cherry picking is so fragile, this is at least a step in the
| right direction
| jrochkind1 wrote:
| 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.
| ninkendo wrote:
| > 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.
| steveklabnik wrote:
| 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.
| Phelinofist wrote:
| 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.
| steveklabnik wrote:
| That is why your forge will show that these two things are
| related to each other, and you may have the same person
| assigned to review both. It can show you this particular
| change in the context of the rest of them. But not every
| reviewer will always want to see all of the full context at
| all times.
| ninkendo wrote:
| > 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.)
| steveklabnik wrote:
| > fixable with code owners files.
|
| Code owners automatically assigns reviewers. You still end
| up in the state where many groups are assigned to the same
| PR, rather than having independent reviews.
|
| > I already said GitHub lets you view single commits during
| PR review.
|
| Yes, you can look at them, but your review is still in the
| context of the full PR.
|
| > 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.
|
| The feature being discussed here is making this a first-
| class feature of the platform, much nicer to use. The
| second PR is "stacked" on top of the first.
| ninkendo wrote:
| > You still end up in the state where many groups are
| assigned to the same PR
|
| > Yes, you can look at them, but your review is still in
| the context of the full PR.
|
| Why is this a bad thing? I don't get it. This has
| literally never been a problem once in my career. Is the
| issue that people can't possibly scroll past another
| discussion? Or... I seriously am racking my brain trying
| to imagine why it's a bad thing to have more than one
| stakeholder in a discussion.
|
| I can think of a _lot_ of reasons why doing the opposite,
| and siloing off discussions, leads to disaster. _That_ is
| something I've encountered constantly in my career. We
| start out running an idea past group A, they iterate,
| then once we reach a consensus we bring the conclusion to
| group B and they have concerns. But oh, group A already
| agreed to this so you need to get on board. So group B
| feels railroaded. Then more meetings are called and we
| finally bring all the stakeholders together to discuss,
| and suddenly hey, group A and B both only had a partial
| view of the big picture, and why didn't we all discuss
| this together in the first place? That's happened more
| times in my career than I can count. The number of times
| group B is mad that they have to move their finger to
| scroll past what group A is talking about? Exactly zero.
| steveklabnik wrote:
| It's totally possible that you aren't the target audience
| for this sort of feature. It tends to be more useful in
| very large team and/or monorepo contexts.
|
| This isn't about siloing discussions: it's about focus.
| You can always see the full stack if you want to go look
| at the other parts, the key is that you don't _have_ to.
|
| The goal is to get thoroughly reviewed changes. It's much
| easier to review five 100 line changes than one 500 line
| one, and it's easier to review five 500 line changes than
| it is a 2500 line one. Keeping commits small and tightly
| reviewed leads to better outcomes in the end. Massive PRs
| lead to rubber stamps of +1.
|
| I agree that that scenario sounds like a nightmare. But I
| don't think that a PR is the right place to solve that
| problem: it sounds like something that should have been
| sorted before any of the code was written in the first
| place.
| ninkendo wrote:
| > It's much easier to review five 100 line changes than
| one 500 line one, and it's easier to review five 500 line
| changes than it is a 2500 line one.
|
| This is true if the changes are orthogonal and are truly
| independent. One should always favor small independent
| changes if one can.
|
| But when changes are all actually part of the same unit,
| and aren't separable (apart from maybe the first of N of
| them which may be mergeable independently), proponents
| always seem to advocate that stacked diffs can somehow
| change this fact. "Oh if only we had stacked diffs we
| could break this into smaller changes", ignoring the fact
| that no, they'd still be ordered and dependent on one
| another.
|
| Stacked diffs seem like a UI convenience for reviewers...
| that's fine I guess. GitHub is basically what you get
| when you ask the question "how can we make code review as
| tedious and unhelpful as possible", and literally
| anything would be better than what we have (seriously I
| could fill a _book_ with how bad GitHub is. I don't think
| I could design a worse experience if I tried.) So, maybe
| I should just be happy they're trying anything.
| steveklabnik wrote:
| In stacked diffs systems, the idea is that the base of
| the stack (once reviewed) can always be merged
| independently, so you're totally right that like, if you
| just purely think you can split things up when they
| shouldn't be split up, that would be bad.
|
| This is the model that the kernel uses, as well as tons
| of other projects (any Gerrit user, for example), and so
| it has gotten real-world use and at scale. That said,
| everyone is also entitled to their preferences :)
| ninkendo wrote:
| > This is the model that the kernel uses
|
| Nah.
|
| The kernel uses a mailing list, and a "review" means a
| mailing list thread. With some nice CLI tools to
| integrate with git when you want to actually apply the
| patch (or start a review thread.)
|
| In that world, "[PATCH 2/5]" (or whatever) in the subject
| title, and a different CC list for each patch, is a nice
| way to be able to ensure different subsets of the patch
| series have different discussions. That's great.
|
| But if you're going to compare this to a GitHub UI, you
| have to choose the basis for comparison, because the two
| are so utterly different. Choosing one aspect (can we
| make sure discussions are kept separate), and saying
| "therefore the kernel uses stacked diffs" is a huge
| misrepresentation of how different GitHub's approach is.
|
| Because the kernel approach is the platonic ideal of a
| code review: it's a simple threaded discussion between
| stakeholders, centered around a topic (the patch, which
| is inlined right in the email.) I would wager close zero
| kernel maintainer actually look at the diffs exclusively
| via their email client. They probably just check out the
| changes locally and look at them, and the purpose of the
| mailing list is to facilitate focused discussion on parts
| of the change (which is all we really want, in the end.)
|
| GitHub has so thoroughly shit the bed on actually
| developing a good model of "threaded discussion about a
| change", that you have to _change the way you think about
| git's model_ to fix how awful GitHub is at allowing
| review discussion to stay focused. You shouldn't need to
| think about stacked diffs and multiple PR's. You should
| use git branches as intended, multiple commits
| representing changes, and a merge meaning "this branch
| makes it or not." That GitHub's UI for discussing subsets
| of a change is so abysmal, does not mean the model is
| wrong. It means their discussion system is so abysmal
| that a mailing list TUI can run circles around it. Fixing
| this is GitHub's problem, and doesn't require _any_
| changes to how PR's should be split up.
|
| If you have a 2500-line PR with 5 500-line commits,
| GitHub should not require you to split things up further
| in any way, just to unfuck their discussion system.
|
| Random idea I spent 10 seconds thinking about: let me
| start a "here's a thread discussing the UI changes" and
| add folks to it, and "here's a thread discussing the
| backend changes", and add folks to that. I can then say
| "let's not merge this until both threads are green". You
| still see the whole change in the UI. (You can click
| directories to drill into the changes, that solves the
| "but the diff is too big" issue.) Discussion on a chunk
| of the diff is scoped to a _discussion thread_ , which
| you select when sending the message. Thus, all discussion
| on any part of the diff is still scoped to a "discussion
| thread" of arbitrary subsets of stakeholders.
|
| None of this needs me to change how I split up my git
| branches, an entire logical change is still either
| "merged" or "not-merged" (seriously who cares about the
| Pyrrhic victory of merging only change 1/N), and if we
| want to limit scopes of discussion to subsets of a
| change, we can just... _do that._
| steveklabnik wrote:
| Sorry, I am talking about stacked diffs in general, not
| this specific implementation on GitHub. That "Patch 2/5"
| is five stacked diffs, on top of each other. Forges that
| are stacked-diff native do that same kernel flow, just on
| the web instead of over email. You can also see this
| corroborated over here:
| https://news.ycombinator.com/item?id=47758251
|
| All of the advantages, like "it's a simple threaded
| discussion between stakeholders, centered around a
| topic", is exactly why people like stacked diffs over
| PRs.
|
| GitHub is doing "stacked PRs", which is like stacked
| diffs but more like PRs in the sense that they're stacked
| _branches_ rather than stacked _diffs_. I agree that this
| seems less ideal, but they also are putting it into an
| existing project, rather than rebuilding everything
| around it. There 's pros and cons to both approaches, but
| I agree that I'd prefer a native system built for this,
| personally. I'm still glad they're going to be
| popularizing the general concept.
| ninkendo wrote:
| > Sorry, I am talking about stacked diffs in general, not
| this specific implementation on GitHub
|
| My point is that the LKML and what GitHub do is so
| different that the definition of "stacked diffs in
| general" can only describe a tiny aspect of each, if you
| want to call both of their approaches by the same name.
| From where I sit, the only common element between them is
| "they offer a way to keep discussion separated."
|
| If that's all people are actually complaining about,
| there are a thousand better ways to "keep discussion
| separated" that don't require me to pretend that it's ok
| that only a subset of my branch is ok to merge.
|
| In git, a branch is the thing you either merge or don't.
| You merge multiple commits at once, or you don't. It's a
| great model. Breaking up the branch into smaller pieces,
| and giving people the impression it's ok to merge the
| first commit but not the rest, _just to unfuck the
| discussion UX_ , is putting the cart before the horse. I
| make a branch strictly _because_ I want it to either all
| merge or none of it merge. It's the only sensible
| approach in my book. If a discussion system is so bad
| that this is unworkable, it means the discussion system
| is bad, it doesn't mean the conceptual model of a merge
| is bad.
| steveklabnik wrote:
| > My point is that the LKML and what GitHub do is so
| different that the definition of "stacked diffs in
| general" can only describe a tiny aspect of each
|
| That's fine, what I mean is, when we started this convo,
| I thought you were asking about the general concept of
| stacked diffs, not the specifics of what GitHub is
| releasing here. That's my mistake for misunderstanding,
| sorry about that.
|
| This is also (assumedly, anyway) why they're calling this
| "stacked PRs" and not "stacked diffs," because what
| they're doing is slightly different than Gerrit,
| Phabricator, Critique, etc.
| ninkendo wrote:
| Thanks for indulging me so far, by the way, I really
| appreciate this discussion, it's very stimulating.
|
| After thinking about the whole thing I think I can
| summarize my opinion a lot better now:
|
| Stacked diffs are a category error. Units of discussion,
| and units of integration, should not be conflated.
|
| A branch is my unit of intended integration: merge all of
| it or none of it. The fact that reviewers need smaller
| slices to discuss does not imply those slices should
| become independently landable history objects. That's a
| UX concern for the review tool, not something I should
| have to encode into Git history.
|
| The ideal system would let me seed discussion however I
| want (by commit, by path, by subsystem, by semantic
| region of the diff, etc) without forcing me to pretend
| those are separate merge units.
|
| Github nails the "merge unit" (CI runs against the whole
| branch, the branch either merges or doesn't, etc), but
| absolutely fumbles in the discussion part. I hate that
| I'd have to change the merge unit just to fix their
| discussion UX.
| charcircuit wrote:
| >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.
| ninkendo wrote:
| 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.
| charcircuit wrote:
| Technically yes, but the work for A and B may not be done
| at the same time so you may want to get a head start on
| getting A reviewed while B is still being worked on.
|
| As a counter example. Why use multiple PRs when you can
| always just merge them into a single one. It's possible to
| make huge PRs with a bunch of different changes all
| included, but then the GitHub tools with managing stuff
| don't really work that well and you have to just do
| everything as comments instead of being about to actually
| accept a single accepted change for example.
| esafak wrote:
| 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.
| fmbb wrote:
| 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.
| mh2266 wrote:
| 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.
| ninkendo wrote:
| > 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.
| dontlikeyoueith wrote:
| Why do you insist on a different but functionally
| equivalent solution to the problem?
|
| It's weird.
|
| > Why do we tolerate the fact that GitHub doesn't let you
| say "approved for changes in `frontend/*`
|
| That's literally what stacked PRs are adding.
| nerdypepper wrote:
| 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/
| ninkendo wrote:
| > - 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.
| matharmin wrote:
| This is not just about the UI, it's about the mental model
| and management of the changes.
|
| Just covering the review process:
|
| Yes, you can structure your PR into 3 commits to be
| reviewed separately. I occasionally structure my PRs like
| this - it does help in some cases. But if those separate
| parts are large, you really want more structure around it
| than just a commit.
|
| For example, let's say you have parts A, B and C, with B
| depending on A, and C depending on B.
|
| 1. I may want to open a PR for A while still working on B.
| Someone may review A soon, in which case I can merge
| immediately. Or perhaps it will only be reviewed after I
| finished C, in which case I'll use a stacked PR. 2. The
| PR(s) may need follow up changes after initial review. By
| using stacked PRs instead of just separate commits, I can
| add more commits to the individual PRs. That makes it clear
| what parts those commits are relevant to, and makes it easy
| to re-review the individual parts with updated changes.
| Separate commits don't give you that.
|
| Stacked PRs is not a workflow I'd use often, but there are
| cases where it's a valuable tool.
|
| Then apart from the review process, there are lots of
| advantages to keeping changes small. Typically, the larger
| a change, the longer it lives in a separate branch. That
| gives more time for merge conflicts to build up. That gives
| more time for underlying assumptions to change. That makes
| it more difficult to keep a mental map of all the changes
| that will be merged.
|
| There are also advantages to deploying small changes at a
| time, that I won't go into here. But the parent's process
| of potentially merging and deploying the search index first
| makes a lot of sense. The extra overhead of managing the
| index while it's "unused" for a couple of days is not going
| to hurt you. It allows early testing of the index
| maintenance in production, seeing the performance overhead
| and other effects. If there's an issue, it's easy to revert
| without affecting users.
|
| The overall point is that as features become large, the
| entire lifecycle becomes easier to manage if you can split
| it into smaller parts. Sometimes the smaller parts may be
| user-visible, sometimes not. For features developed in a
| day or two, there's no need to split it further. But if it
| will span multiple weeks, in a project with many other
| developers working on, then splitting into smaller changes
| helps a lot.
|
| Stacked PRs is not some magical solution here, but it is
| one tool that helps manage this.
| ninkendo wrote:
| > But if those separate parts are large, you really want
| more structure around it than just a commit.
|
| Why? I reject the notion that large commits should be
| intrinsically hard to review.
|
| GitHub already has the concept of "code owners", which
| are people who have ownership/review responsibility over
| slices of the codebase, based on globs/pattern matching.
| But they don't implement the other half of that, which is
| that a reviewer should be able to see a _projection_ of a
| given PR, which matches the part of the repo they 're the
| owner of.
|
| There. That solves the entire problem of "this is too
| big, I can't look at all of it" (because your code
| ownership says this is the chunk of codebase you say you
| care about), and if that still isn't sufficient, there's
| a zillion UI features GitHub _could_ add that they simply
| don 't. Why can't I "focus" on a subset of the changes
| during review, in a way that helps me ignore unrelated
| discussions/changes? That is, even if I'm not code owner
| of the `frontend/` folder, why isn't there a UI
| affordance that says "let me focus on changes inside
| `frontend/` and ignore discussions/etc for the rest"?
|
| > By using stacked PRs instead of just separate commits,
| I can add more commits to the individual PRs
|
| Or you could just add commits to _the_ PR, and if GitHub
| got the damned UI right, it would be natural to see the
| "slice" you care about, for all the new commits. Having
| to rearrange commits into separate PR's and slice-and-
| dice followup changes to file them into the right PR
| unit, is (to me) a workaround for how shitty GitHub's
| review UX is. It really shouldn't be this way.
|
| > Then apart from the review process, there are lots of
| advantages to keeping changes small [...]
|
| I agree with you on most of these points, but the
| decision to land smaller changes earlier should be made
| based on things like "let's get early feedback behind a
| feature flag" or "let's see how this chunk behaves in
| production so we can validate assumptions", or "let's
| merge what we have now so to cut back on conflicts", etc.
| That's all fine. But I'm vehemently opposed to being
| _required_ to slice up my changes this way, _just to work
| around a terrible review UI._
|
| Personally, I review code in my development environment
| GitHub's UI is nonsensically terrible to read code. I
| could go on for hours about this[0], but when looking in
| my IDE I can drill into a subfolder and look at the diffs
| there. I can click and follow symbols. I can look at the
| individual diff history for any wildcarded subset of the
| repo, and see how the change was broken into commits. If
| I'm typing up some feedback to say "try doing it this way
| instead", I can actually try it myself first to make sure
| I'm not suggesting that someone do something that doesn't
| even compile.
|
| And GH's discussion UX is by _far_ the worst part of all
| of it. If you have a thread of discussions around a line
| of code, then wake up the next morning and want to see
| what new comments have been added? Good luck. Your best
| bet is to check your email inbox, because the comments
| are actually shown to you there. Using GitHub 's "inbox"
| feature? All that is is a link to PR's you have to look
| at, with no hints at "why" (it could be a CI run finished
| for all you know.) Good luck figuring out "why" a PR is
| on your list. Did someone @-mention you? Who knows. So,
| find the blue dot next to the PR, click it, and then
| figure out for yourself what changed since the last time
| you looked. No, you can't just scroll and find it because
| GitHub hides half the discussions by default. So you have
| to go and expand all the collapsed sections to hopefully
| find that conversation you were having yesterday. But oh,
| you can only find it in the diff tab. So you click that,
| but the relevant file is collapsed by default ("Large
| diffs are not rendered blah blah"), so then click that.
| _Then_ you may find that discussion.
|
| Contrast this to a mailing list. The discussions are...
| discussion threads. You pick up where you left off.
| People's comments are right there in your inbox, newest
| one on top (or whatever your preference is.) You get
| notified when there's a new message, and when you tap the
| notification, _it 's the actual message_, not some link
| to the PR that makes you click 6 more things to maybe
| find the message that just happened.
|
| [0] like how the first thing you have to do when opening
| up the changes tab is ctrl+f search for "Large diffs are
| not rendered by default" to find the actually-important
| diffs that are not shown to you because GitHub's backend
| can't scale to rendering large diffs without friction.
| Countless times I've been burned by approving a PR
| because I _don 't_ see it making a change to some
| functionality, only to find out it actually _did_ make
| said change, but GitHub just decided not to show me it.
| Seriously, the "large diffs" are the most important
| ones, and those are the ones _you don 't see without
| extra clicks._ The mind boggles.)
| whereistejas wrote:
| Each of your stacked PRs only has one commit. Do you have any
| examples with multiple commits per PR in a stack?
|
| PS: I love the concept of tangled. I currently use
| `sourcehut` but may soon move to tangled.
| whereistejas wrote:
| nevermind, I see what's happening in the UI. Each `jj`
| change is preserved in the UI and we can see multiple
| versions of the same change. The stack then is not really a
| stack of PRs but a stack of changes (where each change has
| its own history, i.e., the interdiff view). Did I get it
| mostly right?
| nerdypepper wrote:
| yes, thats right! when you submit a branch, you can
| choose to "stack" it, so the individual commits in the
| branch turn into separate PRs. these PRs evolve
| individually, can be merged individually, and be reviewed
| individually. you can also set different reviewers/labels
| for different PRs in the stack.
| ChrisArchitect wrote:
| 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)
| WhyNotHugo wrote:
| 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.
| pierrekin wrote:
| 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.
| philwelch wrote:
| Squashing is fine if you're just making a mess of temporary
| commits as you work and you don't want to keep any of those
| changes separate in master, but that's not a useful review
| workflow. A lot of times I've built a feature in a way that
| decomposed naturally into e.g. two commits: one to do a
| preparatory refactor (which might have a lot of noisy and
| repetitive changes, like changing a function signature) and
| another to actually change the behavior. You want those
| changes to be separate because it makes the changes easier to
| review; the reviewer quickly skims the first commit, observes
| that it's a mechanical refactor, and the change in behavior
| has its own, smaller commit without all the noise.
|
| "What if there's feedback and you need to make changes after
| the code review?" Then I do the same thing I did before I
| posted the code review: make separate "fixup" commits and do
| an interactive rebase to squash them into my commits. (And
| yes, I do validate that the intermediate commits build
| cleanly.)
|
| There's nothing you get from stacked PR's that you don't also
| get from saying "please review my feature branch commit by
| commit".
| pierrekin wrote:
| Yes what you're describing is literally the thing GitHub
| has built but instead of having to make a bunch of
| compromises, there is dedicated UI and product metaphor for
| it.
|
| Some examples of compromises:
|
| You can't merge partially merge a large "review commit by
| commit" PR so you are forced to wait until it is all ready
| to merge.
| WhyNotHugo wrote:
| Doesn't this mean that a first review might request that a
| specific change be reverted, and then a later reviewer
| reviews that reversion? That's essentially reviewing a noop,
| but understanding the it's a noop requires carefully checking
| all previous now-invalidated changes.
| pierrekin wrote:
| No, each PR is based on the previous one, so the reviewer
| only needs to consider the ideas that are new in each PR
| one at a time.
| jiveturkey wrote:
| 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.
| mh2266 wrote:
| 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.
| cush wrote:
| It's useful for large PRs in large repos with many
| contributors. It reduces the burden for reviewers.
| Gigachad wrote:
| 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.
| thamer wrote:
| > it invalidates so much work that comes after it.
|
| No, not necessarily.
|
| I work on a large repo and new features often involve
| changes to 3 different services: 2 from the backend, and
| the frontend UI. Sending a single PR with changes to all 3
| services is really not ideal: the total diff size in a
| feature I added recently was maybe 600+ lines, and the
| reviewers for frontend and backend changes are different
| people. The changes in the 2 backend services can be
| thought of as business logic on one side and interactions
| with external platforms on the other. The business logic
| can't work without integrating calls to external APIs, and
| the UI can't work without the business logic.
|
| These days I open 3 separate PRs and the software only
| works once all 3 are merged and built. It would be great to
| have all of them as a single package that's still testable
| and reviewable as 3 distinct parts. The UI reviewer can
| check out the whole stacked PR and see it running locally
| with a functional backend, something that's not possible
| without a lot of manual work when we have 3 PRs.
| pertymcpert wrote:
| The LLVM community used this model for years with
| Phabricator before it was EOL'd and moving to GH and PRs
| was forced. It's a proven model and works very well in
| complex code bases, multiple components and dependencies
| that can have very different reviewer groups. E.g: 1) A
| foundational change to the IR is the baseline commit 2)
| Then some tweaks on top to lay the groundwork for uses of
| that change 3) Some implementation of a new feature that
| uses the new IR change 4) A final change that flips the
| feature flag on to enable by default.
|
| Each of these changes are dependent on the last. Without
| stacked PRs you have o only one PR and reviewing this is
| huge. Maybe thousands of lines of complex code. Worse, some
| reviewers only need to see some parts of it and not the
| rest.
|
| Stacked diffs were a godsend and the LLVM community's
| number one complaint about moving to GitHub was losing this
| feature.
| gorgoiler wrote:
| 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.
| eqvinox wrote:
| > 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.)
| zzyzxd wrote:
| 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.
| devmor wrote:
| 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.
| alkonaut wrote:
| 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?
| steveklabnik wrote:
| > 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.
| herpdyderp wrote:
| 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.
| sroussey wrote:
| Yes! Maybe that feature will come next.
| sameenkarim wrote:
| 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?
| herpdyderp wrote:
| > so the diff in a PR shows just the relevant changes from
| the specific branch
|
| That's exactly right.
|
| > you can create stacked PRs purely via the UI
|
| How?
|
| I see from the docs https://github.github.com/gh-
| stack/introduction/overview:
|
| > When a pull request is part of a stack
|
| How does GitHub determine if a PR is part of a stack? Is it
| automatically detected so that I don't need to adjust my
| tooling that already creates chained PRs?
| pastel8739 wrote:
| 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.
| herpdyderp wrote:
| Agreed. I do have tooling for a rebase + push flow, but it
| simply calls native git commands.
| godzillafarts wrote:
| +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.
| the_gipsy wrote:
| Can we merge from GitHub UI without rewriting the commit already?
| thcipriani wrote:
| 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...>
| sameenkarim wrote:
| > 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.
| zeafoamrun wrote:
| 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.
| dminik wrote:
| 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?
| pierrekin wrote:
| 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.
| Gigachad wrote:
| 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?
| steveklabnik wrote:
| Yes, you can do that, but there's lots of things about it
| that are awkward. This makes it a more first-class feature.
| literallyroy wrote:
| Yeah that is a "stacked" pr. The tooling is nice especially
| when have a larger stack and make changes to the first
| branch. Update refs + push all branches, same with merging
| and rebasing onto main.
| philwelch wrote:
| You can review PR's commit by commit, and you can ask people
| to review PR's commit by commit. Not a big deal.
| pierrekin wrote:
| Then you lose the ability to merge the portion of work
| which has been agreed to, until the whole change overall
| has been agreed to.
| heldrida wrote:
| 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.
| mh2266 wrote:
| 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.
| fmbb wrote:
| > 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.
| altano wrote:
| 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.
| fmbb wrote:
| I am not arguing against small PRs.
|
| Stacking PRs are not a way to make changes smaller and
| therefore not making reviews easier.
| lopsotronic wrote:
| Interesting to see how this integrates with any release flows
| that use a lot of tags or feature flagging.
| throwatdem12311 wrote:
| 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.
| quibono wrote:
| 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.
| Pxtl wrote:
| This feels like a workaround for git's contradictory ergonomics.
| jwpapi wrote:
| 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
| jen20 wrote:
| 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.
| godzillafarts wrote:
| "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.
| j3g6t wrote:
| 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`
| mattstir wrote:
| I hope that GitHub continues copying Graphite's homework in
| terms of functionality, because from what I can see they don't
| have equivalents to `gt split`, absorb, and so on. Those can be
| very useful in the right contexts.
| silverwind wrote:
| This needs to be supported on `git` level first imho, not by a
| forge vendor.
| steveklabnik wrote:
| What would this being supported by git mean to you?
| prakashn27 wrote:
| Meta has something similar to this using mercurial. It was
| awesome.
| altano wrote:
| 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.
| steveklabnik wrote:
| 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
| latentdream wrote:
| the Website for the release statement look soooooo bad
| solaire_oa wrote:
| 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.
| meric_ wrote:
| 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
| mhh__ wrote:
| 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
| meric_ wrote:
| Huh, that's news to me. I had to do some googling and the
| only one I could find was by Phalicity which seems to be from
| a Meta employee who left and made it into a company (a la
| Statsig, or to some extent Graphite)
|
| Seems that company shutdown though a while ago and it got
| forked into Phorge, but either way I assume there's some
| divergence from what's internal at this point.
|
| Still I'll look into it, it does look neat and might suit my
| preferences still. Thanks for the headsup :)
| mhh__ wrote:
| 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 git
| _hub_
| mhh__ wrote:
| 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_
| ruined wrote:
| man, why is this waitlisted. this should have been a feature 10
| years ago
| sbinnee wrote:
| 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.
| zmmmmm wrote:
| 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?
| mattstir wrote:
| > If a rebase conflict occurs, the operation pauses and prints
| the conflicted files with line numbers. Resolve the conflicts,
| stage with git add, and continue with --continue. To undo the
| entire rebase, use --abort to restore all branches to their
| pre-rebase state.
|
| So it tries to replay commits in the stack and will stop
| halfway through that individual stack (layer?) to let you fix
| it if there's a conflict.
| conor_f wrote:
| 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!
| gpm wrote:
| 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.
| scaryclam wrote:
| I do this as well, but there is a workflow problem to solve
| and that is: getting PRs merged when they need to be to
| continue working.
|
| It's not a simple problem to solve, we can't all just jump
| because someone finished some work after all. But if the PRs
| are OK to rubber stamp, and merge, and they're safely behind
| a feature flag, then it could just be as simple as letting
| the submitter merge without the need for an extra review.
| That can of course be contentious, but then we can ask "why
| not?" and figure out what non-human gateways need to be added
| to help make it possible etc.
|
| I'm finding myself increasingly interested in understanding
| what friction can be removed from the software review, merge
| and release process, without sacrificing safe, well tested,
| understandable code that follows good standards.
| conor_f wrote:
| This is a perfect example that I've often seen in practice.
| There's nothing blocking in this workflow at all, and no
| reason these changes cannot be made in independent changes.
| e.g.
|
| 1) API implementation - Including tests and docs this should
| be perfectly acceptable to merge and review independently 2)
| UX implementation - Feature flagged, dummy API responses,
| easy to merge + review 3) One quick "glue" PR where the
| feature can be integration tested etc
|
| This prevents awful merge conflicts, multiple rounds of
| increasingly complex stacked reviews, and a host of other
| annoyances.
|
| Is there any reason that the stacked PR workflow is better
| that I'm ignoring or overlooking?
| gpm wrote:
| You're creating more work for no reason. There is
| absolutely no reason to make dummy API responses, a feature
| flag (well unless you want to delay public release of the
| features - domain dependent here), or a glue PR.
|
| Moreover you haven't even eliminated the dependency. The UI
| PR requires knowing that the dummy API responses you've
| created fit the right format - i.e. approval of the API PR
| up to small nits.
|
| Just test against the actual implementation from the start.
| Even without stacked PRs just leave the second as a draft
| with both sets of commits until the first is merged then
| rebase and make it.
|
| Stacked PRs are superior here because they eliminate that
| extra work of the draft PR and parallelize the review
| process slightly better.
| cadamsdotcom wrote:
| The vibecoded frontend makes the product look like a side
| project.
|
| Is it?
| steveklabnik wrote:
| 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.
| lpeancovschi wrote:
| this was released about 7 years ago, no?
| fphilipe wrote:
| 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-...
| locknitpicker wrote:
| > 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
| fphilipe wrote:
| 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.
| locknitpicker wrote:
| > The point is that I want to use Git, a tool and skill
| that is portable to other platforms.
|
| _You_ want to use git.
|
| Most people around you want to get things done.
| KptMarchewa wrote:
| That's like, most AI obsoleteable skill you could pick.
| philwelch wrote:
| Not at all. One of the tricks of using AI is context
| management and managing the Git history yourself can be a
| big part of that. If the AI has a stupid idea and
| implements it, even when you tell it, "that was a stupid
| idea, don't do that, change it back", the history could
| persist and the stupid idea will poison the context
| window every time an agent reads the commit history. It's
| even worse if _you_ had the stupid idea!
|
| Also, my current workflow actually has hooks to block
| agents from creating or changing commits. I know at some
| point this will be a limit to scaling, but I think that
| will result in me spending more rather than less time in
| git.
| pksunkara wrote:
| I think the CLI is useful for pushing. What do you use to push
| all the rebased child branches?
| fphilipe wrote:
| My git config for pushing is set to push.default=current. For
| rebased stacks I have an alias that does this:
| git --config push.default=matching push --force-with-lease
| --force-if-includes
|
| In other words, I force push all branches that have a
| matching upstream by changing my config on the fly.
| rs545837 wrote:
| 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.
| MASNeo wrote:
| Very curious about this as especially with more use of AI in the
| development process I have seen PR size has increased. So looking
| forward to general availability.
| boomlinde wrote:
| IME the github workflow promotes bad commit hygiene by making
| squashing or rebasing as-is an either-or choice in the web GUI.
|
| This will help some since you can more easily split PRs into
| units that make sense to squash at the end, but it still seems
| like not doing this on a per-commit basis is a disadvantage
| compared to Gerrit. With Gerrit I can use all the built-in Git
| rebase/squash/fixup tools to manage the commit stack and push
| everything in one go. I don't think there's a nearly as
| convenient a way to work with stacked branches in Git.
| baalimago wrote:
| Sounds like a merge-conflict nightmare
| srvaroa wrote:
| This feature is 10 years late
| AJRF wrote:
| I have never got a good answer to "can't you just make smaller
| PRs". This is convoluted tooling (needs its own CLI) for
| something you could achieve with just learning how git works.
| hambes wrote:
| I agree that a `gh stack` command is not needed, but this feels
| to me like just a better UI feature for a good git workflow. It
| literally is about making multiple smaller PRs that build on
| top of each other.
| scaryclam wrote:
| The question is, why are you not just merging them into main
| as you go? It's a bit of a smell when you "need" to merge
| branches into branches. It shows a lack of safety and ease in
| deployments, which is the real problem to solve IMO.
| dontlikeyoueith wrote:
| Because sometimes there are changes that need to land as
| all or nothing.
| gugagore wrote:
| If there is a stack of size n and you make a modification at
| the first change, closest to the trunk, is there a single git
| command you can run to rebase the other n-1 branches and ensure
| they remote branches are updated?
| Liskni_si wrote:
| Not a single one, but it can be done with 2.
|
| Assuming you're currently on the most recent branch (furthest
| from the trunk), `git rebase -i --update-refs trunk` will
| rebase all the intermediate branches. If you need to make a
| change to the first branch nearest the trunk, either use
| `edit` in the interactive rebase, or make a fixup commit and
| enable autosquash for the rebase. The `--update-refs` flag
| makes sure that all the intermediate branches get updated
| during the rebase.
|
| Then, to push them all, something like `git push origin
| 'refs/heads/yourname/*'` will push all branches prefixed with
| `yourname/`. It's a bit stupid that one can't just do `git
| push 'yourname/*'` though.
| poszlem wrote:
| Right, so 'just work the way the tool requires instead of
| making the tool work the way you want'. I would prefer the tool
| worked the way I want and the way I think of the changes
| instead.
| avita1 wrote:
| IMO this tool is basically allowing you to do that, it just
| takes care of the bookkeeping to associate the series of
| smaller PRs with eachother which is possible today but requires
| a lot of clicking.
| nonoesp wrote:
| First thing that came to mind was git butler.
| metafeather wrote:
| I've been using `git town`[1] for years to managed stacked PR's
| alone with Github PR's[2] and juniors I have introduced it to
| have really found it a helpful mental model when developing
| features.
|
| I hope the Gitub CLI will include syncing[3] 'stacks' locally
| with upstream in a similar way.
|
| [1]: https://www.git-town.com/stacked-changes.html
|
| [2]: https://github.com/marketplace/actions/git-town-github-
| actio...
|
| [3]: https://www.git-town.com/commands/sync.html
| netheril96 wrote:
| Does this work from a fork? That is, can I file a stacked PR to a
| project not owned by me, by creating branches in my forked
| project? Previously I asked AI about how to contribute stacked
| PR, it told me that I can only do it when I have push privileges
| to the repo, not from a fork, and the doc here is ambiguous.
|
| ----
|
| OK, I found this from official docs, so this feature is now quite
| useless to me:
|
| > Can stacks be created across forks?
|
| > No, Stacked PRs currently require all branches to be in the
| same repository. Cross-fork stacks are not supported.
| elAhmo wrote:
| Hah, first time seeing github.github.com
| vedant_awasthi wrote:
| This is useful. Managing dependencies across PRs has always been
| tricky -- nice to see better workflows evolving.
| vedant_awasthi wrote:
| Interesting direction. Curious how this compares with traditional
| transformer-based approaches in real use cases.
| sajithdilshan wrote:
| I already do this using git branched. Whenever I have features
| that depends on one another I create a feature branch and then
| stack branches on top of each other for individual feature so
| it's easy for the reviewer to go through the code and and also
| keep the PR size quite small. Also this gives me freedom to push
| more than one commit for each branch to fix things.
|
| The only annoying part is that I have to keep on merging the base
| branch to the feature branch constantly to keep it up-to date. If
| Github can provide a feature to do that automatically, then that
| would be perfect. Other than that, I don't see any advantage on
| this stacked PR approach they are proposing.
| jamesfisher wrote:
| Reminder that if your agent can create PRs then it can merge them
| too: https://github.com/orgs/community/discussions/182732
| balamatom wrote:
| Oh cool, soon people will stop being incapable of working with
| me.
| samsin wrote:
| 'Large pull requests are hard to review' is a good reason to keep
| PRs small, but small PRs also encourage continuous
| integration/delivery/deployment. Stacked PRs sound like they
| encourage long-lived feature branches instead.
| dontlikeyoueith wrote:
| Depends on what you consider long-lived.
|
| I typically generate stacks of 3-5 PRs in 1-2 days now (in a
| gen-AI world).
| nathas wrote:
| Meanwhile, you still can't do fast-forward merges in GitHub
| :clown: https://github.com/orgs/community/discussions/4618
|
| And it doesn't even rebase and merge correctly with fast-forward
| if there it's a clean set of commits!
| https://github.com/orgs/community/discussions/5524
| charles_f wrote:
| > :clown:
|
| We're neither on reddit nor on twitter, avoid that please.
| surajrmal wrote:
| Right. Just use directly.
| tele_ski wrote:
| It is a table stakes missing feature.. for a long time. Clown
| seems appropriate, every project I've worked on in github we
| end up copying a github yml action to allow us to do this
| with one comment '/fast-forward', just is silly at this point
| that github doesn't natively support it.
| sylware wrote:
| microsoft broke nearly all classic web support (noscript/basic
| (x)html).
|
| Since I can still login, is there a web API (using CURL and some
| identifying session token I could retrieve from my login) I could
| use to actually do "something". For instance, be involved in the
| issues of some project?
|
| (for me, that would be mostly valve stuff on linux based OSes)
|
| Any pointers?
| Liskni_si wrote:
| It's awesome that they're adding a UI for stacked branches1! The
| UX of the CLI tool seems weird, though. Why do I need to
| explicitly create and add branches to the stack if all I really
| want is to open PRs from my commits? Here's the workflow that I
| built for myself instead:
|
| [?] `git checkout -b feature-branch-xyz`
|
| [?] make a few commits, perhaps some fixups, rebase, whatever
|
| [?] start tig, look at the history, decide at which points I want
| to break the branch into stacked PRs, and mark those points using
| shift-s (which calls my own `git gh-stack branch create $commit`
| and creates a specially named branch there)
|
| [?] `git gh-stack sync` -- collects all the specially named
| branches, builds a graph of how they're stacked on one another,
| pushes them, opens stacked PRs
|
| GitHub has had some "support" for stacked PRs for a while, so
| merging the first one to main will automatically change the
| target branch of the second to main.
|
| If I need to change anything, I can just `git rebase
| --interactive --update-refs`, amend commits, split commits,
| rearrange commits, and then running `git gh-stack sync` will
| update the PRs for me. If I split a commit in the middle and
| shift-s to mark it, it will open an extra PR and restack
| everything to update the order.
|
| Furthermore, the "PR stack" doesn't actually need to be a stack
| (linear chain), it can be a tree. If I know that some commits are
| independent of the rest, I don't need to create a separate stack,
| I just create another local branch, mark PR-ready commits with
| shift-s, and `git gh-stack sync` will do the right thing. If I
| need to rebase the whole tree on top of current main, then `git
| rebase -i --rebase-merges --update-refs` does the job.
|
| I guess what I'm saying is that as someone who's been using git
| since its inception, it feels much more natural to just do
| everything in git, and then have a single command that pushes my
| work to GitHub. And I think this might work even better with
| jujutsu -- just point `git gh-stack sync` at the branches jj
| makes and it'll make a stack/tree of PRs out of them. :-)
|
| https://github.com/liskin/dotfiles/blob/home/bin/git-gh-stac...
| if anyone's curious. It's just a few hundred lines of code.
| Building the graph is done by `git log --simplify-by-decoration`.
| Opening PRs is shelled out to `gh pr create`.
|
| 1) I mean, I'd much rather they added a UI for reviewing PRs
| commit-by-commit, with the option to approve/request-changes on
| each, and the possibility to merge the first few approved ones
| while continuing work on the rest... But in a world of almost
| every $dayjob insisting on squash-merging, a UI for stacked PRs
| is a total game changer, positively.
| iknownthing wrote:
| That page looks vaguely AI generated
| choi0330 wrote:
| Wait. I built pilegit, Stacked PR solution.
|
| All in all, pilegit works with Github, Gitlab, Phabricator,
| Gitea, and custom.
|
| How about https://github.com/hokwangchoi/pilegit?
| boisterousness wrote:
| Stacked Git (StGit) [1] is a git CLI add-on for working with
| stacked commits in your local repo.
|
| Why are stacked commits useful? Multiple patches can be developed
| concurrently and efficiently, with each patch focused on a single
| concern, for a clean Git commit history and improved
| productivity. The tutorial [2] says:
|
| > One common use of StGit is to "polish" a Git branch before
| publishing it to another public repository. The kinds of polish
| that StGit can help with include: Complete and
| correct commit messages. Each patch limited to one
| coherent topic. Each patch standing on its own: passing
| tests, etc. Considerate patch (commit) order
|
| > Careful curation of Git commit history, as enabled by StGit,
| can be of high value to those reviewing pull requests or trying
| to understand why or how code came to be the way it is. ...
|
| > As a concrete example, consider a situation where several Git
| commits have been made in a repository with commit messages such
| as: "Improve the snarfle cache" "Remove
| debug printout" "New snarfle cache test" "Oops,
| spell function name correctly" "Fix documentation error"
| "More snarfle cache"
|
| > While the above may be the "true" history of commits to the
| repository, it may not be the history that is most helpful to
| code reviewers or the developer who needs to understand what
| happened in this area of the code six months after the fact.
| Using StGit, this history can be revised to be higher quality and
| higher value.
|
| Originally written in Python (2005, pre-GitHub) by Catalin
| Marinas, the current version is in Rust. StGit is free and open
| source [3]. It was inspired by Quilt [4], an earlier system
| credited to Andrew Morton and Andreas Grunbacher.
|
| [1] https://stacked-git.github.io/ [2] https://stacked-
| git.github.io/guides/tutorial/#development-b... [3]
| https://github.com/stacked-git/stgit/ [4]
| https://en.wikipedia.org/wiki/Quilt_(software)
___________________________________________________________________
(page generated 2026-04-14 23:01 UTC)