「マージ中にコンフリクト解決に失敗したのでやり直したい」「マージしたものの方向が間違っていた」「pushしたmergeを打ち消したい」——Gitでのマージ取り消しはマージがどの段階まで進んでいるかで手順が変わります。コンフリクト解決中の中断と、commit済みの巻き戻し、push済みの打ち消しでは使うコマンドが全く違います。
この記事では、Gitのマージを取り消す方法をマージの進行状態(3段階)で整理し、git merge --abort・git reset・git revert -mの正しい使い分けを解説します。fast-forward mergeやsquash merge、octopus mergeといった特殊ケース、そして「revert後に再度マージしても取り込まれない」問題への対処まで、実務で迷わない形でまとめます。
この記事で学べること
- マージの3段階(進行中/commit済み未push/push済み)と対応コマンド
git merge --abortで進行中マージを安全に中断する方法- 未push状態を
git resetで巻き戻す手順 - push済みmergeを
git revert -mで履歴を残して打ち消す手順 - fast-forward merge・squash merge・octopus mergeの取り消し
- revert後の再マージ問題と回避策(cherry-pick/revert of revert)
- 間違ったブランチにマージした場合のリカバリ
まず判定:マージは今どの段階か?
取り消し方を選ぶ前に、マージがどこまで進んでいるかを確認します。git statusのヒントが最初の目印です。
ポイント:判定のコツはgit statusとgit log --oneline -5。「unmerged paths」表示がなければマージは一旦完了しており、あとはリモートに出ているかで未push/push済みを分けます。git log origin/main..HEADが空でないなら未push、空ならpush済みと判断できます。
状態A:進行中のマージを中断する(merge –abort)
git mergeを実行してコンフリクトが発生し、まだgit commitしていない状態です。git statusで「You have unmerged paths」や「use "git merge --abort"」と案内されているはずです。
# 状態確認 git status # → "You have unmerged paths." # → (use "git merge --abort" to abort the merge) # マージ開始前の状態に戻す git merge --abort # 念のため確認 git status git log --oneline -3
merge –abort が行うこと
- マージ開始前のHEAD位置に戻す(
ORIG_HEADベース) - 作業ツリーもマージ前の状態に復元
- ステージされた解決途中も破棄
- マージ前にコミット済みだった自分の作業は消えない
注意:merge --abortは「マージ中の途中解決」は破棄します。せっかく解決したconflictの手作業も失われます。解決した内容を残したければ、まずgit stashで退避を試みるか、解決済みファイルを別途コピーしてから--abortしてください。
状態B:マージcommit済み・未push → reset で巻き戻す
マージが完了してコミットが作られたが、まだpushしていない状態です。履歴書き換えが自由にできるので、git resetでマージ前まで巻き戻すのが最短です。便利な参照ORIG_HEADが「マージ直前のHEAD位置」を指しているので、これを使います。
# マージ直前のHEADを確認 git log --oneline -5 # ORIG_HEAD はマージなど大きな操作の直前のHEADを指す git log --oneline ORIG_HEAD -1 # 巻き戻し(作業ツリーごと戻す) git reset --hard ORIG_HEAD # 状態確認 git log --oneline -5
SHA直接指定でも戻せる
# マージ直前のコミットSHAを確認 git log --oneline --graph -10 # → マージコミットのひとつ前のSHAを探す # そのSHAに戻す git reset --hard a1b2c3d # 変更は残したまま戻すなら --mixed git reset --mixed a1b2c3d
ポイント:ORIG_HEADは「マージやリベース等の直前HEAD」を自動で覚えてくれる便利な参照です。git merge/git pull/git resetを実行するたびに更新されるため、マージ直後に取り消したいならORIG_HEADを使うのが最短。他の操作を挟むとORIG_HEADが上書きされるので注意(その場合はreflog参照に切り替え)。
戻しすぎたときは reflog で救済
git reflog
# HEAD@{0}: reset: moving to ORIG_HEAD
# HEAD@{1}: merge feature: Merge made by...
# ↑ この位置に戻したければ
git reset --hard HEAD@{1}
resetの詳細は特定のコミットまで戻す方法、reflog救済はコミットの取り消し方も参照してください。
状態C:push済みマージ → revert -m で打ち消す
mainブランチにpush済みのマージコミットを取り消す場合、履歴を書き換えないためにgit revertで打ち消しコミットを追加します。マージコミットには2つの親があるため-mで基準にする親番号を指定します。
# マージコミットのSHAを確認 git log --oneline --merges -5 # 出力例: a1b2c3d Merge branch 'feature/xxx' into main # 親番号を確認 git show --no-patch --pretty=raw a1b2c3d # parent 行が2つ出る。通常、1つ目がマージ先(main)、2つ目が取り込んだブランチ # -m 1 でマージ先(main)を基準に打ち消し git revert -m 1 a1b2c3d # コンフリクト時 git add . git revert --continue # リモートに反映 git push origin main
-m 1 と -m 2 の違い
-m 1:親1(マージ先=main等)を基準に打ち消し。「機能ブランチを取り込む前の状態に戻す」のが通常-m 2:親2(取り込んだブランチ=feature等)を基準に打ち消し。「機能ブランチ側に戻す」(稀)- 通常のfeature→main マージを取り消すなら
-m 1が正解 - 親の関係は
git show --pretty=raw <M>のparent行で確認可能
マージコミット取り消しの詳細はmergeコミットを取り消して履歴を元に戻す方法で深掘り解説しています。
revert後の再マージ問題:取り消した機能を再導入するには
「あとから同じ機能を再びマージしたい」とき、単にgit mergeを実行しても打ち消しコミットが勝って取り込まれないという有名な罠があります。対処法を2つ紹介します。
なぜ再マージが効かないか
Gitはマージ対象の変更を「既に親のどこかに取り込んでいるか」でスキップを判断します。mainに打ち消しコミットが入ると「feature側の変更は一度取り込んで打ち消した」状態になり、再度git merge featureしても「既に取り込み済み」とみなされて何も起きません。
対処①:打ち消しコミット自体をさらにrevert
# 最初のマージを打ち消したコミット(= revert commit)のSHAを確認 git log --oneline # その打ち消しコミットをもう一度打ち消す git revert <revert_commit_SHA> # こうすると「機能を再導入するコミット」が積まれる git push
対処②:feature側で整理してからマージし直し
# 取り込みたい変更を feature 側に新コミットとして積み直す git switch feature/xxx # 必要な修正を加えてコミット git commit -am "feat: re-apply the undone feature with improvements" # 改めてmainへマージ git switch main git merge feature/xxx
ポイント:「いったん取り消したが、問題を直してやっぱり入れたい」状況はよくあります。運用としては対処②のほうが履歴が読みやすいのでおすすめ。「なぜ取り消したか」と「何が改善されたか」が新コミットのメッセージで説明できます。
特殊ケース:fast-forward / squash / octopus merge の取り消し
fast-forward merge(マージコミット無し)の取り消し
マージ先ブランチの先端がマージ元の先祖だった場合、Gitはfast-forward(FF)でマージコミットを作らずポインタを進めます。この場合-mが使えないため、revertは個別コミット単位、またはresetで巻き戻します。
# ① reset で戻す(未push個人ブランチ) git reset --hard <FFマージ前のSHA> # ② 個別コミットをrevertで打ち消す(push済み) git revert <SHA1> <SHA2> ... # ③ まとめてrange指定 git revert --no-commit <開始SHA>^..<終了SHA> git commit -m "revert: FFで取り込んだ全変更を打ち消し"
FFを避ける設定
常にマージコミットを残しておくと、後の取り消しが-m 1で一発でできます。git config --global merge.ff falseで「マージコミットを必ず作る」設定にすると、コマンドオプションを毎回付ける必要がなくなります。--ff-onlyを明示してFFだけ許すポリシーもあります。
squash merge の取り消し
GitHubの「Squash and merge」などで取り込んだ場合、マージ元コミット群が1つに押し固められた単一コミットとしてmain側に積まれます。マージコミットではないので-mは不要、通常のgit revertで打ち消せます。
# squashで作られた1コミットを通常のrevertで打ち消す git revert <squash_commit_SHA> # push git push origin main
octopus merge(3つ以上の親)の取り消し
まれにgit merge A B Cのように3つ以上のブランチを同時マージするケースがあります。親番号1〜Nを持つため-mで基準を選びますが、通常は-m 1(最初に取り込まれたマージ先)を基準にします。
# 親一覧を確認 git show --no-patch --pretty=raw <M> # parent行が3行以上ある # 親1を基準に打ち消し git revert -m 1 <M>
間違ったブランチにマージしてしまった場合
「featureブランチをmainにマージすべきがstaging にマージしてしまった」というケースは、取り消し+正しい場所にマージしなおすのが基本です。
# STEP 1: 間違ったブランチで状態確認 git switch wrong-target-branch git log --oneline --merges -3 # STEP 2: マージを打ち消す # pushしていないなら git reset --hard ORIG_HEAD # pushしているなら git revert -m 1 <マージSHA> git push # STEP 3: 正しいブランチに移動してマージ git switch correct-target-branch git pull origin correct-target-branch git merge feature/xxx git push
誤マージを防ぐ運用
- PRベースの運用:GitHubのbase branchを選ぶ画面で再確認
- Branch protection:main/stagingをprotectedにして誤マージを拒否
- CODEOWNERSでレビューアを必須化
- コミット前に
git branch --show-currentでブランチ確認
GUI・IDEでのマージ取り消し
主要ツール
- VS Code / Cursor:conflict発生時はSource Controlで「Abort Merge」ボタン、GitLensで過去マージをRevert
- JetBrains(IntelliJ/WebStorm):Merge Conflicts ダイアログの「Abort」、Git Log右クリック→Revert Commit
- SourceTree:Stop Merge ボタン、過去commit右クリック→Reverse Commit
- GitHub Web:PRページの「Revert」ボタンでマージ打ち消しPRが自動生成(-m 1相当)
- GitKraken:commit右クリック→Revert Commit
GitHubのRevertボタン
ポイント:GitHubの「Revert」ボタンは裏でgit revert -m 1相当を実行し、打ち消し用の新しいPRを作ってくれます。コンフリクトが無い場合はワンクリックで取り消しPRができるので、push済みマージの取り消しには最も安全な選択肢です。
実践シナリオ
シナリオ① pullでconflict、全部やり直したい
git status # "You have unmerged paths" git merge --abort # やり直し # 改めてpull戦略を選ぶ git pull --rebase
シナリオ② さっきのmergeを未pushで戻す
git log --oneline -3 git reset --hard ORIG_HEAD
シナリオ③ push済みmainのマージを打ち消す
git log --oneline --merges -3 git revert -m 1 <MERGE_SHA> git push origin main
シナリオ④ 取り消したfeatureを後で再度取り込みたい
# 打ち消しコミットをさらに打ち消す git revert <revert_commit_SHA> git push
履歴が複雑になったときの整理はpull後に大量のマージコミットが発生したときの原因と履歴整理方法も参考にしてください。
やってはいけない落とし穴
push済みマージを reset –hard + force push で取り消す
共有ブランチのマージをgit reset --hard+git push --forceで消すと、他メンバーのローカル環境を破壊します。共有ブランチは必ずrevert -mで打ち消しコミットを追加してください。個人ブランチでも--forceより--force-with-leaseを使うのが安全側です。
-m 1 と -m 2 を取り違える
-m 2を使うとマージ先ブランチの内容を打ち消してしまい、意図と逆の結果になります。通常のfeature→mainマージを取り消すなら-m 1(親1=main側を基準)が正解。不安ならgit show --pretty=raw <マージSHA>でparent行を確認してから実行しましょう。
merge –abort 前にconflict解決の手作業を保存しない
merge --abortは途中解決をすべて破棄します。せっかく解決したファイルの内容を別の場所にコピー/stashしておかないと、やり直し時に同じ作業を繰り返すはめになります。
revert後に再マージして「何も起きない」と混乱する
一度revertで打ち消したマージを、そのままgit mergeしても何も変わりません。「revert of revert」か「feature側で作業を積み直してマージ」で対応します。詳細は本記事の該当セクションを参照。
squash merge をマージコミットと勘違いする
GitHubの「Squash and merge」で取り込んだコミットは親が1つの通常コミットです。-m 1を付けてrevertしようとすると「not a merge」エラーになります。squashの場合は通常のgit revertで十分です。
よくある質問
reset --hard ORIG_HEAD、push済み・共有ブランチならrevert -m 1が原則です。resetは履歴を書き換え、revertは打ち消しコミットを追加するという大きな違いがあります。ORIG_HEADが上書きされます。git reflogで履歴を遡り、マージ直前の位置を特定してgit reset --hard <SHA>で戻しましょう。git add→git revert --continue。中止はgit revert --abort。revert -m 1相当を実行し、打ち消し用の新PRを自動生成します。コンフリクトが無ければこれが一番早くて安全です。git mergeしても取り込まれません。打ち消しコミット自体をさらにrevertする(revert of revert)か、feature側で新コミットを追加してから再マージします。運用的には後者が履歴の意図が分かりやすくおすすめです。git revertで打ち消すか、git resetで巻き戻します。常にマージコミットを残したい場合はmerge.ff = false設定が便利です。--hardは作業ツリーも破棄します。変更を残したいなら--mixed(既定)を。マージ取り消しの場合、--mixedだと部分的なファイルが残ってしまうので、巻き戻したうえで手動で必要な変更だけ再作成するのが現実的です。関連記事
- 【Git】mergeコミットを取り消して履歴を元に戻す方法 — push済みマージの打ち消し深掘り
- 【Git】コミットの取り消し方 — reset/revert/amendの使い分け
- 【Git】「Pulling is not possible because you have unmerged files」の解決方法 — マージ進行中の脱出
- 【Git】pull後に大量のマージコミットが発生したときの原因と履歴整理方法 — 履歴のクリーンアップ
- 【Git】rebaseとmergeの違いと使い分け — merge戦略の選び方
- 【Git】特定のコミットまで戻す方法 — reset全般
- 【Git】特定のファイルだけをマージする方法完全ガイド — 部分マージ/cherry-pick
- 【Git】pushを取り消す方法 — push済みを取り消す全体像
まとめ
- マージ取り消しは3段階で使うコマンドが変わる:進行中→
--abort/未push→reset/push済み→revert -m - マージ直前への巻き戻しは
ORIG_HEADが便利(他操作で上書きされる前に使う) - マージコミットのrevertは
-m 1(マージ先基準)が通常パターン - revert後の再マージは効かない——revert of revert か feature側再構築で対応
- fast-forward merge は個別commit revertかreset、squashは通常のrevert
- 共有ブランチで
reset --hard+force pushは禁止、必ずrevertで - 予防:PRベース運用・Branch protection・
merge.ff=falseでマージコミットを残す
マージ取り消しは「状態判定」が9割です。git statusとgit log --oneline --mergesで今どこに居るかを把握し、3段階の対応表にあてはめれば、慌てずに正しいコマンドを選べます。どの段階でもreflogがあるので90日以内なら救済できると知っておくと精神的にも楽です。共有ブランチの安全運用と、revert後の再マージ問題に気をつけながら、きれいで追いやすい履歴を保っていきましょう。