In the previous examples, we assumed that the working tree was clean, that is, no tracked files are in the modified state. However, this is not always the case, and if a hard reset is done, the changes to the modified files will be lost. Fortunately, Git provides a smart way to quickly put stuff away so that it can be retrieved later using the git stash
command.
Again, we'll use the example of the hello world
repository. Make a fresh clone of the repository, or reset the master
branch if you have already cloned.
We can create the fresh clone as follows:
$ git clone https://github.com/dvaske/hello_world_cookbook.git $ cd hello_world_cookbook
We can reset the existing clone as follows:
$ cd hello_world_cookbook $ git checkout master $ git reset --hard origin master HEAD is now at 3061dc6 Adds Java version of 'hello world'
We'll also need to have some files in the working condition, so we'll change hello_world.c
to the following:
#include <stdio.h> void say_hello(void) { printf("hello, world "); } int main(void){ say_hello(); return 0; }
In order not to accidently delete any changes you have in your working tree when you are about to undo a commit, you can have a look at the current state of your working directory with git status
(as we already saw). If you have changes and you want to keep them, you can stash them away before undoing the commit and retrieve them afterwards. Git provides a stash
command that can put unfinished changes away so it is easy to make quick context switches without losing work. The stash
functionality is described further in Chapter 12, Tips and Tricks. For now, you can think of the stash
command like a stack where you can put your changes away and pop them later.
With the hello_world.c
file in the working directory modified to the preceding state, we can try to do a hard reset on the HEAD
commit, keeping our changes to the file by stashing them away before the reset and applying them back later:
$ git log --oneline 3061dc6 Adds Java version of 'hello world' 9c7532f Fixes compiler warnings 5b5d692 Initial commit, K&R hello world
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello_world.c no changes added to commit (use "git add" and/or "git commit -a")
hello_world.c
was in the modified state; so, stash it away, check the status, and perform the reset:$ git stash aved working directory and index state WIP on master: 3061dc6 Adds Java version of 'hello world' HEAD is now at 3061dc6 Adds Java version of 'hello world' $ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean $ git reset --hard HEAD^ HEAD is now at 9c7532f Fixes compiler warnings $ git log --oneline 9c7532f Fixes compiler warnings 5b5d692 Initial commit, K&R hello world
$ git stash pop On branch master Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded. (use "git pull" to update your local branch) Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: hello_world.c no changes added to commit (use "git add" and/or "git commit -a") Dropped refs/stash@{0} (e56b68a1f5a0f72afcfd064ec13eefcda7a175ca) $ cat hello_world.c #include <stdio.h> void say_hello(void) { printf("hello, world "); } int main(void){ say_hello(); return 0; }
So, the file is back to the state before the reset, and we got rid of the unwanted commit.
The reset
command works as explained in the previous examples, but combined with the stash
command, it forms a very useful tool that corrects mistakes even though you have already starting working on something else. The
stash
command works by saving the current state of your working directory and the staging area. Then, it reverts your working directory to a clean state.
git stash
command