Undo changes in git
In this article I will talk about the ways we have available to undo things when using git. I will tackle git checkout
, git reset
, git clean
and git revert
.
To better explain it, I would like to go to the basics about the three main sections we have on git, which are Working directory
, Index
and HEAD
.
- Git directory is where all the information regarding metadata and object is stored.
- The Index , also known as
staging area
, is a file with information about what will go into your next commit. - The working directory consist of files that you are currently working on.
These concepts are important, because the ways I will talk to undo changes might change different sections depending on how we use it. Any change on the working directory is considered a dangerous change, because if we delete something from the working tree that was not commited or added to stage yet, it will be lost.
For this article, I already have a repository with two files, a README
and a file named add_new_file
and I try to show how my repository is with the following image.
→ ls
README add_new_file
The current context of add_new_file
is the following:
This is the new file I want to add.
This is a new change
And the changes we can see using git log add_new_file
, which are:
→ git log add_new_file
commit e03d1e99a9210a1fb6d26ceb1fce93f0e795f4a6
Author: Bruno Costa <yyy@yyy.com>
Date: Sun Oct 1 11:42:10 2017 +0100
New change to the add_new_file
commit c5c7ae474a94480530059017066ac6197d0a043b
Author: Bruno Costa <yyy@yyy.com>
Date: Tue Aug 22 23:35:39 2017 +0100
Add new file
I will add some text to the file add_new_file
and I will show you how you can undo that change without having to delete the change yourself.
git checkout
Lets start with git checkout
. If you are using git, my guess is that you use this command often to checkout a branch to work on. This is where we use the git checkout <branch>
option, where is the branch we want to work on. `git checkout` has some other options, but we want to focus on the one that allows us to undo changes we may have and not the option that switch branches.
I will now add some text to the file add_new_file
, something like “Yet another change”.
→ git status
On branch new_branch
Your branch is up-to-date with 'origin/new_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: add_new_file
no changes added to commit (use "git add" and/or "git commit -a")
Now, if I wanted to revert those changes without manually deleting each change I did on the file, I could use the command git checkout <path>
. The git checkout <path>
does not switch branches, instead it is used to restore modified or deleled path from the index. Just type git checkout add_new_file
and check the status using git status
and you should see that those changes are now gone.
→ git status
On branch new_branch
Your branch is up-to-date with 'origin/new_branch'.
nothing to commit, working directory clean
You probably also noticed that you were not asked to confirm the checkout. Please remember, this is a very dangerous command. As far as I know, there is no way to recover those changes using git.
git reset
git reset
is used often and it’s one of those commands that can destroy work, because it affects the working tree. That said, please be careful with doing git reset with –hard option.
git reset –hard
Lets start by the option –hard. I know this is not the best option to start explaining git reset
, but since I just talked about git checkout
I think it would be good to start with –hard because given the same context we had on the git checkout example, the outcome will be the same.
Using git reset --hard
in this context, the outcome will be the same as using git checkout add_new_file
, but we need to understand the internals. Instead of picking the change from Index, it will pick up from HEAD
and it will also replace what we may have on the Index
. Which means that if we had do git add add_new_file
it will also reset the content of add_new_file from Index.
git reset –mixed
This command is the default, which means, if we don’t add anything to the git reset
it will behave as git reset --mixed
.
For this example, lets consider that we add the change we did on the add_new_file
to Index
.
If we do git reset --mixed
or git reset
it will replace the content we have on Index
, but it will not replace anything on the working directory.
git reset –soft
One thing I didn’t tell you is that you can indicate a commit to do the reset. That will first update the HEAD to the given commit and if we use --soft
it will be all that it would do.
Summing up, it will first update the HEAD to the given commit, if --mixed
it will also update the Index
or it will also update the working directory if we use --hard
.
git clean
This command is useful to remove those files that are not tracked, as an example, we can imagine adding a wrong file and we want to clean up the working directory. We can also use -x
option to remove those files that are ignored (.gitignore), which is useful to remove bin
folders for example.
git revert
Last, but not least, git revert
. All the options we’ve looked at so far just erase changes we did, without recording the action itself. This command is different, this command allows us to revert changes already committed, without changing the history of repository, but adding a new commit with the revert.
I can think of one good reason to use this command, imagine that we introduce a change with performance issues. We want to revert that change, but we want to keep that revert in our repository, to explain why we did it and because the history should tell the life story of a repository. However, if we find ourselves using git revert
too often before a release, we might need to change our workflow.
I will use the revert to remove the changes I did on commit e03d1e9
. git revert e03d1e9
and it will ask me for a commit message and I will use something like
Revert "New change to the add_new_file"
After starting the canary release, we started noticing some performance issues with
this change.
This reverts commit e03d1e99a9210a1fb6d26ceb1fce93f0e795f4a6.
After I save it, if I go to git log
I will see my new commit there.
→ git log --pretty=format:"%h %an: %s"
0dbcaef Bruno Costa: Revert "New change to the add_new_file"
e03d1e9 Bruno Costa: New change to the add_new_file
c5c7ae4 Bruno Costa: Add new file
de4fc88 Bruno Costa: Add README
Trying to translate it into an image, it would be something like this:
I hope you enjoyed the article, if you have any questions or suggestions, feel free to comment. Thanks!
Member discussion