Waste your time due to accident git checkout? Prevent it now!

A common thing by using git in unix shell is accident executing git checkout -f command. For example, you want to reset some file by getting it from the head with git checkout -f filename, but instead of this you drop the changes on the whole branch by accidentally executing git checkout -f.

You can easily protect yourself by saving changes on the development branch into a tiny patch file before any git checkout command:

First of all define a directory for patch files:

# Directory contains restore patch files
mkdir ~/git-restore-patches

Add the following wrapper function at the end of ~/.bashrc file:

# Directory with the patch files. Change this variable if you use a different one.
export GIT_PATCHES_DIRECTORY="$HOME/git-restore-patches"

# Git function wraps a git command
function git() {
	# Prefix of patch file name
	file_name_prefix="gbck";
	# Dummy check
	if [ ! -d "$GIT_PATCHES_DIRECTORY" ];
	then
		echo
		echo "Command is aborted!"
		echo "Please define git patches directory. For example: "
		echo "   mkdir ~/git-restore-patches"
		echo "   export GIT_PATCHES_DIRECTORY=~/git-restore-patches"
		echo
		return 0
	fi;
	# Git checkout command is specially handled
	if [ $1 == "checkout" ]; 
	then 
		file_name=$(pwd);
		file_name=${file_name//'/'/"-"};
		if [ ${#file_name} -gt 15 ];
		then
			file_name="-${file_name:(${#file_name}-15):15}";
		fi;
		file_name=$GIT_PATCHES_DIRECTORY/$file_name_prefix$file_name-$(date +%Y%m%d-%H%M%S).patch; 

		echo "The state of the branch is stored in patch file \"$file_name\" before working tree is updated. "
		echo 
		# Storing changes into a patch file
		git diff > $file_name
		# Continue with original git command
		command git "[email protected]";

	else 
		command git "[email protected]"; 
	fi; 
}

Load the function and the variable into the shell. It is done by every shell startup:

source ~/.bashrc

I am going to test how it works now. I have done some changes on the branch, I removed some import lines from a Java file:

[email protected]:~/devres/someproject$ git status
# On branch 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:   src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
#
no changes added to commit (use "git add" and/or "git commit -a")

[email protected]:~/devres/someproject$ git diff
diff --git a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java b/bcsc/webapp/src/main/java/com/o2/port
index 7c1c636..8ae4cab 100755
--- a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
+++ b/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
@@ -1,7 +1,5 @@
 package com.rubezhanskyy.blog.example;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
/**
 * Simple class for git presentations

I execute git checkout -f and loose the changes in the code:

[email protected]:~/devres/someproject$ git checkout -f
The state of the branch is stored in patch file "/home/rubezham/git-restore-patches/gbck-s-git-bcsc-sd32-20111211-131354.patch" before working tree is updated. 

The changes have gone:

[email protected]:~/devres/someproject$ git status
# On branch master
nothing to commit (working directory clean)

But I got a back-up! I can restore my changes from the patch file using git apply command:

[email protected]:~/devres/someproject$ git apply /home/rubezham/git-restore-patches/gbck-someproject-20111211-131354.patch

My code is available now!

[email protected]:~/devres/someproject$ git diff
diff --git a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java b/bcsc/webapp/src/main/java/com/o2/port
index 7c1c636..8ae4cab 100755
--- a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
+++ b/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
@@ -1,7 +1,5 @@
 package com.rubezhanskyy.blog.example;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
/**
 * Simple class for git presentations

Alternative way to restore the changes from the patch file is to use linux patch tool. Here is an example:

# Drop the changes with git checkout -f

[email protected]:~/devres/someproject$ git checkout -f
The state of the branch is stored in patch file "/home/rubezham/git-restore-patches/gbck-someproject-20111211-131444.patch" before working tree is updated. 

# Changes are dropped

[email protected]:~/devres/someproject$ git status
# On branch master
nothing to commit (working directory clean)

# Restore the changes from the patch file using the patch tool:

[email protected]:~/devres/someproject$ patch -p1 < /home/rubezham/git-restore-patches/gbck-s-git-bcsc-sd32-20111211-131354.patch
patching file src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java

# Done. The changes are restored:

[email protected]:~/devres/someproject$ git diff
diff --git a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java b/bcsc/webapp/src/main/java/com/o2/port
index 7c1c636..8ae4cab 100755
--- a/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
+++ b/src/main/java/com/rubezhanskyy/blog/example/PetShopImpl.java
@@ -1,7 +1,5 @@
 package com.rubezhanskyy.blog.example;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
/**
 * Simple class for git presentations

[email protected]:~/devres/someproject$ 

Good luck!

Share