Git history command: fixup, reword and split simplify history rewriting
Git history command: fixup, reword and split simplify history rewriting
TL;DR – What git history does and why it matters
git history (available since Git 2.54) adds three atomic subcommands—fixup, reword and split—that let you modify an old commit’s contents, message, or split it into two, while automatically rebasing all local branches that descend from that commit. The commands never leave the repository in a half‑broken state, making history rewriting as safe as git rebase --abort but without the manual choreography.
Overview of the three subcommands
Each subcommand follows the same workflow: stage changes (if needed), invoke the command with the target commit SHA, and Git rewrites the commit graph, creating new hashes for the edited commit and every descendant. The rewrite is performed atomically; if a conflict would arise, the operation aborts.
git history fixup <commit>
Purpose: Amend an old commit’s tree (i.e., its file changes) and propagate the fix to every branch that contains the commit.
How it works:
- Stage the corrective changes with
git add. - Run
git history fixup <commit>. - Git folds the staged changes into the target commit, creates a new commit hash, and rewrites every descendant commit (e.g.,
C → C*,D → D*). - All local branches that descend from the original commit are moved to point at the new tip.
Key property: Unlike git rebase --update-refs, which only updates refs inside the rebased range, git history fixup rewrites every local branch descended from the target (unless --current-branch is used). This eliminates the need to run separate rebases for each branch.
Limitation – The command does not work on histories that contain merge commits. If your workflow relies on merges, you must fall back to traditional rebase or
jj‑style tools.
git history reword <commit>
Purpose: Change only the commit message of an old commit while leaving its tree untouched.
How it works:
- Run
git history reword <commit>. - Your editor opens with the existing message; edit and save.
- Git creates a new commit with the updated message, updates all descendants, and moves branches accordingly.
Because the index and working tree are not touched, you can reword a commit on a branch you are not currently checked out, avoiding any disruption to ongoing work.
Community note – Users appreciate the simplicity compared to
git rebase -i HEAD~nwhere you replacepickwithreword. The command makes the intent explicit and reduces the chance of accidental reordering.
git history split <commit> [--pathspec]
Purpose: Divide a single commit into two commits, interactively selecting which hunks belong to each.
How it works:
- Run
git history split <commit>. - Git presents a hunk‑by‑hunk prompt (similar to
git add -p). - Choose the hunks that should stay in the first commit; the rest become the second commit.
- Git rewrites the commit graph, inserting the two new commits (
B1,B2) and updating all descendants.
Use case: When a commit unintentionally bundles unrelated changes, split cleanly separates them without the manual choreography of git rebase -i and git reset.
Practical tip –
splitis especially handy for teaching junior developers how to break large PRs into smaller, reviewable units.
Why git history is a game‑changer compared to traditional interactive rebase
- Atomic safety – The commands refuse to start if a conflict would be introduced, guaranteeing that the repository never ends up in a half‑rebased state.
- Branch‑wide rewrites – All local branches that contain the target commit are updated automatically, eliminating the need for repetitive
git rebase --update-refscalls. - No need for extra tooling – Because
git historyships with core Git, you can try it out without installing third‑party tools likejjorstgit. - Focused UI – Each subcommand does one thing (fix content, edit a message, split a commit), reducing the mental overhead of remembering the myriad
git rebase -icommands.
Counterpoint –
jjstill offers features thatgit historylacks, such as first‑class conflict handling, an operation log for easy undo, and a model where the working copy is itself a commit. These capabilities may be added to Git in the future, as the documentation hints at lifting the current limitation once Git supports stateful conflict objects.
Community reactions and practical insights
- Safety concerns – Some commenters argue that
git rebase --abortalready mitigates the “scary” aspect of interactive rebase. While true,git historyremoves the need for a manual abort step by refusing to start when a conflict is possible. - Signing commits – A user reported that
git history reworddrops GPG signatures, forcing them back to manual rebase for signed commits. This is a current limitation; signatures are not automatically re‑applied. - Integration with tools – Questions remain about support in GUIs like Magit or Lazygit. As of now, those front‑ends do not expose
git historycommands directly, but the underlying functionality can be invoked from a terminal. - Branch management philosophies – One commenter prefers keeping explicit versioned branches (e.g.,
myfeature.1,myfeature.2) and does not want automatic rewrites across all branches.git historyoffers a--current-branchflag to limit the rewrite scope, accommodating such workflows. - Learning curve – Users new to Git often find the plethora of rebase options overwhelming. The three dedicated commands provide a clearer, discoverable entry point for common history‑editing tasks.
Practical workflow examples
Fixing a buggy commit on multiple feature branches
# Stage the bug fix
git add src/parser.c
# Apply the fix to commit B and update all branches that contain B
git history fixup B
Result: B becomes B* with the fix, C and D are recreated as C* and D*, and both feat-1 and feat-2 now point at the rewritten tips.
Rewording an old commit message without touching the working tree
git history reword 9f3a2b7
Your editor opens; after saving, the commit gets a new hash, and any downstream commits are automatically rebased.
Splitting a large commit into two logical units
git history split 4c2d1e9
# Follow the interactive hunk prompt to allocate changes
Git creates 4c2d1e9a and 4c2d1e9b, then rewrites subsequent commits.
When to reach for jj instead of git history
- You need first‑class conflict handling that can persist through a rewrite.
- You want an operation log for easy undo/redo of complex history edits.
- Your workflow relies on the working copy being a commit (e.g., immutable snapshots after each change).
If your primary pain points are the three use‑cases covered above—fixing old content, correcting messages, or splitting commits—git history provides a lightweight, built‑in solution.
Conclusion
git history brings three focused, atomic commands to the core Git toolbox, dramatically lowering the friction of common history‑rewriting tasks while keeping the safety guarantees of traditional rebase. Although it does not yet replace the full feature set of jj, it offers a practical, no‑install path for developers who want to edit old commits and have all dependent branches updated automatically. As Git continues to evolve, we can expect further enhancements—perhaps even first‑class conflict support—making git history an increasingly essential part of everyday Git workflows.