Git history is the safer rewrite tool Git needed
Experimental fixup, reword, and split already deliver stack hygiene without leaving core Git.
Parallel feature work in Git has a familiar tax. You stack commits across a few local branches, notice a typo or a bad message three commits down, and suddenly you're staring at rebase -i, hoping nothing conflicts and that every branch tip still points where you expect. That friction is a big reason people keep trying Jujutsu (jj). The quieter development is that Git itself now ships an experimental answer: git history.
It landed across two releases, reword and split in 2.54, fixup in 2.55, and then mostly vanished from conversation. That is a miss. For developers who live in stacked local branches, these three subcommands already capture several of the day-to-day wins people chase when they evaluate jj, while staying inside the tool everyone already has. The design choice that matters most is not the feature list. It is the refusal to start an operation that might leave the tree half-broken.
Three commands, one invariant
git history is not a prettier git log. It is a small rewrite suite with a shared contract: rewrite a commit, then rebuild everything that depends on it, and move every local branch tip that contained the old commit so the graph stays consistent.
fixup is the workhorse. Stage the correction as usual with git add, then run:
git history fixup <commit>
Those staged changes fold into the target commit. Downstream commits are recreated on top with new hashes, and every local branch that descended from the old commit is updated to follow. That last part goes further than git rebase --update-refs, which only moves refs sitting inside the range you are actively rebasing. git history fixup finds the full set of local descendants (with an option to limit the rewrite to the current branch if you prefer a smaller blast radius).
reword does the same graph surgery for messages only:
git history reword <commit>
Your editor opens on the existing message. Save, and the stack rebuilds. Because only the message changes, reword never touches the index or working tree. You can fix a commit on a branch you do not have checked out without disturbing whatever is in flight.
split is the specialized tool. Point it at a commit that bundled two unrelated changes:
git history split <commit>
You get the familiar hunk-by-hunk picker. Kept hunks become the first commit; the rest become the second. Downstream work is rebuilt on the pair. No more detaching, soft-resetting, and restaging just to unpick a commit that should have been two.
In all three cases the rewrite is atomic. If the operation could produce a conflict, it simply does not start.
Atomic by design, not by hope
Interactive rebase will cheerfully begin a rewrite and strand you mid-conflict on a Friday night because you wanted a one-line message fix. git history takes the opposite stance: only start when completion is guaranteed. That is a real ergonomic shift, not a slogan. History rewrites stop being stateful recovery exercises and become something closer to pure graph transforms.
The trade-off is explicit. Merge commits are off limits. If your workflow is merge-heavy, this is a dealbreaker today. The docs also leave the door open: the no-conflict rule exists because rewrites are not meant to be stateful, and it can be lifted if Git ever gains first-class conflicts. Until then, the tool is intentionally less powerful than jj, which can carry conflicted state through a rebase and let you resolve later.
Under the hood it builds on the git replay machinery, which is good news for anyone who scripts Git. The interface is still experimental and may change, but the philosophy (refuse impossible intermediate states) is the part worth keeping even if names shift.
Where it sits next to jj and classic rebase
jj still wins on several axes that git history is not trying to cover: an operation log with easy undo, a working copy modeled as a commit, and conflict-carrying rebases. If those are table stakes for how you think about version control, keep evaluating jj.
What git history does deliver, without a workflow switch, is safer local stack maintenance:
| Need | Classic path | git history |
|---|---|---|
| Fix a bug in an older commit | commit --fixup + autosquash rebase |
history fixup (and branch tips follow) |
| Fix a message mid-stack | rebase -i reword |
history reword (no working-tree churn) |
| Unbundle a mixed commit | soft reset + add -p gymnastics |
history split |
| Multiple local branches on the stack | Manual tip updates or limited --update-refs |
Automatic rewrite of descendant local branches |
| Conflict risk | Start, then maybe fail | Refuse upfront |
That middle ground is exactly where a lot of professional Git use lives: long-lived local stacks, occasional message cleanup, and the constant small fixes that appear once a design settles. You do not need a full VCS migration to stop treating those as high-stakes operations.
Using it in actual day-to-day work
Adoption is low friction because it is already in core Git. No extra install. The practical pattern looks like this.
You are on feat-2, with a stack that also feeds feat-1. Commit B introduced a wrong constant. You fix the file, stage it, and run git history fixup B. Both branch tips move. You keep working.
Later the design name changes and B's message is now wrong. You are mid-edit on something else. git history reword B opens the editor, rewrites the graph, and leaves your index and working tree alone. That property alone removes a whole class of "I rebased the wrong branch while dirty" mistakes.
When a commit grew too large (logging plus the real feature), git history split is the clean escape hatch. Use it before the commit ships and gets shared. After push, the usual rewrite rules still apply: coordinate, force-with-lease, and prefer not to rewrite published history others have built on.
Caveats to keep in muscle memory:
- Merge commits block the tool. Linear or lightly rebased local stacks are the sweet spot.
- It rewrites local branches. Remotes are still your responsibility.
- Experimental means the CLI can change. Treat it as production-useful for local hygiene, not as a frozen public API yet.
- If a rewrite would conflict, you fall back to classic rebase (or jj) and resolve the hard way. The refusal is a feature until first-class conflicts land.
For code archaeology and debugging, the win is indirect but real. Cleaner local history means git log, git blame, and bisect stay legible. You spend less time reconstructing "what did I mean three commits ago" because fixing intent became cheap enough to do when you noticed the problem.
The gap that remains, and why to use it anyway
git history does not close the full distance to jj. There is no first-class operation log, no working-copy-as-commit model, and no conflict-carrying rebase. If your pain is primarily undo and conflicted stack evolution, the mental model shift jj asks for may still be worth it.
For the large set of developers who like Git, dislike brittle interactive rebases, and juggle a few local feature branches, this is already a material upgrade. It is in the distribution. It is atomic. It updates the branches you actually care about. And the documentation's openness about first-class conflicts suggests the project knows where the ceiling is.
Try the three commands on a throwaway stack this week. The best Git improvements are the ones that make the dangerous operation feel boring. git history is pointed squarely at that outcome.
Sources & further reading
- The Git history command deserves more attention — lalitm.com
- Git - Viewing the Commit History — git-scm.com
- git history: the best thing in Git 2.54 · cekrem.github.io — cekrem.github.io
- History or log of commands executed in Git - Stack Overflow — stackoverflow.com
- Understand Git history - Azure DevOps | Microsoft Learn — learn.microsoft.com
Mariana covers the fast-moving world of machine learning and generative AI, with a particular focus on how these technologies are reshaping development workflows. When she isn't stress-testing the latest foundation models, she's usually at a local hackathon.
Discussion 0
No comments yet
Be the first to weigh in.