Git tip: Fix a mistake in a previous commit

Here's a handy tip that shows off one of the little conveniences that makes me love Git.

In Subversion, if you made a mistake in one of your commits — too bad. At best, you might be able to edit the revision log, if your repository was configured to allow that (Sourceforge repositories aren't).

In Git, as long as you haven't pushed upstream yet, it's trivial to change the commit log, or even the actual commit contents!

Suppose you just made a commit, and you're working on some more changes, when you realize you made a mistake in the previous commit. Here's how you can go back and fix it, without losing any of the new work you've done since you commited:

  1. Save your work so far.
  2. Stash your changes away for now: git stash
  3. Now your working copy is clean at the state of your last commit.
  4. Make the fixes. (If you just want to change the log, skip this step.)
  5. Commit the changes in "amend" mode: git commit --all --amend
  6. Your editor will come up asking for a log message (by default, the old log message). Save and quit the editor when you're happy with it.
  7. The new changes are added on to the old commit. See for yourself with git log and git diff HEAD^
  8. Re-apply your stashed changes: git stash apply
  9. Continue happily with your life.

Extra cool: since Git stashes the changes you made (not the whole file), the file will still have the fix even after you re-apply the stash!

What if you want to fix a mistake in an even older revision, not just the most recent? You can do that with Git, too, and it's almost as easy as what we just did. But, again, if you've already pushed your commits to a central repository (or if people have fetched from your local repository), you shouldn't change previous commits retroactively. That would cause a temporal paradox that would swallow the universe!

Here's how to fix an earlier revision [Note: see below for a better way]:

  1. Save and stash your work so far, like before.
  2. Make the fix, and commit without amend mode: git commit -a -m "Foo"
  3. Look at git log and copy the first 5 or so characters from the ID of the earlier commit onto your clipboard.
  4. Start the interactive rebase process, pasting in the characters from the ID: git rebase --interactive ID
  5. Your editor will come up with several lines like pick d3adb33 Commit message, one line for each commit since the older one.
  6. For the most recent commit, change the word "pick" to "squash" at the start of the line. (This tells Git to combine it back into the one before it.)
  7. Use your editor to reorder the lines, putting the line for the most recent commit just after the older commit you want to fix.
  8. Save and quit your editor.
  9. The editor will come up again, this time asking you for the commit message for the combined commit. Delete or comment out the newer, temp message ("Foo").
  10. Save and quit your editor.
  11. Git does its magic, squashing those two commits together, and recreating all the commits since then.
  12. Re-apply the stash and continue happily with your life.

[Update 2008-09-06: Since posting this article, I've stumbled upon an easier way than squashing commits — editing commits!]

  1. Save and stash your work so far.
  2. Look at git log and copy the first 5 or so characters from the ID of the commit you want to edit onto your clipboard.
  3. Start the interactive rebase process, pasting in the characters from the ID: git rebase --interactive ID [Update: Actually, that should be git rebase --interactive ID^, with the ^ to refer to the parent of that commit.]
  4. Your editor will come up with several lines like pick d3adb33 Commit message, one line for each commit since the older one.
  5. Change the word "pick" to "edit" in front of the commit you want to change.
  6. Save and quit.
  7. Edit your project files to make the correction, then run git commit --all --amend.
  8. After you've committed the fixed version, do git rebase --continue.
  9. Git will do its magic, recreating all the commits since then.
  10. You might need to resolve some conflicts, if the change you made affected later commits. Follow Git's instructions to resolve those.
  11. Once the rebase is done, re-apply the stash and continue happily with your life.

Because of how Git commit IDs works, the commits will have new IDs now, which is why you shouldn't do this if someone already has the commits with the original IDs. In that case, you should just suck it up and make a new commit to fix it, like you would with Subversion.


Comments



You say: "Look at git log and copy the first 5 or so characters from the ID of the commit you want to edit onto your clipboard.
Start the interactive rebase process, pasting in the characters from the ID: git rebase --interactive ID"
The ID should actually be the parent of the commit you want to edit.




Thanks for the blog, just used that process.

For those who forgot to add a file to the index before committing and stumbled upon it a while later:
After step 6, you can apply the stash, add the missed file to the index and commit as in step 7. After that you 'git stash' again.

If you stash again before step 7 the file will not be committed, it will be staged.


This saved my evening !

I didn't knew that you could rebase a branch on itself.

Thanks


hi All ,
We came to know this can be achieved by ammend and rebase.

Can there be any alternative so as to achive the same thereby using only commit id not rebase.

Thanks in advance.

Regards,
Abhishek J


Hello,
If you forgot to add/update a file for the last commit, and without even to update the commit message, do so:

git add fileYouForgot
git commit --amend --no-edit

Comments are closed.

Previous post: Revision numbers considered harmful Next post: Fun (and not-so-fun) with Haskell and Scheme