GIT provides us with various mechanisms of undoing changes. Let us look at them one at a time.
Undoing changes in working directory before staging
We are going to use the same scenario as in the article GIT basics where we have a folder named pond at /var/www/html/pond which contains two files frogs and snakes.
Let us first make changes to the file snakes
# vim snakes
this is a deadly snake.
save and exit
Check GIT status
# git status
Output:
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: snakes
no changes added to commit (use "git add" and/or "git commit -a")
Now, as GIT is suggesting we can discard changes in working directory by typing following command;
# git checkout -- snakes
Check GIT status again
# git status
Output:
On branch master
nothing to commit, working directory clean
Thus we can see that with git checkout -- <file> command, we can undo changes in working directory before staging.
Undoing changes in working directory after staging
Make changes to snakes file again
# vim snakes
this is a deadly snake.
save and exit
Check GIT status
# 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: snakes
no changes added to commit (use "git add" and/or "git commit -a")
Stage the modified file
# git add snakes
Check GIT status again
# git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: snakes
Now as GIT is suggesting git reset HEAD <file> command can be used to undo the staging process.
# git reset HEAD snakes
Output:
Unstaged changes after reset:
M snakes
Check GIT status again
# git status
Output:
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: snakes
no changes added to commit (use "git add" and/or "git commit -a")
Now as we can see we are at the situation where modifications have been unstaged. Now we can use git checkout -- <file> command to undo changes in the working directory.
Undo changes in working directory after commit
Check content of frogs file
# cat frogs
Output:
this is the first frog.
Make changes to the frogs file and perform a new commit.
# vim frogs
this is the first frog to see the world.
save and exit
# cat frogs
Output:
this is the first frog to see the world.
# git add frogs
# git commit -m "more changes to frogs"
Output:
[master a63ed19] more changes to frogs
1 file changed, 1 insertion(+), 1 deletion(-)
# git log
commit a63ed199ac742b13cf8fe77992f48e2f8ba66a8c
Author: root <apurwa@gmail.com>
Date: Thu Feb 23 13:51:47 2017 +0545
more changes to frogs
commit b0a2b9b16ed8bced2e04c3f88838479d28553a34
Author: root <apurwa@gmail.com>
Date: Mon Feb 20 12:35:47 2017 +0545
changes to frogs
commit 8d5e7611dd8eb2e43078a8e7dfe56e9e3cff3e8b
Author: root <apurwa@gmail.com>
Date: Mon Feb 20 11:57:56 2017 +0545
first commit
Now, if we want to recall the last commit's frog file we will type the following command;
# git checkout <first 4 characters of commit hash> <file>
Here commit hash is the 40 character long string such as b0a2b9b16ed8bced2e04c3f88838479d28553a34 which is used to identify a specific commit.
We can refer to a given commit by the first 4 character of its hash value.
Now if we want to recall the frogs file from the commit changes to frogs, we need to refer to its hash value, b0a2b9b16ed8bced2e04c3f88838479d28553a34 by b0a2
Hence, in order to recall frogs file from changes to frogs commit we type;
# git checkout b0a2 frogs
# git status
Output:
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: frogs
# cat frogs
Output:
this is the first frog.
We can see that the above command reloaded the frogs file from the specified commit and pushed it to the staging area.
Now, we can continue with a new commit as before. However, this commit will have the old version of frogs file.
If we want to revert this operation and go back to the current the state of affairs, we simply type;
# git checkout master .
Here . represents all the files. Hence, this command loads the latest version of all the files.
There is another way we can reload old versions of the files.
Instead of typing git checkout b0a2 frogs command we can type the following command;
# git checkout b0a2
Output:
Note: checking out 'b0a2'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b new_branch_name
HEAD is now at b0a2b9b... changes to frogs
# git status
Output:
HEAD detached at b0a2b9b
nothing to commit, working directory clean
# cat frogs
this is the first frog.
We can see that even in this case, frogs file has been loaded from an older commit titled changes to frogs marked by b0a2.
However this time, the file is not shown as modified and staged.
Another aspect of this operation we must understand is the detached head state. Detached head state refers to a state when the pointer points to an old state of the filesystem.
In case of the command git checkout b0a2 frogs, the pointer still pointed to the tip of the project however the file frogs was loaded from an old state.
In case of the command git checkout boa2, the whole filesystem was loaded from an old state.
To get out of this detached head state we need to type the following;
# git checkout master
Output:
Previous HEAD position was b0a2b9b... changes to frogs
Switched to branch 'master'
In case we had made any commits before typing this command, these commits would be lost. To preserve these commits, we would have to create a new branch.