Wondering how to undo a git commit? Follow our step-by-step guide on the various methods you use to undo a commit. Some of the methods discussed include the git revert and the git reset command.
Git is one of the most common and versatile version control systems VCS, but it is not always simple and easy to use. You can run into trouble when you commit an undesirable change to a repository. There are several different strategies you can follow to restore your repository. This guide discusses how to undo a git commit and explains the advantages and any drawbacks to each approach.
How to Undo a Git Commit
There are several ways to use git to undo a local commit. The right choice depends on the following factors:
- The changes might be required in the future. Some options preserve the commit while others completely erase it.
- A clean commit log is considered advantageous.
- The changes have been committed to the remote repository or not. If someone has pushed the changes already using
git push
, there are fewer options available.
The most common methods of using git to remove the last commit are git revert
or some variant of git reset
. However, an organization might have standard best practices regarding what command should be used. More detailed information about all these commands can be found in the Git documentation.
Reviewing Previous Commits Using Git Log
Before undoing any commits in Git, familiarize yourself with the change history of any relevant files. The git log <filename>
command displays the entire history of filename
, but git log --oneline
is usually more readable and useful. It displays the commit identifier and the comments for each commit together on a single line. The following command displays all previous commits to testfile1.txt
and identifies the HEAD
version of the file.
git log --oneline testfile1.txt
Output:
6f819a796 (HEAD -> git-test) Second revision of file
705dfa037 Initial draft of file
Undoing a Git Commit Using a Soft Reset
The git reset
command is a handy way to undo recent changes and restore files to an earlier version. The reset
command can be used for a hard reset, a soft reset, or a mixed reset which falls somewhat in the middle. Many experts advise against using git reset
as it throws away the changes, but in many cases this is not a problem.
The git reset --soft <version>
command resets the HEAD
commit on a local branch to the previous commit. The specified version
becomes the current commit and the HEAD
version in the log. More recent commits are discarded, along with their history. However, this command preserves all changes in the working directory and the staging area. The changes can be subsequently committed when they are ready. Although the command is typically used to rewind to the previous commit, an earlier version can be specified. So git reset --soft HEAD~3
restores the HEAD
of the local repository to the third-most recent commit.
A soft reset is the best choice for unwinding accidental commits. A good example is when a file is committed before all testing is complete or if some files are unintentionally omitted. A new version can be committed later when all changes are complete. This updates the repository and history with the correct commit.
If the changes have already been pushed to the remote repository, this approach is not suitable. git reset
does not update the remote file, so the local workspace is now out of sync. The next time files are fetched, the unwanted commit is erroneously restored.
The following example demonstrates how a soft reset might be used on testfile1.txt
.
Review the file contents before the soft reset.
First line of text.
Second line of text for check-in 2.
Third line of text for check-in 3.
Verify the Git log for the file. It displays three commits.
git log --oneline testfile1.txt
Output:
73536b193 (HEAD -> git-test) Third revision of file
6f819a796 Second revision of file
705dfa037 Initial draft of file
Execute a soft reset using the commit that immediately preceded the current version. This version is HEAD~1
, which is an alias for the second most recent commit.
git reset --soft HEAD~1
Note: It is possible to undo multiple commits. Use either the commit identifier or the HEAD~n
notation to identify the commit.
The log now reflects the results of the reset operation. The earlier version has become the current version. This version is now the HEAD
.
git log --oneline testfile1.txt
Output:
6f819a796 (HEAD -> git-test) Second revision of file
705dfa037 Initial draft of file
Verify the state of the file using git status
. The output confirms the changes are still staged and ready for another commit.
git status testfile1.txt
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: testfile1.txt
The contents of the local directory are also unchanged.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Third line of text for check-in 3.
Undoing a Git Commit Using a Hard Reset
The git reset
option also has a --hard
option. git reset --hard <version>
also rewinds the HEAD
version to the specified version the same way a soft reset does. The earlier commits are still removed from the log and the local repository. However, in a hard reset the changes are also removed from both the working directory and the Git index. This means any changes made to the file after the target version are lost.
This command should be used whenever the recent commit is incorrect, not simply incomplete or too early. It is the right choice to permanently undo changes that are not intended for a future commit. As with a soft reset, this approach is not suitable for a file that has been pushed to a remote repository.
The following example demonstrates how a hard reset results in a different outcome than a soft reset.
Before the reset
operation, the log of the current file displays three commits.
git log --oneline testfile1.txt
Output:
3aa5652f2 (HEAD -> git-test) Third revision of file. Take 2
6f819a796 Second revision of file
705dfa037 Initial draft of file
The working copy of the file displays the text from the recent commit.
First line of text.
Second line of text for check-in 2.
Third line of text for check-in 3.
To undo the changes, perform a hard reset using the --hard
option. Specify HEAD~1
to revert to the commit preceding the current commit. If the reset is successful, Git displays information about the current version.
git reset --hard HEAD~1
Output:
HEAD is now at 6f819a796 Second revision of file
View the log to confirm the older commit is now the current version. Information about the third commit has disappeared.
git log --oneline testfile1.txt
Output
6f819a796 (HEAD -> git-test) Second revision of file
705dfa037 Initial draft of file
This time, the commit no longer appears in the git index.
git status git testfile1.txt
Output:
On branch git-test
nothing to commit, working tree clean
The commit has also been undone in the working file. It reflects the contents of the HEAD~1
commit.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Undoing a Git Commit Using a Mixed Reset
The command git reset --mixed <version>
performs a mixed reset. In addition to rolling the local repository back to the specified version, it removes changes associated with the commit from the staging area. This is equivalent to undoing both the git add
and git commit
commands on the file. However, it is similar to the soft reset because it does not remove the changes from the working directory. This makes it easier to add back the changes and commit them again in the future. The mixed mode is the default setting for a reset if no options are specified.
A mixed reset is typically used to undo a commit that still requires some minor adjustments. When the file is corrected, it can be added and committed again. It can also be used to remove a new file that was not yet ready from the staging area. If there is no chance of the changes ever being needed again, a hard reset should be used.
The following example demonstrates how a mixed reset differs from the other two approaches.
At first, the contents of the file reflect the contents of the three commits.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Third attempt at line 3.
Run the git log
command to see the commit history.
git log --oneline testfile1.txt
Output:
cbe82f1a6 (HEAD -> git-test) Third revision of file. Take 3
6f819a796 Second revision of file
705dfa037 Initial draft of file
Undo the changes using the git reset --mixed
command. Identify the version to roll back to. In this case, it is HEAD~1
.
git reset --mixed HEAD~1
Output:
Unstaged changes after reset:
M docs/guides/development/tips-and-tricks/testfile1.txt
The output from git log
confirms the most recent commit has been undone.
git log --oneline testfile1.txt
6f819a796 (HEAD -> git-test) Second revision of file
705dfa037 Initial draft of file
The commit is no longer staged.
git status testfile1.txt
Output:
On branch git-test
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: testfile1.txt
However, all changes remain in the working directory. The contents of testfile1.txt
are unchanged.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Third attempt at line 3.
Note: git reset
can be used on a specific file to move a particular commit to the staging area. The syntax for this command is git reset HEAD <filename>
. Although it can be used with any version from the repository, it is typically used with the HEAD
version. In this case, it has the effect of aligning the local repository and the staging area. This is an efficient method of undoing uncommitted changes to the staging area. Subsequent changes must be staged again using git add
before they can be committed.
Undoing Git Changes Using Git Revert
git revert
is used to completely revert a commit without deleting it. Many experienced Git users prefer this approach over a reset. The main difference is a revert
undoes the changes using a new commit. It compares the commit with the version that precedes it to determine the necessary changes. It also adds a new entry in the commit log to document the reversion.
A revert
does not throw away or overwrite any earlier commits. While git reset
removes all records of the undesirable commit, git revert
generates an entirely new commit to accomplish the same undo operation. The HEAD
of the file is set to the new version that inverts the changes. This version of the file becomes the current version. In all cases, specify the commit to undo as an argument. To roll back the most recent commit, use git revert HEAD
. This command rolls back the entire commit at once, so several files can be affected at the same time.
The main advantage of git revert
is it maintains the history of all commits. This is handy for debugging and quality control purposes. It is also the best approach if the commit has already been pushed to the remote repository. It allows the revert operation to be pushed to the shared repository and undoes the change there too. This approach is the easiest and cleanest way to fix a broken build and is always safe to use. Many organizations only allow changes to be backed out using this method.
To use Git to revert a commit, follow these steps.
Verify the file history and contents to determine the change to revert.
git log --oneline testfile1.txt
Output:
34722a3fd (HEAD -> git-test) Third revision of file. Take 4
6f819a796 Second revision of file
705dfa037 Initial draft of file
Example:
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Third attempt at line 3.
Apply git revert
to the last commit. HEAD
is an alias for the more recent commit.
git revert HEAD
Output:
[git-test 606638205] Revert "Third revision of file. Take 4"
1 file changed, 1 deletion(-)
The log reflects a new commit which inverts the third commit. This restores the contents of the file to the second revision. Commits 606638205
and 6f819a796
are exactly the same.
git log --oneline testfile1.txt
Output:
606638205 (HEAD -> git-test) Revert "Third revision of file. Take 4"
34722a3fd Third revision of file. Take 4
6f819a796 Second revision of file
705dfa037 Initial draft of file
All changes are removed from the staging directory.
git status testfile1.txt
Output:
On branch git-test
nothing to commit, working tree clean
The contents of the working copy also change. They reflect the current version of the file in the local repository.
cat testfile1.txt
Output
First line of text.
Second line of text for check-in 2.
Using Git Checkout
The git checkout
command is typically used to switch to another branch. However, it can also be used to view earlier commits to a file. Later on, the user can restore the current version of the file using another git checkout
.
When used with a specific commit identifier, git checkout <commit_id>
allows developers to checkout and view a specific snapshot of the file. The checked out version is technically independent of any branch and exists in a branchless state. HEAD
now points to this version, but another newer commit is still the current version for the branch. This creates what is known as a detached head condition, which is something to be concerned about. If any further commits or changes are made to this file, they are detached from the current state of the branch. Any subsequent checkout
results in these changes becoming orphaned and unusable. So this approach is of limited use for rolling back a recent commit.
However, it is possible to directly create a new branch based on the earlier commit. While this older commit is still checked out, create the new branch using git checkout -b <new-branch-name>
. The older version of the file automatically serves as the base for the new stream. This allows new development to use an older version of the repository as a starting point.
The checkout
command overwrites any local changes. To save local changes, ensure the file is backed up or stashed before proceeding with the checkout.
Note: When used on a branch, git checkout
checks out the current contents of a branch on the local system. All subsequent commits are made against this branch. This is the main method of switching between branches in a repository. There is no concern about orphaned commits or a detached HEAD
in this case, because HEAD
still represents the current version.
To use git checkout
, follow these steps.
Review the file history using git log
. Locate the version to review.
git log --oneline testfile1.txt
Output:
e3d35e244 (HEAD -> git-test) Third revision of file. Take 5
606638205 Revert "Third revision of file. Take 4"
34722a3fd Third revision of file. Take 4
6f819a796 Second revision of file
705dfa037 Initial draft of file
Use git checkout
to view the contents of commit 606638205
. Git confirms the checkout and warns the user they are in a detached HEAD
state. It also displays a warning about the checkout and explains how to undo the git checkout
operation.
git checkout 606638205
Output:
Note: switching to '606638205'.
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 switching back to a branch.
If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command.
Example:
git switch -c <new-branch-name>
Or undo this operation with:
git switch -
Output:
HEAD is now at 606638205 Revert "Third revision of file. Take 4"
Verify the status of the file and ensure it displays the detached head state. It does not belong to any branch.
git status testfile1.txt
Output:
HEAD detached at 606638205
nothing to commit, working tree clean
The contents of the file reflect the checked out version.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
The log has changed and now tags the earlier version as the head.
git log --oneline testfile1.txt
Output:
606638205 (HEAD) Revert "Third revision of file. Take 4"
34722a3fd Third revision of file. Take 4
6f819a796 Second revision of file
705dfa037 Initial draft of file
At this point, it is possible to create a new branch based around the checked out version. However, the actual current version can be quickly restored using the git switch
command.
git switch -
Output:
Previous HEAD position was 606638205 Revert "Third revision of file. Take 4"
Switched to branch 'git-test'
Confirm the file is no longer in a detached head state.
git status testfile1.txt
Output:
On branch git-test
nothing to commit, working tree clean
The current contents of the file are restored to the working directory.
cat testfile1.txt
Output:
First line of text.
Second line of text for check-in 2.
Final attempt at new line.
Amending a Git Commit Message
If the files and changes in a git commit
operation were correct, but the comment was wrong, it can easily be amended. Use the command Git commit --amend -m <updated-message>
to update the message associated with the commit. This option can only be used to modify the most recent commit.
Note: This command must never be used to modify the message of a commit that has already been pushed to a remote server. This leaves the two repositories out of sync and is almost guaranteed to cause trouble in the future.
Other Related Git Commands
There are a couple of other similar Git commands that are useful in certain situations.
- The
git clean
command removes untracked files or directories that have not yet been added to the staging area or committed. It can be used to clean up a workspace and delete unwanted or forgotten files. - The
git rm
command is sometimes confused withrevert
orreset
, but it has a different purpose.git rm
is used to remove a file that is no longer required from the repository. However, the file history is preserved. This means the command can be undone using thereset
directive and earlier versions can still be retrieved for viewing.rm
is the inverse of thegit add
operation. The file is not removed from the local repository until the change is committed.
Conclusion
This guide explains how to undo a commit in Git. There are several Git commands that can undo a local commit. Choosing the most appropriate command depends on an understanding of how Git operates and the circumstances of the commit.