git revert -m1 の -m は --mainline

TIL

merge commit は親が 2 つ (以上) あるので、revert するときはどちらの親に戻したいかを -m (--mainline) で指定する。

起きたこと

「意図せず feature branch を main に混ぜて push しまった」と呼ばれて飛び出したんだが、コミットツリーがこうなっていた。

525adcbf69 ●─╮ [main] {origin/main} Merge branch 'main' of ...
de745c8669 │ ∙ commit for main
2060e2be19 │ ∙ commit for main
813528befb ∙ │ commit for feature
9ed9cb80a9 ∙ │ commit for feature
9f8e1bb2e1 ∙ │ commit for feature

元々の main 最新は de745c8669 で、そこに feature branch を merge してしまったっぽい。

merge commit を revert して push すれば良いかなと思って

$ git revert -m1 525adcbf69

をしたんだが、何かおかしい。僕の意図としては de745c8669 と同じ状態になって欲しかったんだけど、813528befb と同じ状態になった。

revert で親のどっちにするのか選べたっけ、と man git-revert していたら -m parent-number, --mainline parent-number の記述が見えて、納得した。何も考えずに -m1 としたのが間違いで、-m2 とすべきだった。

git revert の -m

merge commit には親が 2 つある。

$ git show 525adcbf69
commit 525adcbf69824c1888504f5c07f69697baad5aa8
Merge: 813528befb de745c8669
...

このどちらの状態に戻すのかを指定するのが -m (--mainline) オプション。

なお、octopus merge の場合は 3 以上を使うこともある。

5580d0beb1 ●─┬─╮ [main] Merge branches 'foo', 'bar' and 'baz'
a5334bc232 │ │ ∙ [baz] Add baz
0b9531a81e │ ∙ │ [bar] Add bar
b54c490b75 ∙ │ │ [foo] Add foo
2dfb6b27be ◎─┴─╯ Initial commit

親が 3 つあるので

$ git show
commit 5580d0beb173c8e8695f4c0782e3620e93f13cb1
Merge: b54c490 0b9531a a5334bc
...

revert で 3 番目に戻す、とすると

$ git revert -m3 5580d0beb1
[main 2825475] Revert "Merge branches 'foo', 'bar' and 'baz'"
2 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 bar
delete mode 100644 foo

baz branch の状態になる。

感想

-m1 は merge commit を revert するときのおまじないとして覚えていたので、-m は merge の m ぐらいのつもりだったが、全然違った。

feature branch に main をマージした上で、branch 名を main にした (もしくは feature branch の push 先として main を指定してしまった)、というトラブルは git 生活 10 年超で初めて見て、面白かったです。