Git revert
If you've ever pushed a series of public changes to a repository and then realized there is a problem with one of the commits, git revert is a way to cleanly "undo" one or more of those changes. git revert
creates a new commit that's the exact opposite of the given SHA. Anything added in the old commit will be removed in the new commit and anything removed in the old commit will be added back in the new commit.
Let's see how this works with the following example repository.
$ git tree
* 662b249 Nineth commit
* 858b9a1 Eigth commit
* 2ee1496 Seventh commit
* 4ee6fb6 Sixth commit
* 60b949b Merge branch 'feature-branch'
|\
| * 5e9c10e Fifth commit
| * 8e7c992 Fourth commit
|/
* 787608b Third commit
* abe5f9b Second commit
* a44ce08 First commit
In this scenario, you recently discover an issue with SHA 2ee1496
, the 'Seventh commit'. The repository is public so you can't simply do a git reset
or git reset --hard
because with would rewrite the history.
Let's use git revert
to undo the commit instead.
$ git revert 2ee1496
By default, unless you specific the -n
or --no-commit
option, the command automatically launches the text editor and creates a commit log message stating which commits are being reverted.
Revert "Seventh commit"
This reverts commit 2ee14969e39d09e0b25f8561de3b9f5d95dae1b9.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# deleted: file7.txt
#
Saving the file and exiting the editor creates the actual commit.
$ git revert 2ee1496
[master d6a2eae] Revert "Seventh commit"
1 file changed, 1 deletion(-)
delete mode 100644 file7.txt
Viewing the log we see a new commit and message has been added. It is important to note that we have not changed any history at this point. The original SHA 2ee1496
commit is still there, however, our new SHA d6a2eae
commit has undone what the original one did.
$ git tree
* d6a2eae Revert "Seventh commit"
* 662b249 Nineth commit
* 858b9a1 Eigth commit
* 2ee1496 Seventh commit
* 4ee6fb6 Sixth commit
* 60b949b Merge branch 'feature-branch'
|\
| * 5e9c10e Fifth commit
| * 8e7c992 Fourth commit
|/
* 787608b Third commit
* abe5f9b Second commit
* a44ce08 First commit
SHA 2ee1496
originally added the file7.txt
text file and if we list our directory we can see the file is no longer there.
$ ls -l
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:34 file1.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:35 file2.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:38 file3.txt
-rw-r--r-- 1 Wayne staff 22 Jan 17 15:40 file4.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:40 file5.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:42 file6.txt
-rw-r--r-- 1 Wayne staff 22 Jan 17 15:44 file8.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:45 file9.txt
That was easy, however, what if the commit you want to revert is the result of a merge? Let's attempt to revert SHA 60b949b
which was a merge of our 'feature-branch' and see what happens.
$ git revert 60b949b
error: Commit 60b949b1cbabf0d5519bba9fb7ad5386fd58e18c is a merge but no -m option was given.
fatal: revert failed
What happened? In order for git to revert a merge it needs to know which side of the merge should be considered the mainline. The -m
or --mainline
option allows you to specify the parent number starting from 1. The revert will then reverse the change relative to the specified parent. Let's do a git show
on our merge commit and review the information it provides.
$ git show 60b949b
commit 60b949b1cbabf0d5519bba9fb7ad5386fd58e18c
Merge: 787608b 5e9c10e
Author: Wayne Dyck <Wayne@localhost>
Date: Sun Jan 17 15:40:35 2016 -0800
Merge branch 'feature-branch'
Next to the "Merge:" label you will see the 787608b 5e9c10e
SHAs which are the two parent mainlines that make up this merge commit. The first one is from the master mainline and the second from the 'feature-branch' mainline. In this case we want to reference SHA 787608b
as the first parent by using the -m 1
option.
$ git revert -m 1 60b949b
Now that we've told git which parent mainline to reference the text editor again launches and creates a commit log message stating which commits are being reverted.
Revert "Merge branch 'feature-branch'"
This reverts commit 60b949b1cbabf0d5519bba9fb7ad5386fd58e18c, reversing
changes made to 787608b8e83a55ed44fcdc1c4ac459f0791bde34.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# deleted: file4.txt
# deleted: file5.txt
#
Saving the file and exiting the editor creates the commit.
$ git revert -m 1 60b949b
[master ca39bb5] Revert "Merge branch 'feature-branch'"
2 files changed, 2 deletions(-)
delete mode 100644 file4.txt
delete mode 100644 file5.txt
Viewing the log again we see the new commit and message that has been added.
$ git tree
* ca39bb5 Revert "Merge branch 'feature-branch'"
* d6a2eae Revert "Seventh commit"
* 662b249 Nineth commit
* 858b9a1 Eigth commit
* 2ee1496 Seventh commit
* 4ee6fb6 Sixth commit
* 60b949b Merge branch 'feature-branch'
|\
| * 5e9c10e Fifth commit
| * 8e7c992 Fourth commit
|/
* 787608b Third commit
* abe5f9b Second commit
* a44ce08 First commit
Listing the directory you will see that file4.txt
and file5.txt
, which were added in our 'feature-branch' merge commit, are no longer there.
$ ls -l
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:34 file1.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:35 file2.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:38 file3.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:42 file6.txt
-rw-r--r-- 1 Wayne staff 22 Jan 17 15:44 file8.txt
-rw-r--r-- 1 Wayne staff 21 Jan 17 15:45 file9.txt