Editing an old commit in the middle of a branch Jump to heading
Review comments rarely target the last commit. A reviewer asks for a rename in the second of six commits, a test belongs with the change in the third, a commit message needs an issue key. Adding a seventh commit that says “address review” makes the history harder to review and bisect. Editing the commit where the change belongs keeps each commit coherent. Git offers two ways to do it: stop the rebase at that commit and amend it, or write a fixup commit now and fold it in later. Both rewrite every commit after the edited one, which is fine on a branch only you use and needs care otherwise. This page covers both methods and when each fits, within interactive rebase workflows.
When to use this approach Jump to heading
- A review comment applies to a specific earlier commit on your branch.
- A commit message several commits back needs correcting.
- A change was committed to the wrong commit and belongs in an earlier one.
- The branch is yours, or collaborators have agreed to the rewrite — see safe git rebase -i for shared branches.
Step 1 — Identify the commit to edit Jump to heading
List the branch’s commits and find the target. Note its hash; you will need it for either method.
git log --oneline --reverse main..HEAD
# a1f3c20 Add invoice model
# 7be2d94 Add invoice PDF renderer <- reviewer: rename render() to render_pdf()
# c40e8a1 Add page numbering
# 5d9b113 Wire PDF download endpoint Step 2 — Method A: stop at the commit with an edit instruction Jump to heading
Start an interactive rebase from the commit’s parent and mark the commit edit. The rebase stops after applying it, with that commit as HEAD, so you can change it directly.
git rebase -i 7be2d94^
# in the todo list, change "pick 7be2d94" to "edit 7be2d94", save and close # Now HEAD is 7be2d94. Make the change and amend it.
sed -i 's/def render(/def render_pdf(/' src/invoice/pdf.py
git add src/invoice/pdf.py
git commit --amend --no-edit
git rebase --continue In the example, c40e8a1 and 5d9b113 call render(). Replaying them onto the renamed function applies cleanly, but leaves calls to the old name. Fix those in their own commits — the next method makes that easy.
Step 3 — Method B: write a fixup commit and autosquash it Jump to heading
Make the change now, on top of the branch, as a commit marked as a fixup for the target. Later, an autosquash rebase moves it next to its target and folds it in. This is better when you are making several corrections to several commits during one review round.
sed -i 's/def render(/def render_pdf(/' src/invoice/pdf.py
git commit -a --fixup=7be2d94 # "fixup! Add invoice PDF renderer"
sed -i 's/\.render(/.render_pdf(/' src/invoice/pages.py
git commit -a --fixup=c40e8a1 # "fixup! Add page numbering"
git rebase -i --autosquash main # todo list already reordered; just save Set rebase.autoSquash = true globally and the --autosquash flag becomes the default for interactive rebases. The fuller review workflow is in using fixup commits and autosquash during review.
Step 4 — Change only a message Jump to heading
For a message change, there is no need to touch content. Use reword in the todo list, or create an amend-style fixup that replaces the message when squashed.
# Option 1: reword during an interactive rebase
git rebase -i 7be2d94^ # change "pick" to "reword" for 7be2d94
# Option 2: a fixup that carries a new message (Git 2.32+)
git commit --fixup=reword:7be2d94 # opens an editor for the replacement message
git rebase -i --autosquash main # Verification: the message changed and no content did
git log --format='%h %s' main..HEAD
git diff ORIG_HEAD HEAD --stat # empty for a message-only change Step 5 — Check the result commit by commit Jump to heading
Editing a middle commit can leave later commits broken even when the final tree is right — the renamed function above is a typical case. Build or test every commit, not just the tip.
git rebase --exec 'make test >/dev/null' main This is covered in depth in testing every commit with rebase --exec.
⚠️ SAFETY WARNING: Editing a middle commit rewrites it and every commit after it. Push the result with
git push --force-with-lease, and only on branches nobody else has based work on. If the rebase produced something wrong,git reset --hard ORIG_HEADrestores the branch as it was before the rebase started.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Can I edit the very first commit of the repository? Jump to heading
Yes, with git rebase -i --root. It treats the root commit like any other in the todo list.
What if the edit causes conflicts in later commits? Jump to heading
The rebase stops at each conflicting commit, and you resolve it as usual. With rerere enabled, repeating the same rebase later reuses your resolutions; see automating repeated conflict resolution with rerere.
Should I edit commits after a pull request is approved? Jump to heading
It depends on the team’s review rules. Many teams accept squashing reviewed fixups before merge, because the content is what was approved. Some forges dismiss approvals on force-push; check that your rules match your workflow.
Related Jump to heading
- Interactive Rebase Workflows — the parent topic.
- Splitting a Large Commit into Reviewable Pieces — another use of the edit stop.
- Amending the Last Commit Safely — the simple case of editing the tip.
- Reviewing a Force-Push with git range-diff — how reviewers see what you changed.