git mergeを取り消すには?push前後・状況別ガイド

git-merge-undo 開発環境・インフラ
記事内に広告が含まれています。

間違えて意図しないブランチを git merge してしまったり、不要なコミットを巻き込んでしまって冷や汗をかいた経験はないでしょうか。急いで元に戻そうとしても、「履歴がおかしくなったらどうしよう」「他の人の作業に影響が出ないか不安」と迷ってしまう方は少なくありません。

Gitのマージ取り消しは、リモートリポジトリへ push する前か後かによって選ぶべきコマンドが明確に分かれます。仕組みを正しく理解せずにコマンドを実行してしまうと、保存していない作業が消えたり、共有リポジトリの履歴が混乱する原因になります。

この記事でわかること

  • 自分の状況に合わせた最適な取り消し手順(push前・push後)
  • git resetgit revert の仕組みと正しい使い分け
  • git revert -m 1 の意味と、マージコミットを安全に打ち消す方法
  • ブランチ削除(git branch -D)とマージ取り消しの違い
  • 失敗しやすいポイントと、万が一の際の復旧手順(git reflog の活用)

現在の作業状態(pushの有無)を整理し、安全にマージを取り消すための具体的な操作手順を確認していきましょう。

◆◇◆ 【衝撃価格】VPS512MBプラン!1時間1.3円【ConoHa】 ◆◇◆

【状況別】merge取り消しの3つの方法

状況(リモートへのpush有無、ブランチ削除の意図)によって使うべきコマンドが異なります。まずは以下の判断表で、実行すべき操作を確認してください。

状況基本的な方法特徴
push前git reset --hard HEAD~1履歴を書き換えてmerge前の状態へ完全に戻す
push後git revert -m 1 <commit-hash>履歴を残したまま、mergeの変更を打ち消す新しいcommitを作成する
作業ブランチの整理git branch -d / -D不要になったブランチ参照を削除する(※mergeの変更自体を取り消すわけではない)
git-merge – Git コマンドリファレンス(日本語版)

補足:fast-forward mergeとmerge commitの違い

マージの取り消し方法は、マージの種類(Fast-forwardマージかMerge commitを伴うマージか)によっても挙動が変わります。

Merge commit(通常の非Fast-forwardマージ)

親コミットを2つ持つ新しいマージコミットが作成されるマージです。git revertの実行時には親の指定(-m 1など)が必要となります。

Fast-forward merge

マージ先ブランチの先端が単純に進むだけのマージです。専用のマージコミットが存在しないため、git revert -m 1を直接適用することはできません。Fast-forwardマージを取り消す場合は、マージ前のコミットハッシュを指定してgit resetを行うか、マージで取り込まれた個別のコミット群を順番にgit revertする必要があります。

git merge undo

❶ pushする前に取り消し:git reset –hard HEAD~1

リモートリポジトリへgit pushする前のローカル環境であれば、git resetを使ってmerge自体を行わなかった状態に戻せます。

git reset --hard HEAD~1

このコマンドは、現在のブランチの参照(HEAD)を1つ前のコミットに強制的に戻します。

実行時の履歴の変化

たとえば、次のような履歴でfeatureブランチをmainブランチにマージし、マージコミットMが作成されたとします。

A---B---M (HEAD -> main)
     \ /
      C (feature)

この状態でgit reset --hard HEAD~1を実行すると、mainブランチのHEADはマージ直前のコミットBに戻ります。

A---B (HEAD -> main)
     \
      C (feature)

マージコミットMへの参照が失われ、作業ツリー(ワークツリー)およびインデックス(ステージングエリア)の状態もコミットB時点のものへ上書きされます。

注意点:未コミットの変更は消失する

--hardオプションを指定すると、作業ツリー内で保存していない未コミットの変更(編集中のファイルや新規作成ファイル)もすべて破棄されます。退避させたい変更がある場合は、実行前にgit stashで退避させておく必要があります。

リセットの仕組みと安全性

git resetは、現在選択しているブランチが指し示すコミット位置(HEAD)を移動させるコマンドです。

HEADとリセットオプションの違い

git resetには主に3つのオプションがあり、それぞれ影響範囲が異なります。

  • --soft: HEADの位置のみを移動させます。作業ツリーとステージングエリアの変更は保持されます。
  • --mixed (デフォルト): HEADの位置を移動させ、ステージングエリアもリセットします。作業ツリーの変更は保持されます。
  • --hard: HEADの位置、ステージングエリア、作業ツリーのすべてを指定したコミットの状態にリセットします。

