[HN Gopher] Stacked Diffs with git rebase --onto
       ___________________________________________________________________
        
       Stacked Diffs with git rebase --onto
        
       Author : flexdinesh
       Score  : 117 points
       Date   : 2025-12-01 04:47 UTC (4 days ago)
        
 (HTM) web link (dineshpandiyan.com)
 (TXT) w3m dump (dineshpandiyan.com)
        
       | hahahacorn wrote:
       | I consider myself a shmedium experienced dev who likes to learn
       | their tools and read source.
       | 
       | This seems like a house of cards whose juice isn't worth the
       | squeeze. But I would love to split up my PRs into smaller pieces.
       | 
       | I just would hate to waste time with an incomprehensibly goofed
       | git history because I forgot a command.
        
       | the_gipsy wrote:
       | I usually just `git rebase origin/main -i` after the base branch
       | has been merged there, and this means I need to explicitly drop
       | the merged commits, but I can inspect what's happening.
        
         | Xophmeister wrote:
         | Yeah, I do this too: The `--onto` solution feels a bit too
         | magical at times and an interactive rebase is pretty clear
         | about what's happening.
        
           | WorldMaker wrote:
           | Add `--update-refs` to your interactive rebase and it will
           | give you an easy line to know how many commits to drop
           | because it will add an `update-ref` line for the old branch.
           | You can just easily delete everything up to and including
           | that `update-ref` line and don't have to manually pull up a
           | git log of the other branch to remember which commits already
           | merged.
           | 
           | (Plus, of course, if you have multiple branches stacked,
           | `--update-refs` makes it easier to update all of them if you
           | start from the outermost branch.)
        
       | perspectivezoom wrote:
       | I'm a heavy user of git-spice: https://abhinav.github.io/git-
       | spice (created by a former coworker) and can't really go back to
       | a time without it. While still not nearly as good as Facebook's
       | Phabricator, it's probably the best workflow for small, focused
       | stacked PRs you can achieve in a Github / Gitlab based
       | repository.
        
       | jbjbjbjb wrote:
       | I think 'git rebase ---update-refs' is the better way to go for
       | this scenario
        
         | enbugger wrote:
         | Is there any good guide on how to solve the issue which OP
         | solves?
        
           | sirsuki wrote:
           | You don't really need docs as --update-refs does what the OP
           | does automatically instead of manually like the OP does.
        
             | ptx wrote:
             | How? I tried recreating the scenario from the article (the
             | section "First rebase -onto") and ran the first rebase with
             | "--update-refs":                 $ git checkout feature-1
             | $ git rebase --update-refs main       Successfully rebased
             | and updated refs/heads/feature-1.       Updated the
             | following refs with --update-refs:
             | refs/heads/feature-2-base
             | 
             | But all it did was update feature-2-base. It still left
             | feature-2 pointing to the old commits. So I guess it
             | automates "git branch -f feature-2-base feature-1" (step
             | 3), but it doesn't seem to automate "git rebase --onto
             | feature-1 feature-2-base feature-2" (step 2).
             | 
             | Presumably I'm doing something wrong?
        
               | happytoexplain wrote:
               | First, you don't need the extra "marker" commit. This
               | flag obviates the entire workflow.
               | 
               | Second, you run it on the outermost branch: feature 2. It
               | updates all refs in the chain.
        
               | mhw wrote:
               | Yeah, you need to rebase the tip of the feature branch
               | stack. git will then update all the refs that point to
               | ancestor commits that are moved. So in this case
               | $ git rebase --update-refs main feature-2
        
               | ptx wrote:
               | Thanks! Yup, that does the trick.
        
           | jbjbjbjb wrote:
           | I was reading this the other day when I came across this
           | feature because I'm stacking PRs recently which I don't
           | usually do
           | 
           | https://andrewlock.net/working-with-stacked-branches-in-
           | git-...
           | 
           | Another commenter posted this link which was a bit more
           | succinct
           | 
           | https://blog.hot-coffee.dev/en/blog/git_update_refs/
           | 
           | There isn't much to it though, you just go to the branch and
           | run git rebase with the update refs flag.
        
         | lelandfe wrote:
         | Sweet, looks like this is pretty new (2022).
         | 
         | Running a git command on one branch and multiple branches being
         | affected is really unusual for me! This really does look like
         | it is designed for just this problem, though. Simple overview:
         | https://blog.hot-coffee.dev/en/blog/git_update_refs/
        
         | YmiYugy wrote:
         | It breaks if you amend the top commit instead of adding a new
         | one.
        
       | dimitrieh wrote:
       | Jujutsu comes in handy here for the same usecase:
       | 
       | https://github.com/jj-vcs/jj
       | 
       | https://www.stavros.io/posts/switch-to-jujutsu-already-a-tut...
       | 
       | Also found https://github.com/gitbutlerapp/gitbutler
        
         | ndr wrote:
         | The main issue I kept having when trying to do this with just
         | git is then managing all the branch names to be attached to the
         | right moved commits, so that my stack could be reviewable on
         | github's open PRs.
         | 
         | Does jj help with that at all?
         | 
         | I've experimented a bit with git-town.com (OSS) and now
         | everyone at $DAYJOB uses graphite.com (SaaS) which does that
         | part very well.
        
           | baq wrote:
           | It's one of the core features that rebases, including branch
           | names (bookmarks in jj) work 'correctly'. You can rebase
           | whole dags, including merges, with multiple named heads with
           | just one jj rebase -b.
        
             | arccy wrote:
             | note that bookmarks don't float, unlike git branches, so if
             | your pattern is to produce a lot of commits, you'll want
             | something to keep your jj bookmarks pointing to the top of
             | your pile of commits.
             | 
             | this is less of a problem if you're more into the 1 change
             | == 1 commit workflow.
        
               | lima wrote:
               | There's an experimental-advance-branches feature which
               | helps with that!
        
               | pimeys wrote:
               | There's a very common alias `jj tug` for this case:
               | tug = ["bookmark", "move", "--from", "heads(::@- &
               | bookmarks())", "--to", "@-"]
               | 
               | It moves the nearest bookmark to the commit before the
               | current one (which should be your working commit).
        
               | stavros wrote:
               | Thanks, I replaced my Frankenstein's monster of a parsing
               | pipeline with this, very useful!
        
               | mhitza wrote:
               | This looks like any other git arcane incantation. If this
               | is a common pattern and jj aims to make things easier,
               | should probably be part of the core commands, no?
        
               | steveklabnik wrote:
               | It's something that makes a specific workflow easier, a
               | lot of folks that use jj don't necessarily use that
               | workflow.
               | 
               | That doesn't mean it couldn't be a core command someday,
               | but given that the alias works well for people, there's
               | not a ton of reason to make a whole new command. You
               | configure the alias and you're off to the races.
        
             | gcr wrote:
             | To expand: In jj, bookmarks point to "changes," not
             | commits. Rebases, history manipulations, etc. preserve
             | change ID, so this "just works."
        
           | WorldMaker wrote:
           | `--update-refs` flag helps a lot in vanilla git. That and
           | `--autosquash` should probably be default flags to `git
           | rebase`. I also don't entirely trust rebase without `-i`
           | (`--interactive`), personally. I hear there is talk about
           | shaking up the out-of-the-box default flags in git 3, and I
           | think rebase should especially get new defaults.
        
             | 1718627440 wrote:
             | I also use these flag when I have the need to, but I very
             | much don't want them to become the default.
        
         | crabmusket wrote:
         | Half my team switched to JJ this year, and I do find stacking
         | PRs to be much more pleasant now. We had previously tried out
         | Graphite but it didn't really stick.
         | 
         | I wrote up a little way to use JJ's revsets to make it easy to
         | push an entire stack of branches in one command:
         | 
         | https://crabmusket.net/2025/jj-bough-a-useful-alias-for-stac...
        
       | politelemon wrote:
       | This marker branch step feels like a workaround to a missing
       | capability. It's something I can easily see one forgetting
       | especially if they haven't been doing stacked diff workflows
       | regularly.
        
         | sublinear wrote:
         | I agree it seems error prone. I'm not sure if I'm
         | misunderstanding something, but I use `git cherry-pick` when I
         | know I need to move commits around that might have conflicts.
         | The problem with rebase can be that the user doesn't fully
         | understand all the options being applied and end up with a
         | "bad" merge.
         | 
         | I don't usually want to rewrite history. I just want the target
         | branch with all my commits on top (I usually squash the feature
         | branch into one commit anyway). I have yet to run into a
         | situation where this isn't good enough.
         | 
         | If the branch diverges so much and has so many commits that
         | this simpler approach doesn't work, that might not be a git
         | problem, but a project management one. It's still always nice
         | to know git has tools to get me out of a jam.
        
         | imron wrote:
         | The capability is there.
         | 
         | Just use git rebase --update-refs ...
        
           | url00 wrote:
           | Wow you aren't wrong, the first blog post on Google talking
           | about this is exactly what this complicated method does just
           | built-in.
        
       | nopurpose wrote:
       | That particular case can be solved much easier by rebasing outer-
       | most branch with `--update-refs` flag.
        
         | imron wrote:
         | Yep. I set this in .gitconfig
        
         | happytoexplain wrote:
         | I came into the comments specifically to ask if this flag
         | existed. I feel bad that the author developed this whole flow
         | just because they didn't know about this, but that's pretty
         | common with git.
        
           | fwip wrote:
           | I'm pretty sure the author was Claude, so don't feel too bad
           | for it.
        
         | duskdozer wrote:
         | I'm guilty lol. I wrote a helper to do rebase chains like this
        
           | nopurpose wrote:
           | update-refs works only in a narrow case when every branch
           | starts form the tip of a previous. Your helper might still be
           | useful if it properly "replants" whole tree keeping its
           | structure.
        
             | WorldMaker wrote:
             | Though at that point it may be easier to rewrite your
             | helper to manage rebase's interactive scripts.
        
         | onionisafruit wrote:
         | Thanks. This is going to be so useful, but it pains me to know
         | I could have been using --update-refs for the last three years.
         | 
         | I used to dutifully read release notes for every git release,
         | but stopped at some point. Apparently that point was more than
         | three years ago.
        
           | nopurpose wrote:
           | discoverability is a big problem, especially for CLI tools
           | which can't afford to show small hints or "what's new"
           | popups. I myself learned it from someone else, not docs.
        
             | onionisafruit wrote:
             | I plan to pay it forward today with a post on my work
             | slack. I just need to try it a time or two myself first.
        
             | 1718627440 wrote:
             | Except they do. You can type <tab>, search the man page or
             | read the release notes. They just don't force the user to.
        
         | ananthakumaran wrote:
         | Exactly, I was reading the blog and wondering the whole time
         | how it's better than --update-refs, which I have been using a
         | lot recently.
        
       | schacon wrote:
       | GitButler handles all of this pretty automatically, if you don't
       | want to deal with the Git gymnastics needed here.
       | 
       | https://blog.gitbutler.com/stacked-branches-with-gitbutler
        
       | motoboi wrote:
       | I believe the author would love stg: https://stacked-
       | git.github.io/guides/tutorial/#patches
        
       | nrhrjrjrjtntbt wrote:
       | Fun stuff, but I'll stick to trunk based dev, small PRs...
       | thanks!
        
         | taejavu wrote:
         | Stacking commits lets you do that without having to wait for
         | each change to be reviewed/merged to the main branch before you
         | iterate on top of those changes.
        
           | nrhrjrjrjtntbt wrote:
           | True. I find I rarely need it: standard rebase or merge do
           | the trick. If they don't the review cycle or PR size may be
           | too high. Super rare I need onto. So rare I look up how to do
           | it when I do.
        
           | flr03 wrote:
           | It's such a complicated way to work though, you start another
           | set of changes then you go back addressing comments then you
           | go back updating the stacked branch and you might need to do
           | that few times... Teams should focus on getting stuff merged
           | in and not create massive PRs that live forever, life becomes
           | so much easier.
        
       | sockbot wrote:
       | We use graphite at work to automate this workflow. The whole
       | point is avoid this toil.
        
       | ragebol wrote:
       | Ah, I've been doing this for ages but apparently this practice
       | has a name
        
       | dspillett wrote:
       | For the example given, would merging branch 2 into branch 1 then
       | branch 1 into main achieve the same effect?
       | 
       | Perhaps not an option if you need to release the work in branch 1
       | before the work in branch 2 is ready/reviewed/etc.
        
         | happytoexplain wrote:
         | The point of this technique is to keep them separate. See the
         | other comments about `--update-refs`.
        
       | happytoexplain wrote:
       | Even if `--update-refs` didn't exist, my experience is that git
       | can identify duplicate commits produced by rebase, and knows to
       | skip them when rebasing the same commits to the same place again.
       | Am I imagining that?
        
         | 1718627440 wrote:
         | It definitely fast-forwards unchanged commits.
        
         | adrianN wrote:
         | That works until you had to fix conflicts during the rebase and
         | the commits are no longer identical.
        
       | swaits wrote:
       | Every time I see one of these nifty git tricks or workarounds I
       | find myself wondering, "why not just use jj?"
       | 
       | You get a nicer, significantly simpler interface. You don't need
       | any tricks. You don't have to google how to work yourself out of
       | a bad state, ever. And you get near-perfect git compatibility (ie
       | you can use jj on a shared git repo, doing all the same things,
       | and your teammates won't know the difference).
       | 
       | I've wondered if there is a psychological thing here: someone who
       | spent time memorizing all the git nonsense may have some pride in
       | that (which is earned, certainly), that introduces some mental
       | friction in walking away???
        
         | YmiYugy wrote:
         | For me the answer is lazygit. I rarely use the git cli. I don't
         | want to learn the jj cli and the TUI wrappers for jj seem less
         | polished.
        
           | gcr wrote:
           | jjui is great, give it a try!
           | 
           | i went from being a "jj cli power user" to relying on jjui
           | for all of my complex rebase needs so quickly that i now have
           | to read the man page to recall basic commands
        
             | baq wrote:
             | can confirm jjui is super nice, except I've never been a jj
             | cli power user ;)
        
         | smcameron wrote:
         | For me, the answer is stgit. https://stacked-git.github.io/
        
           | max_k wrote:
           | Oh, there's another stgit user! ^5 Coming from darcs, I
           | couldn't use git until stgit came along, and today, it's one
           | of those few tools I can't imagine working without. Nothing
           | else matches my way of code hacking. So often, I watch people
           | making a big mess with git, and I always recommend stgit to
           | them, so they can post proper and reviewable branches for
           | merging. But in all these years, I could never convince
           | anybody.
        
           | michaelbuckbee wrote:
           | That's really neat.
        
         | Hendrikto wrote:
         | It's a bit like qwertz. Sure, it is not optimal, there are
         | better alternatives available. But it is good enough, and it is
         | universal. That trumps a 5% typing improvement on my own custom
         | keyboard layout at the cost of not being able to use my
         | coworkers keyboard.
         | 
         | Also, I dislike all of the alternate git frontends I tried,
         | because they are opinionated in a way they clash with my
         | workflow.
         | 
         | Moreover, I don't think the git CLI is that bad. Once you learn
         | some basic concepts, it makes a lot of sense and is pretty
         | consistent.
         | 
         | Most problems people report stem from a refusal to learn the
         | underlying structure and models. That is on them. And when
         | using a different frontend, they don't disappear either. They
         | are just abstracted, to allow you to avoid learning them. But
         | they are still there, and you will probably still need to know
         | them at some point.
        
           | quietbritishjim wrote:
           | > Most problems people report stem from a refusal to learn
           | the underlying structure and models.
           | 
           | It's very easy to fall into the trap of believing this: git's
           | implementation fits together neatly enough that it feels like
           | the best you could do. Like, yes it's complex, but surely
           | that's just intrinsic complexity of the problem? (Also, I
           | think we all sometimes feel like someone with a different
           | view must just not know as much as us.)
           | 
           | But if you have used other version control systems (I'm
           | thinking particularly Mercurial here) you realise that
           | actually some of that complexity is just totally made up by
           | git.
        
             | Hendrikto wrote:
             | > It's very easy to fall into the trap of believing this:
             | git's implementation fits together neatly enough that it
             | feels like the best you could do.
             | 
             | I explicitly said that git IS NOT the best we can do. But
             | it is universal and good enough. Not nearly as bad as some
             | people make it out to be.
        
         | hansvm wrote:
         | > introduces some mental friction in walking away???
         | 
         | I don't think it's just mental friction. Suppose you've learned
         | git well enough that everything you do in it is automatic and
         | fast, and the things which aren't fast by default you've built
         | aliases and tooling for over the years. Yes, starting from
         | ground zero you might want something like jj, but at the
         | current point in your life you're not starting from ground
         | zero. Switching to jj means learning another tool to achieve
         | similar outcomes on your workflows.
        
           | tcoff91 wrote:
           | But with jj there are better workflows that aren't really
           | doable with git.
        
             | jhhh wrote:
             | Last time I saw this claimed (maybe from steve's tutorial?)
             | it was just autosquash. Do you have another example?
        
               | tcoff91 wrote:
               | https://ofcr.se/jujutsu-merge-workflow
               | 
               | With `jjui` this strategy takes only a few keystrokes to
               | do operations like adding/removing parents from merge
               | commits.
               | 
               | It's so nice to have like 4 parallel PRs in flight and
               | then rebase all of them and all the other experimental
               | branches you have on top onto main in 1 command.
               | 
               | Also, I cannot even stress to you how much first-class-
               | conflicts is a game changer. Like seriously you do NOT
               | understand how much better it is to not have to resolve
               | conflicts immediately when rebasing and being able to
               | come back and resolve them whenever you want. It cannot
               | be overstated how much better this is than git.
               | 
               | Also, anonymous branches are SOOOO much better than git
               | stashes.
        
               | 1718627440 wrote:
               | > Also, anonymous branches are SOOOO much better than git
               | stashes.
               | 
               | You can do anonymous branches in Git as well. I use both
               | for different use cases.
        
               | tcoff91 wrote:
               | The UX around anonymous branches in git is not nearly as
               | good as jj though.
               | 
               | Also git has no equivalent to the operation log. `jj
               | undo` and `jj op restore` are so sweet.
        
           | steveklabnik wrote:
           | It really just depends. I was very comfortable with the git
           | cli. It didn't take long to learn jj's, and I'm faster with
           | it now than I ever was with git, simply because a lot of
           | things are easier to do and take less commands.
        
           | stouset wrote:
           | If jj took many weeks of relearning, I might be right there
           | with you. But the overwhelming majority of people I've
           | personally seen who try the switch convert within a day, are
           | barely slowed down by day two, and are effectively fluent
           | within three days to a week at most.
        
         | unshavedyak wrote:
         | Wish i could remember my issues with jj. I tried it, i wanted
         | to stick with it because i loved the fact that i could reorder
         | commits while deferring the actual conflicts.. but something
         | eventually prevented me from switching. Searching my slack
         | history where i talked about this with a coworker who actually
         | used jj:
         | 
         | 1. I had quite a bit of trouble figuring out a workflow for
         | branches. Since my companies unit of work is the branch, with
         | specifically named branches, my `jj ls` was confusing as hell.
         | 
         | `jj st` might have helped a bit, but there were scenarios where
         | creating an commit would abandon the branch... if i'm reading
         | my post history correctly. My coworker who was more familiar
         | explained my jj problems away with "definitely pre-release
         | software", so at the time neither of us were aware of a
         | workflow which considered branches more core.
         | 
         | Fwiw, I don't even remember when the jj workflow had branches
         | come into play.. but i was not happy with the UX around
         | branches.
         | 
         | 2. iirc i didn't like how it auto stashed/committed things. I
         | found random `dbg!` statements could slip in more easily and i
         | had to be on guard about what is committed, since everything
         | just auto pushed. My normal workflow has me purposefully
         | stashing chunks when i'm satisfied with them, and i use that as
         | the visual metric. That felt less solid with jj.
         | 
         | Please take this with a huge grain of salt, this is 10 month
         | old memory i scavenged from slack history. Plus as my coworker
         | was saying, jj was changing a lot.. so maybe my issues are less
         | relevant now? Or just flat out wrong, but nonetheless i bounced
         | off of jj despite wanting to stick with it.
        
           | oscillonoscope wrote:
           | I more or less use the method described
           | [here](https://steveklabnik.github.io/jujutsu-
           | tutorial/advanced/sim...) for branches. One thing I do change
           | is that I set the bookmark to an empty commit that serves as
           | the head of each branch. When I am satisfied with a commit on
           | head and want to move it to a branch I just `jj rebase -r @
           | -B branch`. When I want to create a new branch it's just `jj
           | new -A main -B head` and `jj bookmark set branch_name -r @`
        
           | steveklabnik wrote:
           | "creating a commit would abandon the branch" is certainly
           | something lost in translation. There are other reasons you
           | may have not liked the UX, largely that if you create
           | branches and then add a bunch of commits after it, the branch
           | head doesn't automatically move by default. There is a config
           | setting you can change if you prefer that, or the `jj tug`
           | alias some people set up.
           | 
           | Auto-commit is still a thing, but you can regain the stuff
           | you like with a workflow change, this is called the "squash
           | workflow" and is very popular:
           | https://steveklabnik.github.io/jujutsu-tutorial/real-
           | world-w...
        
         | dlisboa wrote:
         | There is also mental friction with learning an entirely new
         | tool. `jj` is different enough from `git` that one can't
         | transfer knowledge. Currently the documentation is not good
         | enough to assuage that issue.
        
           | stouset wrote:
           | This really isn't true. All of my git knowledge--except for
           | the CLI flags--is directly useful for jj.
           | 
           | The jj CLI is very easy to grok, even for a seasoned git
           | user. Maybe even especially so for a seasoned git user.
        
         | IshKebab wrote:
         | > why not just use jj
         | 
         | 1. It's very new; I haven't had time to learn it properly yet.
         | 
         | 2. It's very new and tooling doesn't support it well, e.g.
         | VSCode. There aren't many GUIs yet.
         | 
         | 3. I tried it once co-locating with Git and you definitely
         | can't use both at the same time, even if it can use a `.git`
         | directory. It ended up in a huge mess.
         | 
         | I'm definitely in favour of better-than-Git alternatives but I
         | don't think it's reasonable to expect everyone to switch to JJ
         | right now. It isn't _so_ much better that abandoning the de
         | facto standard is _obviously_ worth it yet. (In contrast to
         | things like the iPhone, Rust, SSDs, etc.).
         | 
         | Also I really wish they would focus on some of the bigger pain
         | points of Git. I can deal with rebasing and whatnot. Sometimes
         | it's painful but it's usually not that bad.
         | 
         | What I _can 't_ deal with are submodules and LFS. Both are
         | awful and fixing them properly requires fundamental changes to
         | the VCS which aren't going to happen in Git. JJ has an
         | opportunity to do that. Imagine if JJ could say "we have
         | submodules but they aren't awful!" or "you can check in large
         | files!". Those are the sort of huge advantages that would mean
         | you _can_ say  "why not just use JJ".
        
           | tcoff91 wrote:
           | There is a vscode jj gui extension
        
           | steveklabnik wrote:
           | Colocating is the default now, and you should be able to use
           | both at the same time, though running git commands that
           | mutate can confuse jj. Ideally you only use read-only git
           | commands.
           | 
           | Both submodules and LFS are things that jj wants to address,
           | but they take time.
        
         | bilalq wrote:
         | I have one and a half decades of muscle memory burned in with
         | inoremap jj <Esc>`^
         | 
         | It's not something I can just shift away from.
        
         | yegle wrote:
         | jj does not support git submodules, this precludes even a
         | casual use on my own personal repo.
        
           | steveklabnik wrote:
           | It just means that you use git commands to update your
           | submodules, jj still works for the rest of the repo just
           | fine.
        
         | jhhh wrote:
         | It's not a trick or workaround. It's a very straightforward use
         | of a command flag on one of the most used git commands. It's
         | even conceptually very simple, you're just rebasing a subset of
         | commits of a branch onto a different parent. Anyone who has
         | ever rebased already has a working mental model of this
         | operation. Framing this issue where knowing a single flag on a
         | command every git user uses every day to perform an operation
         | they already broadly understand as some arcane knowledge and
         | 'nonsense' is ridiculous.
        
         | mwcz wrote:
         | I'm one of the git users you describe who are resistant to jj.
         | jj sounds great, Steve Klabnik's endorsement is very
         | convincing, and I would probably love it, but here's the issue:
         | I've used git for 17 years and have internalized its
         | idiosyncracies sufficiently to practically never run into
         | problems, and help anyone on my team who does.
         | 
         | jj is harder to adopt for people with a thorough mental model
         | of git, because it's harder to accept jj commands at face
         | value. I know it's modifying my git tree, so I feel compelled
         | to grok exactly what it's doing, but that's distracting and
         | time consuming.
         | 
         | People like me should probably trial jj exclusively for two
         | weeks, to shed that antijjotic resistance and form a more
         | clear-headed opinion.
        
           | Valodim wrote:
           | I was in the same position. Then I tried jj. I knew within
           | the day that I wouldn't switch back.
        
           | steveklabnik wrote:
           | Glad you like that I like it!
           | 
           | What I will say is this: there is certainly an adjustment
           | period, and I also totally hear you about how learning
           | internals can be time consuming.
           | 
           | I think you can get a lot of the way there with at least the
           | core concept with something like this, if you'll indulge me:
           | 
           | With git, you build up stuff on your filesystem. You then
           | select which bits go into a diff in your index, and then when
           | you're done, you stamp out a commit.
           | 
           | With jj, you instead _start_ by creating a commit. In this
           | case, it 's empty. Then, every time you run a jj command, it
           | does a snapshot, and this produces a new commit. However,
           | because we want to have a stable identifier for a commit, we
           | introduce a new one: the change id. This is basically an
           | alias for the latest commit snapshot that was made. Your git
           | history is just made up of each of these latest commits for
           | each change.
           | 
           | ... does that make any sense? Obviously there's more to all
           | of the features than this, but that's sort of the core way
           | that the histories map to each other.
        
           | stouset wrote:
           | > jj is harder to adopt for people with a thorough mental
           | model of git
           | 
           | No, it really isn't. I have used git since shortly after it
           | was first released and I've written a git implementation.
           | 
           | I switched to jj in one day. And the amount of git arcana I
           | have to keep in working memory is now basically nil. My VCS
           | now works in almost a 1:1 mapping with how my brain wants to
           | interact with my repo rather than having to go through a
           | translation layer.
           | 
           | If you understand what git commands are doing, what jj does
           | is essentially trivial to add to your mental model.
           | 
           | I also get the benefit of being able to use workflows that I
           | always want to use in git but which are an enormous pain in
           | practice. _And_ I get access to wildly powerful new workflows
           | I didn't even consider because they would be outlandish in
           | git.
        
       | Fang_ wrote:
       | Is there any reason besides merge commits ending up in history to
       | not do this with merges instead? ie merge main into feature-1,
       | then feature-1 into feature-2.
       | 
       | Sounds like using --update-refs would let you do all that in a
       | single operation, but you still need to force-push and don't
       | maintain an explicit merge/conflict resolution history, both of
       | which could be considered sub-optimal for collaborative
       | scenarios.
        
         | happytoexplain wrote:
         | The use case is that they are not ready to merge yet.
        
       | bvdw wrote:
       | I've settled on a workflow that reverses the situation. I simply
       | commit all my work to the main branch and cherry pick commits
       | into temporary feature branches only when submitting PRs.
       | 
       | This way I only need to worry about maintaining a single
       | consistent lineage of commits. I've been using this workflow for
       | about a year now and find it to be much easier than juggling and
       | rebasing feature branches.
       | 
       | In case anyone's interested, I made a tool that automates this
       | workflow. The worfklow and tool are described here:
       | https://github.com/bjvanderweij/dflock/
        
         | motoboi wrote:
         | How many people on the team?
        
           | bvdw wrote:
           | I work in a small team, about five people.
        
         | JimDabell wrote:
         | You might like Jujutsu - you commit without being on any branch
         | and then later you can decide where and how you put things onto
         | branches.
        
           | bvdw wrote:
           | Interesting, I'll be sure to check it out. It sounds pretty
           | similar to the tool I built which lets you edit a "plan" in a
           | text editor to assign commits to feature branches - the plan
           | is saved so it can be amended continuously.
        
       | mytailorisrich wrote:
       | Instead of "stacked diffs", isn't the more "continuous
       | integration" solution to split a big feature into small chunks
       | that actually get merged?
       | 
       | Having to rebase again and again is a symptom that a dev branch
       | is living for too long.
        
         | glenjamin wrote:
         | I'm amazed that this comment is so low down
         | 
         | Stacked diffs seems like a solution to managing high WIP - but
         | the best solution to high WIP is _always_ to lower WIP
         | 
         | Absolutely everything gets easier when you lower your work in
         | progress.
        
           | happytoexplain wrote:
           | This seems idealistic. It's very normal to be working on a
           | feature that depends on a not-yet-merged feature.
        
             | glenjamin wrote:
             | I invite you to look into feature flagging.
             | 
             | It is entirely viable to never have more than 1 or 2 open
             | pull requests on any particular code repository, and to use
             | continuous delivery practices to keep deploying small
             | changes to production 1 at a time.
             | 
             | That's exactly how I've worked for the past decade or so.
        
             | SideburnsOfDoom wrote:
             | > It's very normal to be working on a feature that depends
             | on a not-yet-merged feature.
             | 
             | Oh sure, many bad ideas and poor practises such as that one
             | are quite "normal". It's not a recommendation.
        
         | happytoexplain wrote:
         | This problem isn't specific to cases where you are rebasing
         | "again and again". Just needing to do a single rebase (e.g.
         | prior to opening a PR) for a stacked feature is enough to need
         | a solution.
         | 
         | And, as others have pointed out, the modern solution is
         | `--update-refs`, so there's no need for complicated workflows
         | any more anyway.
         | 
         | If you mean rebasing as each PR in the stack is merged, most
         | Git platforms have the ability to do that automatically.
        
           | mytailorisrich wrote:
           | No, I mean small chunks that are actually merged instead of
           | having a stack of them floating around.
           | 
           | A rebase before a merge can always happen, of course. But
           | there are not stacked commits then it is just a standard
           | rebase of your small chunk and that's it. And this will also
           | have a shorter life than a stack so rebases will be rarer and
           | simpler.
        
             | happytoexplain wrote:
             | Oh, yes - but this is more about your situation than your
             | style. Sometimes a feature is large and varied enough to
             | beg multiple PRs, yet singular enough that you are not
             | developing them serially (i.e. as you work on later parts,
             | you are changing earlier parts). Most of the time this
             | isn't the case.
        
               | mytailorisrich wrote:
               | My experience is that you should always aim for frequent
               | small commits. This is what continuous integration and
               | trunk-based development advocate and it saves a lot of
               | headaches and the need to come up with "exotic" solutions
               | to problems created by big, long-lived dev branches.
               | 
               | This is quite orthogonal to developing parts serially or
               | not, and it is perfectly fine to change or refactor
               | ealier parts when you work on later parts. Development is
               | an iterative process.
               | 
               | It seems to me that "stacked diffs" are an example of
               | looking for a technical solution to process issue.
        
       | andrebianchessi wrote:
       | Stacked commits is awesome, but it sucks that with git you need
       | all these workarounds, and that the servers (GitHub etc) are not
       | designed with that workflow in mind.
       | 
       | I left Google a few months back to work on another project. I
       | missed the internal version control so much that I dropped that
       | other project and am now focused http://twigg.vc It's very
       | similar to what's used at Meta and Google. Atomic commits, linear
       | history, auto rebase, code review compatible with stacked
       | commits.
        
       | davidfstr wrote:
       | I've been meaning to write this article for a long time
       | @flexdinesh . Thanks for taking the time to share this technique
       | for managing stacked diffs using vanilla git rebase!
        
       | IshKebab wrote:
       | Eh I just use `git rebase -i` and delete the commits I don't
       | want. Much easier to think about.
       | 
       | But the real problem with this workflow is that neither Github
       | nor Gitlab support it at all. Not even Forgejo does. Which blows
       | my mind because is such an obvious way to work.
       | 
       | As far as I know only Tangled actually supports it, but
       | unfortunately Tangled is tangled up in its own weird federation
       | ideas. There's no way to privately host it at all.
        
         | taejavu wrote:
         | I'm not sure what you mean, I stack PRs all the time with
         | GitHub - select the earlier branch as the merge target instead
         | of the main branch.
        
       | ptx wrote:
       | What I would really like is a Git equivalent to Mercurial's
       | "fold" operation. I usually make a bunch of commits as I work,
       | just as checkpoints, which I then want to turn into a single
       | final commit when it's done, which could be quite some time
       | later, e.g. "started on thing", "broke it", "broke it more", "Add
       | thing to improve foo of bar".
       | 
       | Mercurial's "histedit" command offers two operations to combine
       | the commits: "fold" and "roll", where "fold" brings the earlier
       | commit into the later commit and "roll" does the reverse. The
       | "fold" operation does exactly what I want in this case: It brings
       | all the changes into the final commit from when I actually
       | finished it, using the commit date of that commit.
       | 
       | Git's "rebase -i" command offers "squash" and "fixup" and "fixup
       | -c" and "fixup -C", but they all do the same thing as "roll",
       | i.e. they keep the earliest commit's date, the date when I
       | started working on the thing and not the date when I finished
       | working on it. (So I then cut and paste the correct date and
       | update the commit afterwards. This may not be the best way to do
       | it.)
        
         | WorldMaker wrote:
         | That is an interesting desire to use a later commit date rather
         | than an earlier one. So many prefer that commit date of when an
         | effort started.
         | 
         | One way to accomplish that is to reorder the commmits in
         | `rebase -i`: "pick" the last commit first and squash/fixup the
         | rest after. That can produce some very weird merge conflicts,
         | but it works well more often than you might think, too.
         | 
         | At the very least, you can do your hand editing of the commits
         | during the rebase instead of after by switching the earliest
         | commit from "pick" to "edit" to have the full power to amend
         | the commit before it moves on (with `git rebase --continue`
         | when you are satisfied). (Versus "reword" if you just want to
         | change the commit message only.)
         | 
         | Also, instead of naming commits things like "broke it" and
         | "broke it more" an option is to use `git commit
         | --fixup={previous commit hash}`. That auto-generates a "fixup!"
         | name based on the previous commit and auto-marks it as a fixup
         | if you use the `--autosquash` flag to rebase.
        
           | ptx wrote:
           | I do use fixup commits as well, for fixing small issues
           | discovered after I make the proper commit, and in that case
           | it makes sense to use the date and message of the earlier
           | commit.
           | 
           | But in the case I'm describing, the earlier commits are
           | essentially just temporary snapshots on the way to making a
           | proper commit. Just a more explicit undo history, basically.
           | I usually don't even bother naming them anything as
           | meaningful as "broke it", actually. Maybe most people
           | wouldn't even bother making these commits - or maybe they
           | would if Git supported "fold".
        
             | WorldMaker wrote:
             | My pattern in a case like that is often to start a commit
             | named "WIP thing I'm doing" and `commit --amend` each
             | snapshot, updating the commit message as it starts to come
             | together. `commit --amend` is nicer/gentler than `rebase`,
             | most of the time.
             | 
             | Though there are also times I don't mind working in a very
             | dirty worktree and `git add -p` (interactive add that also
             | makes it easy to stage only parts of files) pieces only
             | once they feel complete and start to tell a story. (And in
             | those cases I may use a lot of `git stash -u` and `git
             | stash pop` snapshots, too, especially if I need to switch
             | branches in that worktree.)
        
               | ptx wrote:
               | Hmm, I may have actually solved my problem. I think what
               | I mostly want to do is this, if I'm working on the
               | "feature-1" branch based on "main":                 $ git
               | checkout feature-1       $ git reset main       $ git
               | commit -a -C feature-1@{1}
               | 
               | This squashes all the changes and keeps the date and and
               | message of the final commit on the branch.
               | 
               | Weirdly, although the "-c" and "-C" flags for the fixup
               | operations sound like they should correspond to the same
               | flags for "git commit", which grabs the date along with
               | the message from the specified commit, the fixup flags
               | only affect the message and not the date.
               | 
               | It would be nice if it worked the same for rebase as for
               | commit. Then "fixup -C" would essentially correspond to
               | Mercurial's "fold".
        
               | WorldMaker wrote:
               | Ah, yeah, that use of `git reset --soft` is how many
               | places do squash merging, so that makes sense. You may
               | want to make sure to include the `--soft` flag explicitly
               | just as a precaution.
               | 
               | Also yeah, I would expect the fixup -c and -C flags to be
               | more aligned with commit in a rebase.
        
               | 1718627440 wrote:
               | git fixup doesn't take the old commit message either, so
               | I would be not so surprised that it doesn't take the
               | commit message. It is really for preparing to do rebase
               | fixup to the older commit.
        
         | 1718627440 wrote:
         | You could use "git reset --soft oldest-commit" and then "git
         | commit -C newest-commit".
        
         | steveklabnik wrote:
         | If you ever try out jj, both fold and roll exist as `jj
         | squash`. You'd choose as the destination the change of the time
         | you'd want to keep.
        
       | foobarian wrote:
       | This made good sense, though by the time I got to bits saying
       | "that last step is critical. Without it, your next sync will
       | break" and "Don't forget!" I was laughing out loud. I love git
       | :-)
        
       ___________________________________________________________________
       (page generated 2025-12-05 23:01 UTC)