Need to undo a git commit? You have a few options.
Git revert
Git revert is the most straightforward way to undo some committed code. Git revert will create a new commit on your branch which does the exact opposite of the commit you want to revert. Let’s see an example:
The most recent commit was a mistake, let’s undo it with a git revert head
:
The revert command above added a new commit to my branch which does exactly the opposite of the commit it reverted. I accidentally deleted something, and git revert
put it back. This is what the history looks like now:
The full history of my mistake is still there for posterity, but If I compare the current state of the branch with HEAD~2
(the commit before the mistake), there are no differences:
Using git revert has the advantage of preserving the entire git history—it keeps the record of the bad commit and the revert commit in the git log. For that reason, I’ve used this many times when rolling back bad code on a production branch, where you really want to keep the full git history intact, and where you really don’t want to git push –force.
Git reset
The simplest version of git reset
will un-stage some changes you have ready to be committed:
If your changes have already been committed you can reset to an earlier commit, and git reset has a few options specifying how to handle the changing code.
Git reset –hard
Git reset –hard is destructive, anything in the way of the reset will be deleted. If you reset –hard back 4 commits, the code from the most recent 3 commits will be lost.
That said, it’s very handy if you need to scrap your most recent commit (or commits). If you know for sure your most recent commit is not worth saving, just git reset --hard HEAD~1
. In English, that command says “reset the HEAD of this branch back one commit”1. Here’s an example:
In the screenshot above, I accidentally committed something directly to the main
branch, which is generally not a good idea. Let’s assume we can just delete that commit—we don’t care what’s in it. That’s where git reset --hard
comes in:
My local HEAD has now been moved back one commit, and if I run git log
again the first commit above–”did something awesome”–is gone:
A note on HEAD~1
Using HEAD~12 is not commonly seen in git commands other than this one, but it is available; most git command examples use a specific commit hash instead.
Head~1 literally means “one commit before HEAD”. If you wanted to reset back two commits, you would reset --hard HEAD~2
, and so on. You can also git reset
to a specific commit hash—the example above could equally have been written using git reset --hard b1385919
.
Git reset –soft
This is a more gentle version of git reset
. Running git reset --soft HEAD~1
will roll your branch back one commit, but instead of deleting the most recent code it will put it back in your “changes to be committed”, and let you modify the commit as you normally would. If you git reset --soft
back several commits, all of the intermediate commits will be added to your staged files:
Force Pushing
With any of the above git reset options, you may have to git push --force
to get your code to push to the remote. Git reset can change the history of your branch, and if the local git history does not match the repository’s remote history, github will raise error: failed to push some refs
. Assuming your code is the latest, you can get around this error with a git push --force-with-lease
3.
Git reset –keep
I have never actually used this option, but it allows you to run a git reset and keep any existing uncommitted changes you might have. In practice, I prefer the following:
- git stash
- git reset
- git stash pop
I find the three-step workflow above more clear than using –keep.
Git commit –amend
This one doesn’t exactly undo a commit, but it does allow you to modify the most recent commit. I have a whole post about this command, but here are a few quick examples:
# Modify the message of your most recent commit
git commit --amend -m "new message for commit"
# Add a file to your most recent commit without changing the message
git add forgotten_file.txt
git commit --amend --no-edit
With either of these versions of git commit amend you will have to force push, or better yet: git push --force-with-lease
. For an explanation of why, go to this section of my post about amend.
Helpful Links
- What is HEAD in git? – Great explanation on StackOverflow
- Official git reset docs
Notes
- That’s not exactly what it means, but HEAD is a complicated git concept. For a great explanation, see this StackOverflow answer. ↩︎
- Another plug for the link about HEAD in note 1 above ^ ↩︎
- I have more detail about force pushing in this post about amending commits ↩︎