Rendered at 23:44:30 GMT+0000 (Coordinated Universal Time) with Cloudflare Workers.
opello 6 hours ago [-]
It seems strangely polarizing to refer to the "old and cumbersome" interactive rebase approach in contrast to "the right approach" using `git history` which is a brand new, experimental feature as of Git 2.54 from April 20, 2026, a few months ago.
I have split a lot of work with interactive rebasing. I'm excited to try `git history split` and to have learned about it here. But the stylistic flourish makes it seem like it's been a feature for years that people just haven't moved to instead of being something shiny and new to be excited about trying.
locknitpicker 5 hours ago [-]
I agree, interactive rebasing existed for ages and git history is mainly a user experience improvement over it. This way of framing git history goes way past engagement tricks and erodes the blogger's credibility.
schacon 4 hours ago [-]
Not sure if this is written by an AI, but I'll engage, because it's a bit misleading.
The new `git history` command can take three verbs - fixup, reword and split. While interactive rebasing can do fixup and rewording relatively easily and the `history` variant is a slight improvement, the split sub-subcommand is really _quite_ difficult to do in other Git tooling, including interactive rebase. Before reading on, I would challenge the reader to think about how they might do it.
The answer is that you would rebase back to at least the commit you're splitting, choose 'edit' in the rebase script for that commit (pick the rest), then when the rebase stops there, `git add (-p)` the parts you want, commit, then commit the rest, then `git rebase --continue`. I would guess that maybe 5% of the readers of this paragraph would have guessed that correctly, if I'm being pretty positive.
Patrick wrote the `git history` stuff because tools like JJ and GitButler are pushing UX that makes this type of thing very easy and Git itself is struggling to catch up. Even at a fundamental level, the first versions of history were based on the sequencer code that rebase used but it couldn't do the job properly (messing with the index/workdir), so he rewrote it based on the `git replay` machinery instead (which _itself_ is still somewhat experimental).
Interestingly, it's _still_ not optimized for agents or scripting - `split` specifically needs an interactive terminal like `git add -p` does, so not many agents are good at it. I recently tried it and Claude piped `y\ny\nn` through the command, guessing at the interactive input (y, y, n) needed to stage the hunks.
The point is, interactive rebasing is a horrible solution to this problem, `git history` clearly better, at least for splitting, but really we need non-interactive solutions to problems like this so agents can do them well.
GitButler does this well because we focus specifically on it - our status command gives hunks/files ids and hunk/file movement can happen between commits so splitting is creating an empty commit and then moving the parts over - but it's surprising that nobody else builds tooling for this increasingly common use case.
I think `git history` will slowly get better at this over time, but "interactive rebasing" is a _much_ more error prone and unintuitive way to accomplish this.
skydhash 4 hours ago [-]
> The point is, interactive rebasing is a horrible solution to this problem, `git history` clearly better, at least for splitting, but really we need non-interactive solutions to problems like this so agents can do them well
If it’s for scripting why not use the lower level ‘git update-index’. Start from the original file and a patch filed, edit the patch, apply it, and then add it the modified file to the index. The patch is the container of changes in my opinion, better have the agent act on that and then apply it instead of working interactively with the work tree.
schacon 4 hours ago [-]
Well, there are a couple of problems here, if you are interested in an actual answer.
The first is that update-index only works on worktree files, so you need to be actually modifying file contents on disk and then essentially running `hash-object` on them, then `commit-tree`, etc. For an agent, each of these are tool calls and ones that they're not very good at (because nobody really does this manually).
Quick example for clarification. Let's say you want to move half of the changes of a file from one commit to another.
The ideal way would be to run something like:
`git squash <commit-a>:<hunk-id> <commit-b>`
Which could load the tree of commit-a into memory, virtually apply the hunk change to it, calculate the new tree, write it out and rebase the commits - thus moving the hunk. This is what tools like GitButler do with `but squash` etc. One command, pretty simple, all in memory and extremely fast.
The "plumbing" path you suggest would be something like:
- (record patch changes you want)
- git reset HEAD~3
- (apply patches you want in commit 1)
- git update-index
- git commit
- (apply patches you want in commit 2)
- git update-index
- git commit
- (recreate the commits above that)
You talk about not interacting with the work tree, but `update-index` directly deals with the working directory. It's just not built for operations like this. It's built for Linus interactively building trees from contents on a filesystem.
skydhash 3 hours ago [-]
I'm saying this because, git store whole files for each commit, not hunks or patches. Scripting it out without doing any interactions would be to have discrete steps which can be rollback. So it's better to have the changes (the patch) separate from what needs to be changed (the file). So the agents need only to edit the patch in three separate version (c1, c2, c3), then loop with the apply->update-index->commit action, each easily verifiable and revertable.
jez 4 hours ago [-]
This is a relatively recent feature. I was first introduced to it in GitHub's release notes for Git 2.54:
> P.S. The stack overflow question for splitting commits discusses the old and cumbersome approach. The 20th answer discusses the right approach but has a meagre 2 upvotes as of today, compared to 2656 for the older top answer with the cumbersome approach.
It now has 5 upvotes. A long way to go to 2656 but at least it's going in the right direction.
hotelsacher 8 hours ago [-]
Still, it shows how incredibly broken the StackOverflow system is. It's completely normal for older answers to become outdated, but they still act like the old answers are laws of nature.
BeetleB 8 hours ago [-]
I think this is harsh criticism. It's because not many people use SO any more.
One of SO's selling points was that outdated answers get replaced with the more modern, better ones - and it worked for many, many years.
hungryhobbit 7 hours ago [-]
... until they decided it was a good idea to train all power users to grief new users off the site.
da_chicken 6 hours ago [-]
It was always like that.
It's due in part to the number of people that kept posting screenshots of their code in their question, and the number of people that couldn't search to save their life. The noise ratio on incoming questions was always ridiculous, and the number of people who don't know how to ask a technical question is staggering. I do wish they had started to ask a series of questions to people for their first 5 or 10 posts to force them to ask questions in a useful way that was conducive to getting answers. The whole site never got over being hostile to new users.
SO had a number of fundamental issues. If you found a duplicate and flagged it as such, you couldn't explain why you thought it was a dupe in the flag. This meant that "this is a different question but the answer is the same" was just tagged a dupe the same way "this is literally the identical question" was.
I stopped participating entirely after a single event. When I was moderating the moderation, and the site decided to give me a trick question to "test if I was paying attention". Yeah, no. I don't have time for that bullshit. Apparently I'm still a top 1% user, though, and I think I've gained another 8,000 or 9,000 points since I left and stopped answering at anything.
jatora 6 hours ago [-]
The rise of AI and the death of SO is a beautiful beautiful thing. That community is the worst, and this opinion seems to be shared universally by anyone who had extensive interaction with it.
locknitpicker 5 hours ago [-]
> One of SO's selling points was that outdated answers get replaced with the more modern (...)
I don't think this is description is right. What you describe as "outdated answers" are actually answers to apply up to specific releases. Just because a project launches a release that rolls out a new feature that doesn't render the old answers wrong or stale.
schacon 4 hours ago [-]
Not only this, but it's not super simple to update to the newest version anymore. I actually tried this today and I'm on `2.50.1 (Apple Git-155)`. It turns out I don't actually know how to update that. `brew install git` does not overwrite the system binary. I'm not the smartest person in the world, but if I had issues getting to the latest version, I assume a very small percentage of users actually has a Git version that will run `git history`.
It was introduced in 2.54, which was in April of this year, so less than 4 months ago. Almost nobody can run this command unless they're _really_ on top of things.
layer8 7 hours ago [-]
At least the answer is there. When asking ChatGPT (Plus) the same question, it only suggests the old ways.
locknitpicker 5 hours ago [-]
> At least the answer is there. When asking ChatGPT (Plus) the same question, it only suggests the old ways.
The old ways are still the right answer for multiple reasons, such as they still clearly work with the latest release and odds are you and everyone around you is not running the latest and greatest version of git installed.
brewmarche 6 hours ago [-]
These answers are ~15 years apart, so the vote difference is not surprising.
I personally sort by ‘date modified (newest first)’ on StackExchange sites.
no-name-here 7 hours ago [-]
It’s now the 6th answer instead of the 20th, and if answers are sorted by Trending (which I recommend people do) in the dropdown, it’s currently 3rd.
diath 8 hours ago [-]
That's good to know, I'd usually go about it in a roundabout way: soft reset the commit then git add --patch to stage the hunks to split it into multiple commits.
WorldMaker 5 hours ago [-]
It's not that roundabout compared to what `git history split` is doing. It's sort of the missing tool in a lot of the rebase discussions (in the Stack Overflow answers). Basically `git history split` is most useful for "split an older commit in this branch", so it's a higher level complex dance of, essentially:
- `git rebase -i`
- Change the TODO list to `edit` the chosen commit (everything else to `pick`)
That "simplified" `git add -p` in the middle and that assumption that everything else not selected is the "other patch" is fine for quick splits, but there's still power user super powers in knowing the full rebase workflow and `git add -p`.
opello 4 hours ago [-]
This is nicely documented but seems to be missing the `git reset HEAD^` when rebase drops you into editing the marked commit.
adregan 6 hours ago [-]
I think you'll find your workflow is still required. `git history split` is really only focused on turning a commit into 2 commits, so if you wanted multiple, you would need to run the command multiple times.
Add to that, it's patch functionality isn't as robust as `git add --patch`. For example, you cannot edit a hunk, so if you intended to tease out atomic changes, you won't be able to.
qsera 7 hours ago [-]
I do the same, but use `git gui` to select the changes.
ericfrederich 3 hours ago [-]
Yeah, I've been using git for nearly 20 years. I still rely on `git gui`. It's my crutch. I know you can do all of that in a terminal, but staging different hunks (or individual lines) is super easy.
merb 7 hours ago [-]
Which gui? I’m on Mac and somehow I could never really be happy with gui‘s for git. Gitkraken is the last I tried and I found it too much cluttering. Paid choices weren’t that great aswell either (yet)
In case you're curious if _you_ can run `git history split`, the answer is almost certainly no, unless you manually installed the newest release of Git within the last 3 months.
`git history` was introduced as an experimental command less than 4 months ago, which means that there are essentially zero OS releases that packages a version that contains it by default. Also, if you're on OSX and run `brew install git`, it will not overwrite the Apple version that is too old to have it.
Git 2.54 was the first release with this subcommand and it is not in Xcode CLT, Debian testing, Ubuntu 26.04 - nothing. It will be "normal" in a year, but it's ridiculous to criticize SO for "old" methods of splitting long before anyone ships with it.
gnoack 3 hours ago [-]
Point taken, that's true. I'm admittedly developing on distributions where the updates are faster, so I missed that. But then again, I also didn't expect that this short weblog article would have such a large audience when I wrote it. :)
nemetroid 3 hours ago [-]
It's in Fedora, Arch, and many other popular operating systems.
mateusz834 6 hours ago [-]
or use jj and stop worrying about git
jj split --interactive {ref}
cleaning 2 hours ago [-]
This. Can't imagine going back to git after getting comfortable with jj, it's excellent.
BeetleB 6 hours ago [-]
You don't need --interactive. It is interactive by default.
pixl97 6 hours ago [-]
Ah yes, I'll just pass that over to the compliance team and wait a few months for a denial.
nomel 1 hours ago [-]
jj uses git as a backend, even though it's not a git frontend. You can use it in a "colocated" mode, with an existing git clone, alongside your normal git tools. Either way, nobody will know that you're using jj except you.
BeetleB 6 hours ago [-]
You need compliance to install jj?
bywater 5 hours ago [-]
The SO upvote problem is real. Once an answer hits a few hundred votes, nobody scrolls past it to check if something better came along.
meling 4 hours ago [-]
I’m doing rebasing a lot these days, especially since I started using gh stack. The main struggle I have with rebasing is for git to recognize that a branch has actually been merged when its commits have changed (e.g., if I forgot to delete the local branch with old commits and come back months later and trying to figure out if it was actually merged or not). My understanding is that this is nicer with jj when you work locally, but if you sync with GitHub, I think your still faced with the same problem, or?
What are people doing to workaround this? I’ve tried to ask LLMs, but the complexity is frankly a bit off putting (I don’t have the suggestions handy)
skydhash 4 hours ago [-]
Don’t you have an id system, like an issue tracker? That’s your source of truth for change requests.
The common advice is to never have long standing branches for WIP. But if you do, squash it to have a small number of commits, then replay it on top of the default branch. The try to understand why there’s any diff or conflict (refactor, update to the design, code removal, was not merged at all,…). As I squash merge, I always remove the PR branch from the main repo. And I keep it for about a week on my local repo (In case the PR is reverted and I needed my drafting version of it).
nulltrace 47 minutes ago [-]
[dead]
matheusmoreira 8 hours ago [-]
Today I learned about git history split. Thanks!!
furkanturan 3 days ago [-]
I never thought about whether this was possible, but now that I know it is, I immediately see how useful it could be for my workflow.
jaltekruse 8 hours ago [-]
I had a team that insisted on a linear history and was constantly rebasing things. This workflow strongly favors "just squash it all together" before a rebase to avoid re-resolving the same conflicts. This workflow often produces big patches that reviewers end up asking to be broken back up. This experience and a recommendation from my manager at the time made a good git GUI like sourcetree a daily driver for me. It's just second nature to review my own changes every time I stage files and quickly do a mix of staging whole files or "hunk-based", even allowing clicking to select specific lines within a hunk intuitively, rather than using the terminal UI and asking git to take a guess at splitting a hunk. I find a good git GUI invaluable, I have used sourcetree for years and love it. Unfortunately sourcetree isn't available for Linux, but I recently found SourceGit which seems to be a decent substitute.
Highly recommend trying a good GUI out, I think you would likely have similar ah-ha moments about workflow optimizations, and honestly I just cannot understand how people get by in git without a convenient way to visualize the commit tree (and yeah I know there is a decent command line treeview, but the context switching all the time in the terminal to go from viewing the tree or even the simple log to exit out and ask for commit contents just is so much more fussy and tedious).
farlight 8 hours ago [-]
git rerere will resolve repeated conflicts for you automatically, you just need to enable it.
Be careful, it will also cache any mistakes in merging including things like bug regressions. Especially if you also have a git history full of cherry-picking and reverts.
nullsanity 7 hours ago [-]
[dead]
sunshowers 6 hours ago [-]
BTW not sure how you feel about LLMs but merge conflicts are not really an issue with them any more.
StackOverflow is really not great at handling questions for which the right answer changes over time. I guess it doesn't matter since SO is dead anyway: https://news.ycombinator.com/item?id=46482345
mohamedkoubaa 3 hours ago [-]
I think I'd rather keep my old school interactive rebase skills sharp for when I actually need it
BeetleB 8 hours ago [-]
Is this yet another case of git improving its UI due to jujutsu?
(This is exactly how jj does it.)
If you like this kind of convenient CLI command, I would seriously recommend giving jj a try. To give you an idea, I never bothered splitting a commit in my git days. I do it almost daily with jj. Lots of other niceties that I didn't bother with in git, but routinely do in jj.
jj has fewer commands than git, yet does everything git does.
jgtrosh 2 hours ago [-]
Also, `jj split` can use alternative tooling to the built-in TUI, whereas I don't think `git history split` can.
rkangel 5 hours ago [-]
Yes this is how jj does it, except that jj is more powerful because it does automatic rebase of all the downstream commits.
If you have any descendants of the gir history split (i.e. it's not the most recent) you'll have to do some more work (a rebase) to get back to a useful history. I think you can do this in the middle of an interactive rebase which is ok, but still not as easy.
steveklabnik 6 hours ago [-]
My understanding is that `git history` was started after seeing jj, yes. I don't have a citation for you though, I could be wrong, that's just what I remember.
svyatoslavpavl 8 hours ago [-]
The bit that made this click for me: it works on any commit in history, not just HEAD. Mark the commit as edit in git rebase -i, then git reset HEAD^ to uncommit it while keeping the changes in the working tree, and rebuild the pieces from there.
git add -p is the real workhorse for the rebuild: s splits a hunk into smaller ones, and e lets you hand-edit the hunk when the boundary doesn't fall on clean line breaks. Stage a coherent slice, git commit, repeat until the tree is empty, then git rebase --continue. The rebase's only job is to drop you at the right spot; add -p does the actual splitting.
bonzini 6 hours ago [-]
Or the opposite direction, with repeated "git checkout -p ..." and commit steps. Slightly less comfortable because you have to figure out (such as from "git rebase --edit-todo") the later commit, but it makes testing the pieces easier.
wasting_time 6 hours ago [-]
Please don't use LLM to write HN comments. To quote the guidelines[0], "HN is for conversation between humans".
I have split a lot of work with interactive rebasing. I'm excited to try `git history split` and to have learned about it here. But the stylistic flourish makes it seem like it's been a feature for years that people just haven't moved to instead of being something shiny and new to be excited about trying.
The new `git history` command can take three verbs - fixup, reword and split. While interactive rebasing can do fixup and rewording relatively easily and the `history` variant is a slight improvement, the split sub-subcommand is really _quite_ difficult to do in other Git tooling, including interactive rebase. Before reading on, I would challenge the reader to think about how they might do it.
The answer is that you would rebase back to at least the commit you're splitting, choose 'edit' in the rebase script for that commit (pick the rest), then when the rebase stops there, `git add (-p)` the parts you want, commit, then commit the rest, then `git rebase --continue`. I would guess that maybe 5% of the readers of this paragraph would have guessed that correctly, if I'm being pretty positive.
Patrick wrote the `git history` stuff because tools like JJ and GitButler are pushing UX that makes this type of thing very easy and Git itself is struggling to catch up. Even at a fundamental level, the first versions of history were based on the sequencer code that rebase used but it couldn't do the job properly (messing with the index/workdir), so he rewrote it based on the `git replay` machinery instead (which _itself_ is still somewhat experimental).
Interestingly, it's _still_ not optimized for agents or scripting - `split` specifically needs an interactive terminal like `git add -p` does, so not many agents are good at it. I recently tried it and Claude piped `y\ny\nn` through the command, guessing at the interactive input (y, y, n) needed to stage the hunks.
The point is, interactive rebasing is a horrible solution to this problem, `git history` clearly better, at least for splitting, but really we need non-interactive solutions to problems like this so agents can do them well.
GitButler does this well because we focus specifically on it - our status command gives hunks/files ids and hunk/file movement can happen between commits so splitting is creating an empty commit and then moving the parts over - but it's surprising that nobody else builds tooling for this increasingly common use case.
I think `git history` will slowly get better at this over time, but "interactive rebasing" is a _much_ more error prone and unintuitive way to accomplish this.
If it’s for scripting why not use the lower level ‘git update-index’. Start from the original file and a patch filed, edit the patch, apply it, and then add it the modified file to the index. The patch is the container of changes in my opinion, better have the agent act on that and then apply it instead of working interactively with the work tree.
The first is that update-index only works on worktree files, so you need to be actually modifying file contents on disk and then essentially running `hash-object` on them, then `commit-tree`, etc. For an agent, each of these are tool calls and ones that they're not very good at (because nobody really does this manually).
Quick example for clarification. Let's say you want to move half of the changes of a file from one commit to another.
The ideal way would be to run something like:
`git squash <commit-a>:<hunk-id> <commit-b>`
Which could load the tree of commit-a into memory, virtually apply the hunk change to it, calculate the new tree, write it out and rebase the commits - thus moving the hunk. This is what tools like GitButler do with `but squash` etc. One command, pretty simple, all in memory and extremely fast.
The "plumbing" path you suggest would be something like:
- (record patch changes you want) - git reset HEAD~3 - (apply patches you want in commit 1) - git update-index - git commit - (apply patches you want in commit 2) - git update-index - git commit - (recreate the commits above that)
You talk about not interacting with the work tree, but `update-index` directly deals with the working directory. It's just not built for operations like this. It's built for Linus interactively building trees from contents on a filesystem.
https://github.blog/open-source/git/highlights-from-git-2-54...
And discussion at the time:
https://news.ycombinator.com/item?id=47837698
It now has 5 upvotes. A long way to go to 2656 but at least it's going in the right direction.
One of SO's selling points was that outdated answers get replaced with the more modern, better ones - and it worked for many, many years.
It's due in part to the number of people that kept posting screenshots of their code in their question, and the number of people that couldn't search to save their life. The noise ratio on incoming questions was always ridiculous, and the number of people who don't know how to ask a technical question is staggering. I do wish they had started to ask a series of questions to people for their first 5 or 10 posts to force them to ask questions in a useful way that was conducive to getting answers. The whole site never got over being hostile to new users.
SO had a number of fundamental issues. If you found a duplicate and flagged it as such, you couldn't explain why you thought it was a dupe in the flag. This meant that "this is a different question but the answer is the same" was just tagged a dupe the same way "this is literally the identical question" was.
I stopped participating entirely after a single event. When I was moderating the moderation, and the site decided to give me a trick question to "test if I was paying attention". Yeah, no. I don't have time for that bullshit. Apparently I'm still a top 1% user, though, and I think I've gained another 8,000 or 9,000 points since I left and stopped answering at anything.
I don't think this is description is right. What you describe as "outdated answers" are actually answers to apply up to specific releases. Just because a project launches a release that rolls out a new feature that doesn't render the old answers wrong or stale.
It was introduced in 2.54, which was in April of this year, so less than 4 months ago. Almost nobody can run this command unless they're _really_ on top of things.
The old ways are still the right answer for multiple reasons, such as they still clearly work with the latest release and odds are you and everyone around you is not running the latest and greatest version of git installed.
I personally sort by ‘date modified (newest first)’ on StackExchange sites.
- `git rebase -i`
- Change the TODO list to `edit` the chosen commit (everything else to `pick`)
- At the `edit` point:
- `git rebase --continue`That "simplified" `git add -p` in the middle and that assumption that everything else not selected is the "other patch" is fine for quick splits, but there's still power user super powers in knowing the full rebase workflow and `git add -p`.
Add to that, it's patch functionality isn't as robust as `git add --patch`. For example, you cannot edit a hunk, so if you intended to tease out atomic changes, you won't be able to.
I use this only for the aforementioned purpose.
`git history` was introduced as an experimental command less than 4 months ago, which means that there are essentially zero OS releases that packages a version that contains it by default. Also, if you're on OSX and run `brew install git`, it will not overwrite the Apple version that is too old to have it.
Git 2.54 was the first release with this subcommand and it is not in Xcode CLT, Debian testing, Ubuntu 26.04 - nothing. It will be "normal" in a year, but it's ridiculous to criticize SO for "old" methods of splitting long before anyone ships with it.
jj split --interactive {ref}
What are people doing to workaround this? I’ve tried to ask LLMs, but the complexity is frankly a bit off putting (I don’t have the suggestions handy)
The common advice is to never have long standing branches for WIP. But if you do, squash it to have a small number of commits, then replay it on top of the default branch. The try to understand why there’s any diff or conflict (refactor, update to the design, code removal, was not merged at all,…). As I squash merge, I always remove the PR branch from the main repo. And I keep it for about a week on my local repo (In case the PR is reverted and I needed my drafting version of it).
Highly recommend trying a good GUI out, I think you would likely have similar ah-ha moments about workflow optimizations, and honestly I just cannot understand how people get by in git without a convenient way to visualize the commit tree (and yeah I know there is a decent command line treeview, but the context switching all the time in the terminal to go from viewing the tree or even the simple log to exit out and ask for commit contents just is so much more fussy and tedious).
https://git-scm.com/docs/git-rerere
(This is exactly how jj does it.)
If you like this kind of convenient CLI command, I would seriously recommend giving jj a try. To give you an idea, I never bothered splitting a commit in my git days. I do it almost daily with jj. Lots of other niceties that I didn't bother with in git, but routinely do in jj.
jj has fewer commands than git, yet does everything git does.
If you have any descendants of the gir history split (i.e. it's not the most recent) you'll have to do some more work (a rebase) to get back to a useful history. I think you can do this in the middle of an interactive rebase which is ok, but still not as easy.
git add -p is the real workhorse for the rebuild: s splits a hunk into smaller ones, and e lets you hand-edit the hunk when the boundary doesn't fall on clean line breaks. Stage a coherent slice, git commit, repeat until the tree is empty, then git rebase --continue. The rebase's only job is to drop you at the right spot; add -p does the actual splitting.
[0] https://news.ycombinator.com/newsguidelines.html