マージコミットを取り消す際、マージによって発生した作業ツリー上の変更もまとめて削除して完全にマージ前に戻す目的で--hardが使用されます。

共有ブランチ(push済み)でresetを使ってはいけない理由

すでにリモートリポジトリにpushしたコミットに対してgit resetを行い、git push --forceでリモート履歴を上書きすると、他の開発者のローカル履歴と矛盾が生じます。他の開発者がリポジトリを引き直す手間が発生したり、変更の紛失につながるリスクがあるため、push済みの共有ブランチではgit resetではなくgit revertを使用してください。

❷ pushした後に取り消し:git revert -m 1

すでにリモートリポジトリにpushしてしまったマージを取り消す場合は、git revertを使用します。

git revert -m 1 <commit-hash>

git revertは過去のコミット履歴を削除したり書き換えたりするのではなく、「特定のコミットの変更を打ち消す新しいコミット」を末尾に追加するコマンドです。

<commit-hash>の確認方法

まず、打ち消したいマージコミットのハッシュ値を確認します。

git log --oneline

出力例:

a1b2c3d (HEAD -> main, origin/main) Merge branch 'feature'
e5f6g7h Commit on main before merge
8i9j0k1 Commit on feature

この場合、a1b2c3d がマージコミットのハッシュ値となります。

-m 1(mainline parent)が必要な理由

マージコミットは一般的なコミットとは異なり、2つ以上の親(Parent)コミットを持っています。

  • Parent 1: マージ元となっていた元のブランチ(例: main)の直前コミット
  • Parent 2: 取り込まれたブランチ(例: feature)の直前コミット

git revertを実行する際、どちらの親コミットの状態を基準(mainline)として残し、もう一方の親から取り込まれた変更を打ち消すのかを明示する必要があります。

-m 1 は「1番目の親(マージを実行した側のブランチ)をベースラインとして残し、2番目の親から合流した変更を打ち消す」という指示です。-m 1 は「1番目の親を削除する」という意味ではありません。

revertの利点(履歴が残る)

git revertを使う最大の利点は、リポジトリのコミット履歴を変更せず、新しく修正コミットを追加する点にあります。

A---B---M---R (HEAD -> main)
     \ /
      C
  • M: マージコミット
  • R: マージを打ち消すrevertコミット

すでに他の開発者がgit pullでマージ済みのコミットMを取得していても、Rが新しくプッシュされるだけであるため、チーム全体のGit履歴が壊れません。

見た目上マージコミットMが消えるわけではなく、「マージによって導入された変更差分を正確に打ち消すコミットRを適用した状態」になります。

❸ ブランチ全体を取り消し:git branch -D

マージで使用したトピックブランチ(例: featureブランチ)が不要になった場合、ブランチ参照を削除します。

git branch -D <branch-name>

注意:ブランチ削除はマージの取り消しではない

git branch -D は「ブランチという名前付きのポインタ(参照)」を削除するコマンドであり、マージによってmainブランチ等に取り込まれたコミットやマージコミットを消去する機能はありません。

すでにマージが完了している場合、featureブランチを削除してもmainブランチ上のマージ内容や履歴には影響しません。マージ自体を取り消したい場合は、ブランチの削除ではなくgit resetまたはgit revertを実行してください。

-d-Dの違い

  • git branch -d <branch-name>: 安全な削除。指定したブランチが現在のブランチに完全にマージされている場合のみ削除を許可します。
  • git branch -D <branch-name>: 強制削除。マージされていないコミットが残っていても警告なしでブランチ参照を削除します。

マージしたブランチの削除方法

作業が終了し、マージも完了した(またはマージを取り消した)後に不要なブランチを整理する標準的な手順です。

ローカルブランチの削除

削除対象以外のブランチ(例: main)に切り替えてから実行します。

git checkout main
git branch -d feature-branch

未マージのコミットが含まれるブランチを意図的に削除する場合は-Dを使用します。

git branch -D feature-branch

リモートブランチの削除

GitHubなどのリモートリポジトリ側に残っているブランチを削除するには、以下のコマンドを実行します。

git push origin --delete feature-branch

これにより、リモート上のブランチ参照も削除されます。

国内シェアNo.1のエックスサーバーが提供するVPSサーバー『XServer VPS』

よくある失敗と対処法

