「さっきのコミット、取り消したい…」そんな経験はありませんか。コミットメッセージを間違えた、余計なファイルを含めてしまった、そもそも変更内容を見直したい。Gitで開発していると、こうした「やり直し」が必要になる場面は意外と多いものです。
そんなときに活躍するのがgit resetコマンドです。このコマンドを使いこなせば、コミットの取り消しからステージングの解除まで、さまざまな「取り消し」操作を柔軟に行えます。この記事では、git resetの3つのモード(--soft、--mixed、--hard)の違いと、安全に使うためのベストプラクティスを解説します。
git resetの概要
git resetは、現在のHEAD(ブランチの先端)を指定した状態にリセットするGitコマンドです。コミット履歴の操作、ステージングの取り消し、作業ディレクトリの復元など、バージョン管理における「取り消し」操作の中核を担います。
このコマンドが強力なのは、影響範囲を細かく制御できる点にあります。--soft、--mixed、--hardという3つのモードを使い分けることで、「コミットだけ取り消したい」「ステージングも解除したい」「変更自体をなかったことにしたい」といった異なるニーズに対応できます。
Stack Overflowでは「how to undo the most recent commit(直前のコミットを取り消すには)」という質問が7百万回以上閲覧され、1万を超える投票を集めています。それだけ多くの開発者がコミットの取り消しに悩み、git resetを必要としているということです。
3つのモードの違いを理解する
git resetを使いこなすには、Gitの「3つのツリー」を理解することが重要です。Gitは内部的に、HEAD(コミット履歴)、インデックス(ステージングエリア)、作業ディレクトリという3つの領域でファイルを管理しています。各モードはこれらのどこまでをリセットするかが異なります。
| 項目 | –soft | –mixed(デフォルト) | –hard |
|---|---|---|---|
| HEAD | 移動する | 移動する | 移動する |
| インデックス | 変更しない | リセットする | リセットする |
| 作業ディレクトリ | 変更しない | 変更しない | リセットする |
| 変更の保持 | すべて保持 | すべて保持(未ステージ状態) | すべて破棄 |
–soft:コミットだけを取り消す
--softはHEADの位置だけを移動させ、インデックスと作業ディレクトリには一切触れません。つまり、コミットは取り消されますが、変更内容はステージング済みの状態で残ります。
# 直前のコミットを取り消す(変更はステージング済みのまま)
git reset --soft HEAD~1このモードは「コミットをやり直したい」ときに最適です。たとえば、コミットメッセージを修正したい場合や、別のファイルも一緒にコミットしたかった場合に使います。リセット後、すぐにgit commitを実行すれば新しいコミットを作成できます。
–mixed:ステージングも解除する
--mixedはデフォルトのモードで、HEADとインデックスをリセットしますが、作業ディレクトリは維持します。変更内容は残りますが、ステージングされていない状態になります。
# 直前のコミットを取り消す(変更は作業ディレクトリに残る)
git reset HEAD~1
# または明示的に指定
git reset --mixed HEAD~1「git addを取り消したい」「ステージングした変更を再編集したい」といった場面で活用できます。変更自体は失われないので、安心して使用できるモードです。
–hard:すべてをリセットする
--hardはHEAD、インデックス、作業ディレクトリのすべてをリセットします。指定したコミットの状態に完全に戻るため、それ以降のすべての変更が破棄されます。
# 直前のコミットを取り消す(変更も完全に破棄)
git reset --hard HEAD~1
# 特定のコミットまで戻る
git reset --hard abc1234実験的なコードを完全に破棄したい場合や、クリーンな状態に戻したい場合に使用します。ただし、コミットしていない変更は復元できなくなるため、慎重に操作する必要があります。
基本的な使い方
ここからは、よく使うgit resetのパターンを紹介します。
ファイルをステージングから外す
特定のファイルだけをステージングから外したい場合は、ファイルパスを指定します。
# 特定のファイルをステージングから外す
git reset -- src/config.js
# 複数ファイルを指定
git reset -- src/config.js src/utils.jsなお、Git 2.23以降ではgit restore --stagedコマンドが推奨されています。より直感的な名前で同じ操作ができます。
# git restore を使う場合(Git 2.23以降)
git restore --staged src/config.js直前のコミットを修正する
コミットメッセージを間違えた、ファイルを追加し忘れた、といった場合の対処法です。
# 直前のコミットを取り消し(変更はステージング済み)
git reset --soft HEAD~1
# 必要な修正を行う
# ...
# 新しいコミットを作成
git commit -m "正しいコミットメッセージ"ちなみに、単純なコミットメッセージの修正だけならgit commit --amendの方が手軽です。
複数のコミットをまとめる
作業中に細かくコミットしすぎた場合、それらをまとめたいことがあります。
# 直近3つのコミットを取り消す
git reset --soft HEAD~3
# まとめて1つのコミットにする
git commit -m "機能Xの実装"マージを取り消す
マージ後に問題が発覚した場合、ORIG_HEADを使ってマージ前の状態に戻せます。Gitはresetやmergeなどの操作前に、元のHEADの位置をORIG_HEADとして保存してくれます。
# マージを実行
git merge feature-branch
# 問題発覚、マージ前に戻す
git reset --hard ORIG_HEADgit reset と git revert の使い分け
「コミットを取り消す」という目的ではgit revertも選択肢になります。両者の違いを理解しておくことが重要です。
git resetはコミット履歴を書き換えます。指定したコミット以降の履歴が消えるため、ローカルでの作業には便利ですが、すでにリモートにプッシュしたコミットに対して使うと問題が発生します。
# resetは履歴を書き換える
# A - B - C (HEAD)
git reset --hard HEAD~1
# A - B (HEAD) ← Cは履歴から消える一方、git revertは指定したコミットを打ち消す新しいコミットを作成します。履歴は保持されるため、共有ブランチでも安全に使用できます。
# revertは打ち消しコミットを作成
# A - B - C (HEAD)
git revert HEAD
# A - B - C - C' (HEAD) ← C'はCを打ち消すコミットチームで作業している場合、プッシュ済みのコミットを取り消すにはgit revertを使いましょう。git resetはローカルの未公開コミットに対してのみ使用するのが基本です。
ベストプラクティス
git resetを安全に使うためのポイントをまとめます。
ローカル作業でのみ使用する
git resetは履歴を書き換えるコマンドです。すでにリモートにプッシュしたコミットに対して使用すると、他の開発者がプルした後に履歴が不整合になり、コンフリクトや混乱の原因となります。共有ブランチではgit revertを使用してください。
段階的にリセットする
いきなり--hardを使うのではなく、まずは--softで様子を見ることをおすすめします。--softで問題なければ--mixed、それでも変更を破棄したい場合のみ--hardという順序で試すことで、意図しないデータ損失を防げます。
reflogを知っておく
--hardで誤って変更を消してしまった場合でも、コミット済みであればgit reflogから復元できる可能性があります。reflogはHEADの移動履歴を記録しており、過去の状態に戻るための命綱となります。
# reflogで履歴を確認
git reflog
# 出力例
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: 重要な変更
# ...
# 特定の状態に戻す
git reset --hard HEAD@{1}ただし、コミットしていない変更は復元できません。重要な変更は早めにコミットするか、git stashで一時保存しておきましょう。
よくある質問
–hardで消した変更を復元できますか?
コミット済みの変更であればgit reflogから復元できる可能性があります。ただし、コミットしていなかった変更は復元できません。重要な作業中は定期的にコミットすることをおすすめします。
HEAD~1とHEAD^の違いは何ですか?
どちらも「直前のコミット」を指します。HEAD~nはn個前のコミット、HEAD^は直前のコミットを意味します。HEAD~1とHEAD^は同じ結果になりますが、マージコミットの場合は挙動が異なることがあります。
プッシュ済みのコミットをresetしてしまいました。どうすればいいですか?
2つの選択肢があります。1つ目はgit push --forceでリモートを上書きする方法ですが、チームメンバーに影響を与えるため注意が必要です。2つ目はgit pullでリモートの状態を取り込み、git revertで対処する方法です。チーム開発では後者が安全です。
まとめ
git resetは、Gitでの「取り消し」操作の中核を担う強力なコマンドです。3つのモードの違いを理解しておくことで、状況に応じた適切な取り消し操作ができるようになります。
--softはコミットのやり直しに、--mixedはステージングの解除に、--hardは完全なリセットに使用します。ただし、--hardは変更を完全に破棄するため、段階的に試すことをおすすめします。
また、プッシュ済みのコミットに対してはgit resetではなくgit revertを使用することで、チームでの混乱を避けられます。reflogという復元手段があることも覚えておくと、いざというときに役立ちます。
参考リンク
Git公式ドキュメント – git-reset:すべてのオプションと動作が網羅された公式リファレンスです。
Pro Git Book – Reset Demystified:git resetの内部動作を3つのツリーの観点から詳しく解説しています。
Atlassian Git Tutorial – git reset:図解を交えた分かりやすい解説で、reset・checkout・revertの違いも説明されています。



