Reverting a merge

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.

Reverting a merge

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.

Getting ready

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

How to do it...

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:

  1. Let's take a look at history, the latest five commits, and find the merge commit:
    $ 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
    
  2. Revert the merge, keeping the history of the first parent:
    $ 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
    
  3. Let's have a look at the contents of our new 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.

How it works...

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.

There is more...

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!

See also

For more information on reverting merges, refer to the following articles:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset