How to undo the latest local commit in Git repository
Undo the latest local commit in Git
So we’ve committed some changes by mistake and we want to revert it back. No worries, Git is here to help!
Depending on what we need, we can do one of the following:
Undo all changes in the last commit
To undo all changes made in the last commit, and go back to an earlier commit, we
can use git revert
.
Let’s say we have some repository, and we committed something by mistake:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log --oneline
cba9069 (HEAD -> master) Oops, this change is a mistake <-- We want to revert this commit
5b7794b Some stable changes
e57b197 Adds some content
187ea9d Initial commit
Then, to revert the latest commit, we can use git revert <commit sha>
:
1
2
3
git revert cba9069
This will pop up our favorite editor to enter the revert commit message and will create
a new commit, which reverts all changes in the given commit.
After the command is complete, the repository history will look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log --oneline
0ed5618 (HEAD -> master) Revert "Oops, this change is a mistake" <-- this reverts all changes from cba9069
cba9069 Oops, this change is a mistake
5b7794b Some stable changes <-- The final state of the repository will be the same as in this commit
e57b197 Adds some content
187ea9d Initial commit
As you can see, there is an additional commit that reverted our changes and the final state of the repository is
the same as the state it had in the previous commit (the commit before the one we wanted to revert).
This is useful when we want to keep the repository history and it is the recommended way of doing things. The upside
is that we haven’t lost any data, even if we change our mind later, we can always revert again or see the changes in the
reverted commit - it’s all there in the history.
Reset the changes, modify and commit again
Sometimes we don’t want to revert everything, but instead we need to make just a couple of changes and commit again.
In these situations we can use git reset --soft <commit-ref>
:
Given the same state of the repository:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log --oneline
cba9069 (HEAD -> master) Oops, this change is a mistake <-- We want to make some changes here again and commit
5b7794b Some stable changes
e57b197 Adds some content
187ea9d Initial commit
Then we can do:
1
2
3
git reset --soft HEAD^
This makes a soft reset to the commit that is parent of the current HEAD (the current HEAD is what we want to change).
Soft reset means that Git will undo the changes of the commit in the following way:
-
The HEAD of the repository will now point to
5b7794b Some stable changes
(HEAD^ - one up from current HEAD) -
The changes introduced in the faulty commit are not lost, but are staged in the repository
This gives us the opportunity to redo the changes and commit again.
Doing a git status
to see the changes we see something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
And the history log:
1
2
3
4
5
6
7
8
9
10
11
$ git log --oneline
5b7794b (HEAD -> master) Some stable changes
e57b197 Adds some content
187ea9d Initial commit
This is exactly what we wanted. After doing the changes we can commit:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ git commit -m "Fixed the commit"
$ git log --oneline
55293e6 (HEAD -> master) Fixed the commit
5b7794b Some stable changes
e57b197 Adds some content
187ea9d Initial commit
Getting rid of the commit completely
Sometimes we want to get rid of the commit completely. Maybe we have committed some sensitive data by mistake (like
an access key or password), and we want no trace of it in the history. In those cases, we can rebase the branch and
drop the commits we don’t want.
Again starting with the same repository, we can rebase on the parent commit of the good commit (going 2 commits back,
because we cannot drop a commit if there is just one commit in the rebase list):
1
2
3
4
5
6
7
8
9
10
11
12
13
$ git log --oneline
cba9069 (HEAD -> master) Oops, this change is a mistake <-- We want this gone
5b7794b Some stable changes
e57b197 Adds some content <-- we pass this commit to the rebase command, so we have 2 commits in the list
187ea9d Initial commit
Rebase:
1
2
3
git rebase -i e57b197
In the rebase editor:
1
2
3
4
5
pick 5b7794b Some stable changes <-- we want to keep this
drop cba9069 Oops, this change is a mistake <-- but we want to drop this
Save, and commit.
The final state of the repository:
1
2
3
4
5
6
7
8
9
$ git log --oneline
5b7794b (HEAD -> master) Some stable changes
e57b197 Adds some content
187ea9d Initial commit
And there are no changes staged for commit:
1
2
3
4
5
6
7
$ git status
On branch master
nothing to commit, working tree clean
Important Note: This will completely remove any trace of the commit, so once done, it is very hard to get
those changes back if we change our mind later on. We should use this approach only if we are certain that we
want that commit completely gone.
Final notes
When using approaches 2 and 3 (using reset
and rebase
), we’re making changes in the repository graph itself. While
locally this is absolutely fine, there might be a problem when we’re having a remote repository.
When the commit we want to revert is on the remote repository as well, then the two repositories will have different
graphs and we cannot push
our changes in a straight forward manner without Git complaining that the branches
have diverged.
Approach 1 however, when using git revert
, adds an additional commit, just like a regular commit, which then can
be easily pushed to the remote repository.