Git: Undoing the last commit

April 8, 2009

No posts for a few weeks, whoa I’ve been slacking! Well here is a good one about Git!

I just did something stupid. Something really stupid. Firstly I haven’t been following Gits ‘commit often’ moto. As such I have a load of changes that need to be committed, which really should be seperate commits. Luckily most of the changes are in seperate files so its not too much of an issue. So off I go that file and that file in this one, commit. That file and that file, commit. That file and that file, commit. Oh, woops, that was the wrong file.

In case you weren’t following the situation is like this:

  • I have a number of changes uncommited (new and changed files)
  • In the last commit one of the files I committed (which was new) was the wrong file
  • So I need to change the commit, so that the correct new file is commited

If I was using Subversion I would probably give up now (or still be waiting for it to commit) and in the next commit write something along the lines of “whhoops wrong file in last commit blah blah blah” – and yes probably with typos. Luckily though, I am using Git! Well, Git is powerful but also (for me at least) very difficult to understand.

When I first did this I thought the first thing I needed to do was store everything in the stash as I have lots of changes (as shown below). However, that proved to be complicated as the stuff saved in the stash would not apply to a tree with uncommitted changes. Fortunately, this does not change any uncommitted changes, so no stash is required!

$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   app/controllers/storage/files_controller.rb
#   modified:   spec/controllers/storage/files_controller_spec.rb
#   modified:   spec/views/storage/files/index.html.rb
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   app/views/storage/files/new.html.erb
#   features/step_definitions/file_steps.rb
no changes added to commit (use "git add" and/or "git commit -a")

Next up we need to undo the last commit, the mixed argument is the key here, as it also leaves files in the state before you add them to be committed:

$ git reset --mixed HEAD^ 
app/controllers/storage/files_controller.rb: locally modified
app/views/storage/files/index.html.erb: locally modified
spec/controllers/storage/files_controller_spec.rb: locally modified
spec/views/storage/files/index.html.rb: locally modified
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   app/controllers/storage/files_controller.rb
#   modified:   app/views/storage/files/index.html.erb
#   modified:   spec/controllers/storage/files_controller_spec.rb
#   modified:   spec/views/storage/files/index.html.rb
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   app/views/storage/files/new.html.erb
#   features/step_definitions/file_steps.rb
#   spec/views/storage/files/new.html.rb
no changes added to commit (use "git add" and/or "git commit -a")

FInally I can make the correct changes!

$ git add app/views/storage/files/index.html.erb 
$ git add spec/views/storage/files/index.html.rb 
$ git commit -m "Add menu to Files index view"
Created commit 2233a71: Add menu to Files index view
 2 files changed, 11 insertions(+), 8 deletions(-)
$ git status
# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#
#   modified:   app/controllers/storage/files_controller.rb
#   modified:   spec/controllers/storage/files_controller_spec.rb
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   app/views/storage/files/new.html.erb
#   features/step_definitions/file_steps.rb
#   spec/views/storage/files/new.html.rb
no changes added to commit (use "git add" and/or "git commit -a")

Easy! This is a good example of why I believe Git to be too complicated, an alias to the command in the second example of “git undo” would be soooo much easier!