How to revert a git merge

You have merged a branch to your current branch and a few commits later you realize that you want to revert that merge. How do you do it?

setup

Let’s suppose you have two branches: main and b1.

alina

4d2cd8882d43ecbcada984fa0b70ab0bacb213ff64416c761b1mainHEAD

On your main branch, you have the following files: A, B, and C:

alina

alina git:( main ) ls

A.txt     B.txt     C.txt     README.md

alina git:( main )

And on the b1 branch, you have the following files: 1, 2, and 3:

alina

alina git:( main ) git switch b1

Switched to branch 'b1'

alina git:( b1 ) ls

1.txt     2.txt     3.txt     README.md

alina git:( b1 )

Back on the main branch, you merge b1:

alina

alina git:( b1 ) git switch main

Switched to branch 'main'

Your branch is ahead of 'origin/main' by 3 commits.

  (use "git push" to publish your local commits)

alina git:( main ) git merge b1

Merge made by the 'ort' strategy.

 1.txt | 1 +

 2.txt | 1 +

 3.txt | 1 +

 3 files changed, 3 insertions(+)

 create mode 100644 1.txt

 create mode 100644 2.txt

 create mode 100644 3.txt

alina git:( main )

This gives you the following commit graph:

alina

7a0f2034d2cd8882d43ecbcada984fa0b70ab0bacb213ff64416c761b1mainHEAD

Now you have the files A, B, C, 1, 2, and 3:

alina

alina git:( main ) ls

1.txt     2.txt     3.txt     A.txt     B.txt     C.txt     README.md

alina git:( main )

The git log command shows you the commit graph:

alina

alina git:( main ) git log --graph --oneline --all

*   7a0f203 (HEAD -> main) Merge branch 'b1'

|\  

| * 4d2cd88 (b1) add 3.txt

| * 82d43ec add 2.txt

| * bcada98 add 1.txt

* | 4fa0b70 add C.txt

* | ab0bacb add B.txt

* | 213ff64 add A.txt

|/  

* 416c761 (origin/main, origin/HEAD) Initial commit

alina git:( main )

You then add a new commit:

alina

alina git:( main ) echo "x" > x.txt; git add x.txt; git commit -m 'add x.txt'

[main 1438321] add x.txt

 1 file changed, 1 insertion(+)

 create mode 100644 x.txt

alina git:( main )

And you get this commit graph:

alina

14383217a0f2034d2cd8882d43ecbcada984fa0b70ab0bacb213ff64416c761b1mainHEAD

Now you have the following files:

alina

alina git:( main ) ls

1.txt     2.txt     3.txt     A.txt     B.txt     C.txt     README.md x.txt

alina git:( main )

git revert -m 1

You realize that the merge was a bad idea, and you decide to revert the merge commit 7a0f203:

alina

alina git:( main ) git revert 7a0f203

error: commit 7a0f2030dd4a8b3670b49a8eb9b45dd1460310df is a merge but no -m option was given.

fatal: revert failed

alina git:( main )

Git is unhappy and gives an error message.

The reason why Git is unhappy is that you are trying to revert a merge commit without specifying which parent you want to revert to.

A Git revert is a three-way merge between your current commit (HEAD) and the parent of the commit you are reverting (the commit you are reverting to), where the base is the commit you are reverting.

That means that Git needs to know which parent you want to revert to.

The git show command shows you the parents of the commit 7a0f203:

alina

alina git:( main ) git show 7a0f203

commit 7a0f2030dd4a8b3670b49a8eb9b45dd1460310df

Merge: 4fa0b70 4d2cd88

Author: alina <alina@email.com>

Date:   Sat Jul 1 08:42:54 2023 -0700

    Merge branch 'b1'

alina git:( main )

The Merge: line indicates that we have a merge commit, and the two hashes after the Merge: line are the hashes of the parents of the merge commit.

The order of those hashes is important:

  • Parent #1 is the commit of the branch you were on when you did the merge.
  • Parent #2 is the commit of the branch you merged.

If you want to revert to the state of the branch you were on when you did the merge, you need to use the parent #1 hash.

alina

alina git:( main ) git revert 7a0f203 -m 1

[main 6b121fa] Revert "Merge branch 'b1'"

 3 files changed, 3 deletions(-)

 delete mode 100644 1.txt

 delete mode 100644 2.txt

 delete mode 100644 3.txt

alina git:( main )

That brings you back to the state of the main branch you were on when you did the merge, but it keeps the changes made since then. The file x.txt is still there:

alina

alina git:( main ) ls

A.txt     B.txt     C.txt     README.md x.txt

alina git:( main )

And your commit graph is:

alina

6b121fa14383217a0f2034d2cd8882d43ecbcada984fa0b70ab0bacb213ff64416c761b1mainHEAD

git revert -m 2

Let’s go back to the state after the merge and the commit adding the x file:

We have the following files:

alina

alina git:( main ) ls

1.txt     2.txt     3.txt     A.txt     B.txt     C.txt     README.md x.txt

alina git:( main )

And the commit graph is:

alina

b4a552ff970398c11694283b09d3cf81b85daa69b11de119564da50a0a11992b1mainHEAD

The commit tree was recreated completely so the commit hashes are different from the previous example.

The new merge commit is f970398:

alina

alina git:( main ) git show f970398

commit f970398069ccb6a8340ee52ec4673bb3d9c9806b

Merge: daa69b1 c116942

Author: alina <alina@email.com>

Date:   Sat Jul 1 09:22:42 2023 -0700

    Merge branch 'b1'

(END)



(END)

alina git:( main )

Usually, git revert -m 1 is what you want to do, but sometimes you want to revert to the state of the branch you merged.

Let’s try this time to revert the merge by restoring the state of the branch we merged:

alina

alina git:( main ) git revert f970398 -m 2

[main c4ce39c] Revert "Merge branch 'b1'"

 3 files changed, 3 deletions(-)

 delete mode 100644 A.txt

 delete mode 100644 B.txt

 delete mode 100644 C.txt

alina git:( main )

This time, the files A, B, and C are deleted as they were the files on the main branch before the merge.

alina

alina git:( main ) ls

1.txt     2.txt     3.txt     README.md x.txt

alina git:( main )

The commit graph is:

alina

c4ce39cb4a552ff970398c11694283b09d3cf81b85daa69b11de119564da50a0a11992b1mainHEAD