初学者あるあるだと思いますが、
「間違えてPRをマージしちゃった!」となることは珍しくありません。
しかも、マージのされ方(Fast-Forwardかどうか)によって、適切な戻し方が異なります。
本記事では、状況別の対処法と、よく遭遇する -m option エラーの解決策をまとめます。
1. Fast-Forward (FF) マージを戻したい場合
Fast-Forwardマージとは、枝分かれがない状態で「ポインタを先に進めただけ」のマージです。この場合、マージコミットが作成されません。
まだ Push していない場合
git reset を使うのが最も簡単で履歴も綺麗です。
# 作業中の変更を退避(もしあれば)
git stash
# マージ直前の状態(ORIG_HEAD)に強制的に戻す
git reset --hard ORIG_HEAD
すでに Push してしまった場合
一度公開した履歴を reset で消すのは、他のメンバーのローカル環境と不整合を起こすため(強制 Push が必要になり)危険です。そのため、基本的には revert を検討します。
ただし、FFマージは単にコミットが一直線に並んでいるだけなので、以下のいずれかの対応が必要です。
-
マージされたコミットを一つずつ revert する
git revert <commit_id_1> <commit_id_2> ... -
マージ前の状態を新規コミットとして上書きする
マージ前の状態(ORIG_HEAD)の内容で現在のワークツリーを上書きし、新しいコミットを作ります。git checkout ORIG_HEAD -- . git commit -m "Revert fast-forward merge"
2. マージコミット(非FFマージ)を戻したい場合
履歴が分岐していたり、--no-ff オプションでマージした場合は「マージコミット」が作成されます。これを revert しようとすると、以下のエラーが出ることがあります。
遭遇するエラー
error: commit <hash> is a merge but no -m option was given.
原因
マージコミットには親が2つ(マージ先とマージ元)あります。Gitは「どちらの親の状態に戻せばいいのか(どちらをベースラインとして残すか)」が判断できないため、エラーを出します。
解決策: -m 1 をつける
-m 1 オプションを付けて、マージ先のブランチ(元々自分がいたメイン側のブランチ)の状態に戻すよう指示します。
# -m 1 は「マージされた側」ではなく「マージした先」の履歴を優先する指定
git revert -m 1 <マージコミットのハッシュ>
3. 【重要】マージを revert した後の注意点
マージコミットを revert で打ち消すと、Gitの履歴上は
「そのブランチの内容は一度取り込まれた後に、削除された」
という扱いになります。
そのため、修正後に
同じトピックブランチを再度マージしようとしても、中身が反映されない(Already up to date と言われる)
という罠があります。
再度マージしたい時の対処法
- トピックブランチ側で、内容を修正して新しいコミットを作る
- または、以前行った 「revert コミット」自体を revert する(=打ち消しの打ち消しを行うことで、再びマージした内容を有効化する)
まとめ:判断フロー
| 状況 | 推奨コマンド | 備考 |
|---|---|---|
| FFマージ & Push前 | git reset --hard ORIG_HEAD |
履歴が一番綺麗 |
| FFマージ & Push後 | git revert <各コミットID> |
コミットが多いと大変 |
| マージコミットあり | git revert -m 1 <hash> |
-m 1 が必須 |
「マージを戻す」作業を行う際は、まず git log --graph で現在の履歴の状態を確認することをお勧めします。