TIL: git history fixup replaces the autosquash dance
Git 2.55 shipped an experimental subcommand, git history fixup <commit>, that takes whatever you have staged and folds it into an earlier commit, then replays everything after it. One command. It replaces the git commit --fixup plus git rebase --autosquash two-step I have been typing on autopilot for about a decade. TIL, and I ran it to check it does what the release notes claim.
The setup pain first: Ubuntu 24.04’s apt ships git 2.43, and even the git-core PPA is only on 2.54 as I write this. git history landed in 2.54 but the fixup subcommand is new in 2.55, so I built it from source. One wrinkle worth knowing: 2.55 includes Rust components by default, so either have cargo on the box or build with make NO_RUST=1.
What does git history fixup actually do?
You know the moment. You’re polishing a branch, you spot a change in your working tree that belongs three commits back, and you sigh because now it’s fixup-commit-then-autosquash time. The new way: stage the change, name the commit it belongs to, done.
Real repo, three commits, and the root commit is a pancake recipe missing its maple syrup (borrowed from the release post’s example, because it’s a good one):
$ git log --oneline
1c27b69 (HEAD -> main) add serving size
607de8b add method
85873a0 add pancake recipe
$ printf -- '- maple syrup\n' >> pancakes.md
$ git add pancakes.md
$ git history fixup 85873a0
That’s it. No editor, no todo list, no --autosquash. The log after:
$ git log --oneline
3a459f9 (HEAD -> main) add serving size
0e42f1e add method
3f4de5e add pancake recipe
$ git show 3f4de5e:pancakes.md
# pancakes
- 100g flour
- 2 eggs
- 300ml milk
- maple syrup
The syrup is in the root commit as if it had always been there. Message and authorship stay as they were (pass --reedit-message if you want the editor). Every descendant hash changes, obviously, because that’s how content addressing works. This is history rewriting, same rules as always: fine on your branch, rude on shared ones.
There’s a --dry-run that prints the ref update it would make without touching anything, which I’ll be using out of habit:
$ git history fixup 85873a0 --dry-run
update refs/heads/main 3a459f9668... 1c27b69d29...
And the reflog records it as its own operation, so git reset HEAD@{1} gets you back if you regret it:
$ git reflog -2
3a459f9 HEAD@{0}: fixup: updating 85873a0
1c27b69 HEAD@{1}: commit: add serving size
What happens when the fixup would conflict?
It refuses to start. I staged a change to a line that a later commit had also touched:
$ git history fixup HEAD~1
error: fixup would produce conflicts; aborting
Exit code 255, branch untouched, staged changes still staged. This is the design choice I like most. git rebase --autosquash will happily drop you into a half-finished rebase with conflict markers and a detached HEAD, and now your quick fixup is a ten-minute cleanup. git history fixup is all-or-nothing. If it can’t do the thing cleanly, it does nothing. For the 95% case (typo fixes, forgotten files, review feedback that belongs in an earlier commit of the series) that trade is exactly right, and for the other 5% the old rebase dance still exists.
Two more guardrails I hit while poking at it: it errors with nothing to fixup: no staged changes on a clean index rather than guessing at your working tree, and it needs a working tree at all, so no bare repos.
git history in 2.55 also has reword (edit an earlier commit’s message without a rebase) and split (break a commit up by pathspec). Same shape: name the intent, not the mechanism. The git history docs mark the whole command experimental, so flags may shift before it stabilises. I’m using it anyway. It’s been ten years since I set up my default branch rename muscle memory; this one is going in next to it.
FAQ
What version of Git has git history fixup?
Git 2.55.0, released in late June 2026. The parent git history command arrived experimental in 2.54, and 2.55 added the fixup subcommand alongside reword and split. Most distro packages lag: Ubuntu 24.04 apt has 2.43 and the git-core PPA had 2.54 when I checked, so you may need to build from source (with cargo available, or make NO_RUST=1).
Is git history fixup the same as git commit –fixup?
No. git commit --fixup=<commit> creates a marker commit you still have to squash later with git rebase --autosquash. git history fixup <commit> applies your staged changes to the target commit directly and replays the descendants in one step, with no interactive rebase, no editor, and no intermediate commit to clean up.
Is it safe to use an experimental Git command?
Reasonably, for this one. It aborts before touching anything if the result would conflict, records the operation in the reflog so you can undo with git reset HEAD@{1}, and refuses to run without staged changes. The experimental label means the interface may change between releases, not that it eats data. Keep it off shared branches, same as any history rewrite.
Does git history fixup work with unstaged changes?
No. It reads from the index only, and errors with nothing to fixup: no staged changes if you haven’t staged anything. That’s a feature: you choose exactly what gets folded into the earlier commit with git add -p, and anything unstaged stays in your working tree untouched.