マージの取り消し操作では、手順の誤りやGitの仕様に対する認識不足によって予期せぬトラブルが発生することがあります。代表的な失敗パターンとそれぞれの対処法は以下の通りです。

1. git reset --hard で未コミットの作業変更が消えた

git reset --hard を実行すると、インデックス(ステージングエリア)および作業ツリー上の未コミットの変更がすべて上書きされ、消絶します。この操作で破棄された「コミット前の変更」は、Gitの履歴管理対象外であるため、コマンド実行後に復旧することは原則不可能です。

対処法

今後の防止策として、git reset --hard を実行する前には必ず git status で未コミットの変更がないか確認し、残したい変更がある場合は git stash で一時退避するか、事前に別コミットとして保存してください。

2. Fast-forwardマージで HEAD~1 や -m 1 が期待通り動かない

Fast-forward(FF)マージが行われた場合、新しいマージコミットは作成されず、単にマージ先ブランチの参照がマージ元ブランチの先端まで進みます。

この状態で操作を行うと、以下の問題が発生します。

git reset --hard HEAD~1 を実行した場合

単にマージされた側のブランチに含まれる最新コミットの1つ前に戻るだけで、マージ前の状態(マージ直前の自ブランチの先端)には戻りません。

git revert -m 1 <commit-hash> を実行した場合

対象のコミットがマージコミットではないため、以下のエラーが発生して実行できません。

fatal: commit <commit-hash> is not a merge

対処法

resetで戻す場合

git refloggit log でマージ直前の自ブランチのコミットハッシュを確認し、git reset --hard <commit-hash-before-merge> のようにコミットハッシュを直接指定してリセットします。

revertで戻す場合

マージコミットが存在しないため、マージによって取り込まれた個々のコミットを末尾から順に git revert <commit-hash> で打ち消すか、マージ全体を打ち消す新しいコミットを手動で作成します。

3. Revertした後に同じブランチを再度マージしても変更が反映されない

git revert -m 1 を使用してマージを取り消した後、トピックブランチ側で修正を行って再度マージしようとしても、初回マージで取り込まれていた変更が反映されない現象が発生します。

Gitの内部構造上、初回マージのコミット群はすでに履歴上に到達済みとして扱われます。そのため、それらのコミットに含まれる変更を再度マージしようとしても、Gitは「すでに適用済みの変更」と判断し、無視します。

対処法

過去に実行した「マージを打ち消したrevertコミット」自体を、さらに git revert します(RevertのRevert)。

git revert <revert-commit-hash>

これにより、以前打ち消した変更が再度有効化されます。その上で、新しく追加した修正分と一緒にマージを行います。

4. Push済みリポジトリに対して git reset を実行してしまった

すでにリモートリポジトリへ push されたマージコミットに対してローカルで git reset を行うと、ローカルリポジトリの履歴がリモートリポジトリより古い(進みが遅れている)状態になります。この状態で通常の git push を実行すると、以下のエラーで拒否されます。

error: failed to push some refs to '...'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

ここで git push --force を行うとリモートの履歴が書き換わり、同じブランチで作業している他の開発者のローカル環境と整合性が取れなくなります。

対処法

すでにpush済みの場合は git reset による解決を取りやめ、git pull でリモートの状態に戻してから git revert -m 1 を使用して打ち消しコミットを作成し、通常通り push してください。

5. git branch -D を実行したのにマージした変更が消えない

git branch -D <branch-name> は指定したブランチの参照(ポインタ)を削除するコマンドであり、マージによってメインブランチに取り込まれたコミットや差分を削除する効果はありません。

対処法

マージされたコード差分を取り消したい場合は、ブランチ削除ではなく git reset または git revert を実行してください。

merge取り消し後に気をつけるべきこと

マージの取り消しコマンドを実行した後は、意図通りの状態になっているか必ず状況を確認する必要があります。

状態の確認手順

取り消し操作を終えたら、以下の2つのコマンドで現在の状態をチェックします。

git status
git log --oneline --graph --decorate -n 5

git status で作業ツリーに予期せぬ未コミットファイルが残っていないか確認し、git log でHEADの位置やコミット履歴が意図した通りになっているかを確認します。

git reflog による過去状態の確認と復旧

git reset --hard を誤って実行し、必要なマージコミットや作業履歴を見失った場合でも、コミット自体が存在していたのであれば git reflog から復旧できます。

git reflog

出力例:

a1b2c3d HEAD@{0}: reset: moving to HEAD~1
e5f6g7h HEAD@{1}: merge feature: Merge made by the 'ort' strategy.
b3c4d5e HEAD@{2}: commit: main branch commit

