Merge commits are a special case when it comes to revert. In order to be able to revert a merge commit, you'll have to specify which parent side of the merge you want to keep. However, when you revert a merge commit, you should keep in mind that though reverting will undo the changes to files, it doesn't undo history. This means that when you revert a merge commit, you declare that you will not have any of the changes introduced by the merge in the target branch. The effect of this is that the subsequent merges from the other branch will only bring in changes of commits that are not ancestors of the reverted merge commit.
In this example, we will learn how to revert a merge commit, and we'll learn how we can merge the branch again, getting all the changes merged by reverting the reverted merge commit.
Again, we'll use the hello world
repository. Make a fresh clone of the repository, or reset the master
branch if you have already cloned.
We can create a fresh clone as follows:
$ git clone https://github.com/dvaske/hello_world_cookbook.git $ cd hello_world_cookbook
We can reset the existing clone as follows:
$ cd hello_world_cookbook $ git checkout master $ git reset --hard origin master HEAD is now at 3061dc6 Adds Java version of 'hello world'
In this example, we also need to use some of the other branches in the repository, so we need to create them locally:
$ git branch -f feature/p-lang origin/feature/p-lang Branch feature/p-lang set up to track remote branch feature/p-lang from origin. $ git checkout develop Switched to branch 'develop' Your branch is up-to-date with 'origin/develop'. $ git reset --hard origin/develop HEAD is now at a95abc6 Adds Groovy hello world
On the develop
branch, we have just checked out that there is a merge commit that introduces "hello world" programs from languages that start with P
. Unfortunately, the Perl version doesn't run:
$ perl hello_world.pl Can't find string terminator '"' anywhere before EOF at hello_world.pl line 3.
The following steps will help you revert a merge:
$ git log --oneline --graph -5 * a95abc6 Adds Groovy hello world * 5ae3beb Merge branch 'feature/p-lang' into develop | | * 7b29bc3 php version added | * 9944417 Adds perl hello_world script * | ed9af38 Hello world shell script |/
The commit we are looking for is 5ae3beb Merge branch 'feature/p-lang' into develop
; this adds the commits for "hello world" in Perl and PHP to the develop
branch. We would like the fix of the Perl version to happen on the feature
branch, and then merge it to develop
when ready. In order to keep develop
stable, we need to revert the merge commit that introduced the faulty Perl version. Before we perform the merge, let's just have a look at the contents of HEAD
:
$ git ls-tree --abbrev HEAD 100644 blob 28f40d8 helloWorld.groovy 100644 blob 881ef55 hello_world.c 100644 blob 5dd01c1 hello_world.php 100755 blob ae06973 hello_world.pl 100755 blob f3d7a14 hello_world.py 100755 blob 9f3f770 hello_world.sh
$ git revert -m 1 5ae3beb [develop e043b95] Revert "Merge branch 'feature/p-lang' into develop" 2 files changed, 4 deletions(-) delete mode 100644 hello_world.php delete mode 100755 hello_world.pl
HEAD
state:$ git ls-tree --abbrev HEAD 100644 blob 28f40d8 helloWorld.groovy 100644 blob 881ef55 hello_world.c 100755 blob f3d7a14 hello_world.py 100755 blob 9f3f770 hello_world.sh
The Perl and PHP files introduced in the merge are gone, so the revert did its job.
The revert
command will take the patches introduced by the commit you want to revert and apply the reverse/anti patch to the working tree. If all goes well, that is, there are no conflicts, a new commit will be made. While reverting a merge commit, only the changes introduced in the mainline (the -m
option) will be kept, and all the changes introduced in the other side of the merge will be reverted.
Though it is easy to revert a merge commit, you might run into issues if you later want to the branch again because the issues on the merge have not been fixed. When revert of the merge commit is performed, you actually tell Git that you do not want any of the changes that the other branch introduced in this branch. So, when you try to merge in the branch again, you will only get the changes from the commits that are not ancestors of the reverted merge commit.
We will see this in action by trying to merge the feature/p-lang
branch with the develop
branch again:
$ git merge --no-edit feature/p-lang CONFLICT (modify/delete): hello_world.pl deleted in HEAD and modified in feature/p-lang. Version feature/p-lang of hello_world.pl left in tree. Automatic merge failed; fix conflicts and then commit the result.
We can solve the conflict just by adding hello_world.pl
:
$ git add hello_world.pl $ git commit [develop 2804731] Merge branch 'feature/p-lang' into develop
Let's check the tree if everything seems right:
$ git ls-tree --abbrev HEAD 100644 blob 28f40d8 helloWorld.groovy 100644 blob 881ef55 hello_world.c 100755 blob 6611b8e hello_world.pl 100755 blob f3d7a14 hello_world.py 100755 blob 9f3f770 hello_world.sh
The hello_world.php
file is missing, but this makes sense as the change that introduced it was reverted in the reverted merge commit.
To perform a proper re-merge, we first have to revert the reverting merge commit; this can seem a bit weird, but it is the way to get the changes from before the revert back into our tree. Then, we can perform another merge of the branch, and we'll end up with all the changes introduced by the branch we're merging in. However, we first we have to discard the merge commit we just made with a hard reset:
$ git reset --hard HEAD^ HEAD is now at c46deed Revert "Merge branch 'feature/p-lang' into develop"
Now, we can revert the reverting merge and re-merge the branch:
$ git revert HEAD [develop 9950c9e] Revert "Revert "Merge branch 'feature/p-lang' into develop"" 2 files changed, 4 insertions(+) create mode 100644 hello_world.php create mode 100755 hello_world.pl $ git merge feature/p-lang Merge made by the 'recursive' strategy. hello_world.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
Let's check the tree for the Perl and PHP files, and see whether the Perl file has been fixed:
$ git ls-tree --abbrev HEAD 100644 blob 28f40d8 helloWorld.groovy 100644 blob 881ef55 hello_world.c 100644 blob 5dd01c1 hello_world.php 100755 blob 6611b8e hello_world.pl 100755 blob f3d7a14 hello_world.py 100755 blob 9f3f770 hello_world.sh $ perl hello_world.pl Hello, world!
For more information on reverting merges, refer to the following articles: