Sometimes it’s hard to understand a few Git commands. What do they actually do and how do they differ when certain flags are used with the commands? In this article, we’ll see how to undo and redo the last commit in multiple ways.
How Does Git Commit Work?
- When we modify a file in a working directory, the changes are initially unstaged (not added to index).
- We have to add it to the index (staging area) by using
git add filename
in order to commit the changes. - When we commit using
git commit -m “message”
all the changes that are added to the index will be committed (saved to the local Git repository). - Finally, the current branch and
HEAD
will be pointing to the last commit (i.e. the most recent commit).
Before we jump in, take a look at the pictorial representation for the different flags. Refer to this diagram throughout the article whenever needed.

What Flags Do We Use With the Git Reset Command?
- git reset --soft HEAD~1
- git reset --mixed HEAD~1
- git reset --hard HEAD~1
1. What Is git reset --soft HEAD~1?
git reset
changes where the current branch is pointing to (HEAD
).
HEAD
is a pointer or a reference to the last commit in the current branch. HEAD~3
refers to the commit that is three places before the current HEAD
in the commit history.
Let’s say we have the following commit tree:
- C1 - C2 - C3
Assuming HEAD
is pointing to C3
and the index (stage) matches to C3
.
When we execute git reset --soft HEAD~1
, now HEAD
points to C2
, but the index (stage) will have changes from C3
and git status
will show them as staged. Now if we run git commit
at this point, we’ll get a new commit based on the same staged changes as C3
.
2. What Is git reset --mixed HEAD~1?
Assume HEAD
is pointing to C3
and the index matches C3
.
- C1 - C2 - C3
When we execute git reset --mixed HEAD~1
then HEAD
points to C2
, also the index gets modified to match C2
(all the changes that were committed won’t be seen on stage but they are there in the working directory).
If we run git commit
at this point, nothing will happen since the index matches HEAD
. But the changes are still there in the working directory, but since they’re not in the index, git status
shows them as unstaged.
To commit them, we would use git add
and then we’ll use git commit -m “message”
to commit changes as we do.
Note that if you run git reset HEAD~1
without specifying a flag, --mixed
is the default option.
What Is git reset <file>?
git reset:
It removes from the index/staging area, but it leaves the file unchanged in the working directory. Basically, this command unstages a file without overwriting any changes.
3. What Is git reset --hard HEAD~1?
Assuming HEAD
is pointing to C3
and the index matches C3
.
- C1 - C2 - C3
--hard
flag modifies HEAD
, index and working directory. If we’re at C3
and run git reset --hard HEAD~1
, then the changes committed to C3
, uncommitted changes that we have in the staging area and all the changes in the working directory will be removed and the working directory will match C2
commit.
In summary, the HEAD
, index and working directory all will match the state of the C2
commit and have the same version of files.
Note that when we use --hard
all the changes will be removed from the local git repository, index (staging area) and working directory. So always think before using --hard
whether you want to delete the changes from the working directory also.
How to Undo a Remote Commit
There are times you want to undo a commit you have pushed to a remote repository. You can use git revert
to undo it locally and push this change to the remote branch. This does not remove the commit, but it creates a new commit that undoes the changes made.
It’s worth noting that unlike git reset
, which modifies history, git revert
preserves history by just creating a new commit that reverses the changes.
First, get the commit hash using git reflog
.
git reflog
Then revert it. Let’s assume my commit hash is 1257b6910
, I’ll do the following:
git revert 9157b6910
Finally, push this change to the remote branch.
How to Recover a Commit After Using git reset --hard
Let’s say you destroyed a commit using --hard
, but then decided you need it back.
Don’t worry! There’s still a way to get it back.
Type git reflog
and you’ll see a list of (partial) commit SHA’s (basically commit hashes). This command shows a log of changes to the local repository’s HEAD
. Now find the commit you destroyed and execute the below command.
git checkout -b NewBranchName CommitHashYouDestroyed
Now you’ve restored that commit. Commits don’t actually get destroyed in Git for 90 days or so, so you can usually go back and rescue one you didn’t mean to get rid of.
Frequently Asked Questions
What does the git reset command do?
The git reset
command moves the HEAD
(current branch pointer) to a different Git commit, allowing you to undo changes in a working directory and return to a certain commit in different ways depending on the flag used. git reset
can be specified as --soft
, --mixed
or --hard
for different undo/redo needs.
What is the difference between git reset --soft, --mixed and --hard?
With the git reset
command:
-
git reset --soft
: Moves HEAD to the previous commit, but leaves staging area and working directory unchanged. -
git reset --mixed
(default): Moves HEAD to the previous commit and updates the staging area, but leaves working directory unchanged. -
git reset --hard
: Moves HEAD to the previous commit, updates the staging area and removes all changes from the working directory to match the commit specified.
Can I recover a commit after using git reset --hard?
Yes, to recover a commit after using git reset --hard
, use git reflog
to find the commit hash and restore it using git checkout -b NewBranchName CommitHashYouDestroyed
.