Undo – working with a dirty area

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.

Getting ready

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;
}

How to do it...

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:

  1. First, check the history:
    $ git log --oneline
    3061dc6 Adds Java version of 'hello world'
    9c7532f Fixes compiler warnings
    5b5d692 Initial commit, K&R hello world
    
  2. Then, check the status:
    $ 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")
    
  3. As expected, 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
    
  4. The reset is done, and we got rid of the commit we wanted. Let's resurrect the changes we stashed away and check the file:
    $ 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.

How it works…

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.

See also

  • Refer to Chapter 12, Tips and Tricks, for more details on the git stash command
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset