When you are working on a project by yourself or as part of a team, there might be instances when you want to undo a commit. The
git reset command is one of the tools known to be a real lifesaver.
Git’s Tracking Mechanism
Before going to
git reset, we need to understand about the underlying structure of git. Git manages and tracks files, through tree-like structures with nodes and pointers.
There are basically three of these “trees” on your local git repository:
- The Working Directory: Or working tree, and it refers to your local directory and
git statuswill give you the state of your working directory.
- HEAD: Is just your current branch’s last commit snapshot. If you were to switch branches with
git checkoutthen the HEAD will change to the last commit on the branch.
- Index: Or staging area. So when you
git addfiles to commit it adds them to this index.
The following example shows making changes to a file and then adding it to index (staging) using
git add then checking
git status to see changes to be committed.
Now when we do
git commit it saves as a more permanent snapshot and updates the master and HEAD to that pointer. So if we do
git status after
git commit, we’ll see that all three trees are in the same state (there will be nothing to commit).
So what’s the purpose of git reset?
You might be wondering why all this preamble just to get to
git reset. Well
git reset manipulates these trees in different ways. So git reset will accept a variety of options depending on what you want to do.
Git Reset Modes
Let’s say we committed some changes and files, then to realize we committed them to the wrong branch or our commit is buggy so we want to rewind. Here’s where knowing about the git reset modes is useful.
All git reset with mode will update the HEAD pointer. It has the following syntax
git reset <mode> <commit-optional>
The main modes are:
--soft: Resets HEAD pointer and leaves the index and working directory untouched. So your HEAD will be reset and the other trees still showing the latest changes.
--mixed: Default option. Resets the HEAD and index. This basically un-stages all your changes and leaves you before you did
git add. Note: If you do git reset by itself without any options, it will be interpreted as
git reset --mixed.
--hard: Be careful with this one. Besides resetting HEAD, index, it also resets your working directory. So you could lose code written! This as any changes after current HEAD pointer (last commit) are discarded.
Other modes such as
--keep can be read in the official documentation.
Useful git reset Tricks
Rewinding a commit
Remember that if we omit the mode (git reset without any options) it will be interpreted as –mixed.
Now if we just type
git reset HEAD nothing will happen, but if we do
git reset HEAD~1 then our HEAD will now point to its previous commit.
The following example continues from the previous one. Let’s suppose we add new text to our sample file. Then we git add and commit. Then after we do git reset HEAD~1, all our changes are un-staged and on the previous commit.
This is a useful and fast way when we want to undo a commit!
Un-staging a specific file
Let’s say you added a file to the index with git add. We can remove that file just by doing:
git reset HEAD <file-name>
Scenario: I messed up all my code! Can I go back to when it was working?
If you want to throw away all local changes and go back to your previous commit your last resort is
git reset --hard.
Often if you break your code this could be your only option. If you know the commit hash you can
do git reset --hard <commit>. But note this one will also affect any other commits after the specific commit (if any)!
Scenario: This commit was supposed to be in a new branch!
This often happens, especially when you are starting to work in production. If this happens to you, no panic!
What we need to do is basically create the new branch that has the state of the branch we need to rewind. Then we will reset the affected branch, and then we checkout to the new branch and do the commits there:
git branch new-branch
git reset HEAD~1 --hard
git checkout new-branch
One Last Word
Be careful when doing
git reset --hard and also when rewinding to a specific commit, especially in production code and when you are working with other developers. Often git revert is the safe way to make these changes. But that is a conversation for another time. Until then! 👋🏼