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
An edit stop during interactive rebaseThe rebase replays commits up to the one marked edit, then stops. You amend that commit, continue, and the rebase replays the later commits on top of the amended version, resolving any conflicts the change causes.yourebasecommit 7be2d94later commitsrebase -i 7be2d94^apply, then stopedit + commit --amendrebase --continuereplay on amended committhe later commits are replayed, so a rename can conflict with code they added

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
Edit stop against fixup commitsAn edit stop changes one commit immediately and is good for a single, self-contained correction or a message change. Fixup commits record corrections as you go and fold them in later, which suits several corrections across several commits and lets reviewers see the corrections before they are squashed.edit stop--fixup + --autosquashcorrections at onceonemanyreviewers see the fixno, folded at onceyes, until squashedmessage-only changereword--fixup=amend:interruptionstops mid-rebasenone until squashfixups keep you on the branch tip; edit stops put you in the middle of history

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
Editing a middle commit safelyIdentify the target commit, apply the change either by an edit stop or a fixup commit, fold everything in with a rebase, then run the tests at every commit so later commits that relied on the old code are caught.Find targetlog --reverseChangeedit stop / --fixupFold inrebase -i --autosquashTest eachrebase --execa correct final tree does not mean every commit on the way is correct

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_HEAD restores 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.