Git Reset, not so --hard after all

TL;DR: To undo a commit, $ git reset {last_good_commit}. All the changes you ‘reset’ will be ready for you to modify and re-commit.


Git “reset” is an easy way to undo commits. Like many things in the Git universe, it is scary until you learn to use it, and then it’s easy as pie.

We will start off with some examples (‘Git reset for dummies’), continue with some of the theory behind it, and end with references for further reading.

So, we’re in our branch and we’ve made a mistaken commit, as we can see in our git log:

Oh shit, a mistake

Fixing this is easy. We want to go back to the last ‘good commit’, in this case 80997b1. So:

$ git reset 80997b1

After resetting, we are exactly back to ‘just before’ applying the last commit: our log will show the top commit as ‘commit #5', and all the changes applied in the bad, ‘stupid commit’ will be waiting to be committed once more.

Now we can start getting fancier. Instead of a normal reset, we could have run a ‘reset --soft’, as in:

$ git reset 80997b1 --soft

We would get a similar result: after the reset the top commit in the log would be commit #5, but the changes made in ‘stupid commit’ (the one we just undid) would appear in the staging area, as in already ‘added’ to the next commit.

In other words, the difference between (a):

$ git reset 80997b1
$ git commit -m “undoing reset” #will NOT undo reset

and (b):

$ git reset 80997b1 --soft; 
$ git commit -m “undoing reset” #WILL undo reset

is that (a) would result in a ‘no changes added to commit’ message , while (b) would indeed undo the commit. Replace (a) above with ‘git commit -am’ and both would do exactly the same.

So, using the --soft flag when resetting leaves the reset changes in the ‘staging’ area.

Which naturally brings us to the harbinger of doom,

$ git reset 80997b1 --hard

This puppy will bring you back to commit 80997b1, but will destroy all of the changes you’re resetting. No getting ‘em back. So you better be damn sure you want to reset your changes.

Bottom line, IMHO, is always use the simplest git reset. The --soft flag is confusing and the --hard flag is dangerous, but a simple git reset can do no harm, and you can immediately [fix and] re-apply everything you’ve just reset with a simple commit.

If you’re unsure of yourself but you want to amend a commit, git reset.


If you’ve reached this point, it may be worth learning how this stuff actually works.

So, Git ‘knows’ about three things:
1. Previous commits (including the last commit, referred to as The HEAD)
2. The next commit (referred to as the Index or the ‘staging’ area)
3. The working directory (the files on the filesystem, which you see in your text editor.)

This is indeed an important revelation and should help you understand much of ‘wtf is going on’ in the Git world. Most of Git’s actions (checkout, commit, and so on) is about making transitions between these states. As far as ‘resets’ are concerned, from the perspective of the changes you are undoing, it works out as follows:

  • ‘reset soft’ brings the changes you are reverting back into your Index.
  • ‘reset’ brings the changes you are reverting back into your working directory.
  • ‘reset hard’ destroys the changes you are reverting.

In all of the above cases, the git log (HEAD) will point to the commit you had reset to.

NB: Now that you understand this process, you can also understand (and test yourself) how and why to move a file back from the ‘staging area’, using the same git reset <filename> ( == git reset HEAD <filename>): it will set the HEAD to be the ‘HEAD’ and all changes back into the working directory.


Learning Git is a long process. I heartily recommend creating your own repository and playing around it with — try the various ‘reset’ techniques and see what works for you. This is the best way to learn how Git actually works. You can also always RTFM. My two recommended links on ‘Git reset’ are http://git-scm.com/blog and http://blog.plover.com/prog/git-reset.html, which were the basis of this post.

Best of luck.

-Sella