I am trying to revert a merge, but I don't know whether to use git revert -m 1 <merge commit's sha>
or git revert -m 2 <merge commit's sha>
. How do I find out which parent is -m 1
and which parent is -m 2
?
Well, the super-short answer is that it's always -m 1
. :-) But this deserves some explanation:
The parents are ordered, and commands like
git log
andgit show
show the order:commit c13c783c9d3d7d3eff937b7bf3642d2a7fe32644 Merge: 3f7ebc6ec 39ee4c6c2
so here
3f7ebc6ec
is parent #1 and39ee4c6c2
is parent #2.The suffix
^
operation takes these same values:$ git rev-parse c13c783c9d3d7d3eff937b7bf3642d2a7fe32644^1 3f7ebc6ece46f1c23480d094688b8b5f24eb345c
(and of course
...^2
would be the other one).Programs that draw the graph, including
git log --graph
, will show you how these connect.But most importantly, the first parent of any merge is the commit that was current when you made the merge.
In particular, this means that if you are on branch main
and run git merge sidebranch
, the commit you make now (if all goes well) or eventually (if you must resolve the merge by hand) has, as its first parent, the previous tip of the main
branch. Its second parent is therefore the tip of sidebranch
.
Suppose, then, that we have this to start with:
...--o--*--A--B------C <-- main
\
D--E--F--G--H <-- sidebranch
when we run git merge
. The common base commit is *
, and Git makes a new merge commit M by doing, in essence:
git diff * C
(what did we change?)git diff * H
(what did they change?)
and then combining these two sets of changes and applying those to *
, giving us this final result:
...--o--*--A--B------C--M <-- main
\ /
D--E--F--G--H <-- sidebranch
Now, if everything changed in A-B-C
is completely independent of everything in changed in D-E-F-G-H
, it would not matter too much precisely how Git did the revert, as long as it kept the A-B-C
changes while ditching the D-E-F-G-H
changes.
But what if B
is mostly the same as F
, i.e., both B
and F
fix a bug? In that case, we don't want to undo the shared changes from B
and F
, that Git took one copy of. This is where the -m 1
part comes in.
When git revert
goes to undo some changes, it run its own git diff
. The git diff
it runs compares the commit you want to revert, to its parent. For any ordinary non-merge commit, this is easy: compare B
vs A
, or E
vs D
, or whatever, to see what happened, and back it out. With a merge commit, though, it's not obvious which parent to compare to (except that it kind of is :-) ). The first parent here is C
, so let's see what we get if we run:
git diff C M
The changes between C
and M
are those we picked up by adding the changes from D-E-F-G-H
to the changes we already had in A-B-C
, if we compare M
vs *
. In other words:
If
B
andF
overlap 100%, the changes inC
-vs-M
areD-E-G-H
: everything except the overlap. So we wind up reverting just those.If
F
has more changes in it thanB
, the changes inC
-vs-M
areD-E-(some-of-F)-G-H
: We wind up reverting those changes, but not the ones inB
.If
F
has fewer changes in it thanB
, the changes inC
-vs-M
are justD-E-G-H
again, and we wind up reverting just those.
Since the first parent is C
, and we want to back out the D-E-F-G-H
changes (excluding any we already had via A-B-C
), we want -m 1
in this revert.
© 2022 - 2024 — McMap. All rights reserved.