For developers, its very common to be in a situation to revert some git commits. In most cases, its as simple as running git revert <commit-sha>. However, when it comes to reverting merge commits, it gets a little confusing. Simply doing a revert on a merge commit will fail:

$ git revert 0992bc966c6e8fbde08454c27724c87a95d6d48b
error: commit 0992bc966c6e8fbde08454c27724c87a95d6d48b is a merge but no -m option was given.
fatal: revert failed

GIT is basically asking for the mainline number. In other words, asking for the parent number.

Commit parents

A commit’s parent is the commit on top of which the commit is applied. Take the following example:

  C3 *
     |
  C2 *
     |
  C1 *

C2 is C3’s parents, C1 is C2’s parent.

Merge commit

A Merge commit, unlike other commit have two parents. Take the following example:

                 M1  * (Change File1 and Change File2)
                     | \
  (Change File1) A5  *   * B3 (Change File2)
                     |   |
                 A4  *   * B2
                     |   |
                 A3  *   * B1
                     |  /
                 A2  *
                     |
                 A1  *

Branch B is checked out from A2 and later merged into Branch A with a merge commit M1. In this case, M1 has two parents.

  • Parent 1: A5 (Commit changed File1)
  • Parent 2: B3 (Commit changed File2)

If one is to revert the changes that were applied on Branch A because of the merge M1, they want the mainline as A5, so parent 1

$ git revert M1 -m 1

What GIT does is that is reverts the diff between M1 and A5, which are changes to File2. After the revert, Branch A will have only changes to File1.

When will I ever use parent 2 in revert or cherry-pick?

Lets take the case of a cherry-picking a merge commit. GIT will ask for the parent number just like it does for reverting a Merge.

From above example, A5 has changes to File1 and B3 has changes to File2. When cherry-picking M1 to a different branch, git has no way to know whether to apply the changes to File1 or File2.

If cherry-picking with Parent 1, GIT looks at the diff between M1 and A5 and applies that. In case of the above example, it will apply changes to File2 but not changes to File1

If cherry-picking with Parent 2, GIT looks at the diff between M1 and B3 and applies that. In case of the above example, it will apply changes to File1 but not changes to File2