意図しないマージを取り消して履歴を元に戻したいとき、公開済みのブランチでは履歴を書き換えずに「マージを打ち消す」ことが基本方針になります。
本記事では、公開ブランチで安全に使えるgit revert -m
を中心に、未pushならgit reset
で巻き戻す方法、取り消し後に必要な変更だけを取り戻す手順、コンフリクト時の進め方までを整理します。
まず状況確認:どのマージを、どの親を基準に取り消すか
最初に対象のマージコミット(例:M
)を特定し、親の関係を把握します。一般的な2親のマージでは「親1=マージ先(今いるブランチ側)」「親2=取り込んだ側」になっていることが多いですが、確実に確認しておくと安全です。
# 参照を更新して履歴を俯瞰
git fetch --prune
git log --oneline --decorate --graph -n 30
# 対象マージ M の親と差分を確認
git show --no-patch --pretty=raw <M>
git show -m --name-status <M>
親関係が分かったら、公開ブランチではrevert
、未pushであればreset
の選択肢が取れます。
公開ブランチで安全に取り消す:git revert -m
git revert
は指定コミットの変更を打ち消す新しいコミットを作り、履歴は維持されます。マージを取り消すときは基準にする親を-m
で指定します。通常は親1(マージ先)を基準にします。
# マージコミット M を取り消す(親1=マージ先を基準)
git revert -m 1 <M>
# コンフリクトが出たら解消→ステージ→続行
git add .
git commit
# リモートへ反映
git push
取り消し後、必要な変更だけを別ブランチへcherry-pick
して再適用すれば、履歴の整合性を保ちながら最小限の修正にできます。
未pushなら履歴を巻き戻せる:git resetでマージ自体を無かったことに
まだ誰にも共有していないマージなら、マージ直前のコミットまでreset
で戻すのが最短です。作業ツリーも巻き戻すなら--hard
、変更は残したいなら--mixed
を使います。
# マージ直前のコミットを base として巻き戻す
git reset --hard <base> # 作業ツリーごと戻す
# もしくは
git reset --mixed <base> # 変更は残して履歴だけ戻す
# その後、必要なら整えたうえでpush(公開後は reset は不可)
すでにpushしているブランチでreset
を使う場合は、強制pushが必要になり共同作業に影響します。やむを得ない場合のみ合意のうえ--force-with-lease
を使います。
git push --force-with-lease
取り消し後に「必要な変更だけ」を取り戻す方法
マージ全体は誤りでも、中に正しい修正が含まれていることはよくあります。取り消しコミットの後で、必要なコミットだけを元ブランチから選んでcherry-pick
すれば、意図した差分だけを復活できます。
# 例:取り込み元ブランチ side のコミット C1, C2 だけを再適用
git cherry-pick <C1> <C2>
ファイル単位で部分的に戻したい場合は、対象コミットの直前から内容を引き戻します。
# 例:マージ M の影響前(M^)の内容に特定ファイルを戻す
git restore --source <M^> -- path/to/fileA path/to/fileB
git add .
git commit -m "Restore files to state before <M>"
コンフリクトが発生したときの進め方
revert -m
でも差分が大きいとコンフリクトになります。衝突箇所を解消してステージし、通常のマージと同様にコミットで完了します。手に負えない場合は一旦取り消して方針を変えます。
# 進行中の状態を確認
git status
# 解消→続行
git add .
git commit # revert の最終コミット
# やり直す場合(revert を取消すにはこのコミットを git reset で落とす or 逆revert)
# 逆revert(取り消しの取り消し)で元に戻す例
git revert <revert-commit-hash>
複数のマージを連続で取り消す場合の注意
時系列に沿って一つずつrevert -m
を行い、その都度テストを通すのが安全です。対話的にまとめたいときは、作業用ブランチで順番に取り消してから一つのPRにまとめます。三つ以上の親を持つオクトパスマージでも-m
で基準親を選べますが、衝突が増えやすいため粒度を小さく進めます。
やる前の保険と、巻き戻し経路の確保
誤操作に備えて退避ブランチを切り、reflog
で戻れることを確認してから着手します。これだけで心理的安全性が上がります。
git switch -c safety/backup-$(date +%Y%m%d-%H%M)
git reflog
運用面の再発防止
デフォルトブランチには保護設定を入れ、PR経由でのみ統合する運用にすると誤マージが減ります。取り込み前にgit fetch
で最新を取り、fast-forward可能なときは--ff-only
で履歴を汚さない方針を徹底します。日常の同期はリベースを基本にして差分を小さく保つのも有効です。
# 推奨設定例
git config --global pull.ff only
git config --global pull.rebase true
git config --global fetch.prune true
まとめ
公開済みの誤マージはgit revert -m
で「機能的に取り消す」のが安全で、未pushならgit reset
でマージ自体をなかったことにできます。
取り消し後は必要な変更だけをcherry-pick
やrestore
で丁寧に取り戻し、コンフリクトは通常のマージ同様に解消します。
退避ブランチとreflog
で退路を確保しつつ、保護設定と取り込みルールを整えることで、同種の事故は確実に減らせます。