リセット前の状態(例: HEAD@{1} またはハッシュ値 e5f6g7h)を確認し、再度 git reset を実行することで、誤って消したマージ直後の状態に戻せます。

git reset --hard e5f6g7h

※ただし、コミットしていなかった作業ツリー上の変更は reflog にも記録されないため、この方法でも復旧できません。

Force Pushが必要なケースとリスク

ローカル環境で git reset を行い、その結果をリモートリポジトリにどうしても反映させなければならない場合(個人専用ブランチなど)、安全なオプション付きの強制プッシュを使用します。

git push --force-with-lease origin <branch-name>

--force-with-lease は、自身が最後に把握しているリモートの状態から他人が更新を行っていない場合のみ強制プッシュを許可するオプションであり、通常の --force よりも安全です。

ただし、複数人で共有しているメインブランチ(maindevelop など)での強制プッシュは、他人の作業を上書き・破棄する原因となるため、チーム開発においては原則禁止とするのが一般的です。共有ブランチで履歴を書き換える必要がある場合は、必ず事前に関係者へ確認を取ってください。

新世代レンタルサーバー『シンレンタルサーバー』

よくある質問(FAQ)

merge直後(push前)ならどうやって取り消せばいいですか?

まだリモートリポジトリに push していない状態であれば、git reset --hard HEAD~1 を実行することでマージ直前のコミットまで履歴を戻せます。ただし、未コミットの作業内容は破棄されるため、事前に git stash などで保護してください。

pushした後でもmergeを取り消せますか?

取り消せます。すでに push 済みの場合は、履歴を書き換える git reset ではなく、マージを打ち消す新しいコミットを作成する git revert -m 1 <commit-hash> を使用します。

git resetとgit revertはどちらを使えばいいですか?

まだリモートに push していないローカル環境であれば git reset を使用して履歴を綺麗に戻します。すでに push して他の開発者と共有している状態であれば、履歴を崩さない git revert を使用してください。

git revert -m 1の「1」は何を意味していますか?

マージコミットが持つ複数の「親コミット(Parent)」のうち、どちらをベースライン(mainline)として残すかを指定する番号です。1 を指定すると「マージを実行した側のブランチ」の状態を維持し、取り込んだ側の変更のみを打ち消します。

git branch -Dでmergeを取り消せますか?

取り消せません。git branch -D はブランチの参照(ポインタ)を削除するコマンドであり、すでにメインブランチ等に取り込まれたマージコミットやコード差分を消去する機能はありません。マージを取り消すには git reset または git revert を使用してください。

merge取り消し後に再度mergeできますか?

できます。ただし git revert -m 1 でマージを取り消した場合、そのまま再度マージしても過去のコミット差分が適用されません。打ち消した revert コマンド自体をさらに revert(RevertのRevert: git revert <revert-commit-hash>)してから再度マージを行う必要があります。

間違えて reset –hard した場合は戻せますか?

コミット済みの変更であれば、git reflog コマンドで過去の HEAD の移動履歴を確認し、git reset --hard <hash-before-reset> を実行することでリセット前の状態へ戻せます。ただし、コミットしていなかった未コミットの作業ツリー上の変更は復旧できません。

まとめ

マージを取り消す際は、リモートリポジトリへの push の有無と作業の目的に合わせて適切なコマンドを選択します。

  • push前(ローカルのみ): git reset --hard HEAD~1 を検討します。マージ前の状態に履歴ごと戻せますが、作業ツリー上の未コミットの変更も消絶するため、事前の退避(git stash など)が必要です。
  • push後(共有リポジトリ): git revert -m 1 <commit-hash> を基本とします。マージを打ち消す新しいコミットを追加するため、共有ブランチの履歴を壊さずに安全に対処できます。
  • ブランチの削除: git branch -d-D はブランチのポインタ(参照)を削除する操作であり、マージされたコミットやコード差分を取り消す効果はありません。

コマンドを実行する前後は git statusgit log でリポジトリの状態を逐次確認してください。誤って reset --hard を実行した場合でも、コミット済みの履歴であれば git reflog から過去の状態をたどって復旧可能です。

現在の作業環境が「push前」か「push後」か、そして「個人作業」か「チームでの共有作業」かを整理したうえで、安全な手順を選択してください。

まずは無料体験・説明会に参加を♪【Winスクール】
タイトルとURLをコピーしました