Gitでコミットした後に「やり直したい」と思う場面は頻繁にあります。コミットメッセージを書き間違えたり、余計なファイルをコミットしてしまったり、作業途中でコミットしてしまったりと、理由は様々です。
本記事では、Gitでコミットやステージング(add)を取り消すための強力なコマンド「git reset」の使い方を、図解を交えて分かりやすく解説します。
初心者にとって混乱しやすい「–soft」「–mixed」「–hard」のオプションの違いから、万が一間違えてリセットしてしまった場合の復旧手順(git reflog)、さらには git revert や git restore との違いまで網羅しています。この記事を読めば、Gitでの「巻き戻し」操作に迷いがなくなり、安全に作業をやり直せるようになります。
git resetでよく使うコマンド早見表
まず、コミットの取り消し・修正で最もよく使われるコマンドとオプションの早見表です。状況に合わせて使い分けましょう。
| やりたいこと | 実行するコマンド | オプション | 変更ファイルの扱い |
|---|---|---|---|
| 直前のコミットメッセージを直したい / コミットをまとめたい | git reset --soft HEAD^ | --soft | ステージングエリアに残る(git addされた状態) |
コミットをなかったことにして、addからやり直したい | git reset --mixed HEAD^(または git reset HEAD^) | --mixed(デフォルト) | ワーキングツリーに残る(add前の状態に戻る) |
| ミスしたコミットとその変更をすべて消去したい | git reset --hard HEAD^ | --hard | 完全に削除される(復元が困難なので注意) |
| 直前のコミットメッセージだけを変更したい | git commit --amend -m "新しいメッセージ" | – | 直前のコミットが上書きされる |
1. git resetの基本概念と3つのオプションの違い
1-1. git resetとは?
git reset は、リポジトリの「HEADの位置(コミット履歴のポインタ)」を指定したコミットまで移動(巻き戻し)させるコマンドです。Gitは、「コミット履歴(HEAD)」「ステージングエリア(Index)」「作業ディレクトリ(Working Tree)」の3つのツリーでファイルを管理しています。
git reset に指定するオプションによって、巻き戻した際に「Index」と「Working Tree」の状態をどう処理するかが異なります。
1-2. soft / mixed / hard オプションの影響範囲(図解)
各オプションが3つのエリアにどのように影響するかを図解で示します。
+-------------------------------------------------------------+
| [ git reset オプション比較 ] |
| |
| HEAD (コミット履歴) Index (ステージ) Working Tree (ファイル) |
| | | | |
| v v v |
| --soft : 巻き戻る 変更残る 変更残る |
| (ステージ済) |
| |
| --mixed : 巻き戻る 巻き戻る 変更残る |
| (デフォルト) (ステージ解除) (ファイル残る) |
| |
| --hard : 巻き戻る 巻き戻る 巻き戻る |
| (全て消去) |
+-------------------------------------------------------------+
1-3. –soft:コミットのみを取り消す(直前の修正向け)
--soft オプションは、コミット履歴(HEAD)のみを過去に戻し、ファイル変更や git add した状態(ステージングエリア)はそのまま維持します。
# 直前のコミットを取り消して、ステージング状態に戻す
git reset --soft HEAD^
こんな時におすすめ:
「コミットした直後に、コミットメッセージの間違いに気づいた」「複数のコミットを1つにまとめてからプッシュしたい(ローカルの履歴整理)」といったケースに最適です。実行後、そのまま git commit を行えば、メッセージを修正してコミットし直せます。
1-4. –mixed:コミットとステージングを取り消す(デフォルト)
--mixed オプションは、コミット履歴(HEAD)とステージングエリア(Index)の両方を過去に戻します。ファイルの変更内容自体は作業ディレクトリ(Working Tree)にそのまま残ります。オプションを省略した場合は、自動的に --mixed が適用されます。
# コミットとステージングを取り消す(--mixed は省略可能)
git reset HEAD^
こんな時におすすめ:
「コミットしたけれど、構成やファイルを再度選び直して git add からやり直したい」という場合に便利です。ファイル自体は消えないため、安全にやり直しができます。
1-5. –hard:すべての変更を完全に破棄して戻す(取り扱い注意)
--hard オプションは、コミット履歴(HEAD)、ステージングエリア(Index)、そして作業ディレクトリ(Working Tree)のすべての変更を、指定したコミットの状態に強制的に巻き戻します。
# 直前のコミットとその変更を完全に消去する
git reset --hard HEAD^
注意点:
このコマンドを実行すると、前回のコミット以降に書いたソースコードは完全に削除され、復元できなくなります。まだコミットしていない作業中の変更も失われます。実行する前には、必ず git status や git diff で未保存の重要な作業がないか確認してください。
2. 【シーン別】git resetの具体的な使い方・ユースケース
2-1. 直前のコミットを取り消したいとき
直前のコミットを取り消すには、戻り先として HEAD^ または HEAD~1 を指定します。
# 例: ファイルの変更を残したままコミットだけ取り消す
git reset --soft HEAD^
※ HEAD^ は「現在のHEAD(最新コミット)の1つ前の親コミット」を意味します。
2-2. 複数(N個前)のコミットをまとめて1つにしたいとき
ローカルで細かくコミットを刻みすぎた場合、プッシュする前にそれらを1つのきれいなコミットにまとめることができます。例えば、直近の3つのコミットをまとめたい場合は、3つ前(HEAD~3)に --soft でリセットします。
# 3つ前まで履歴を戻し、変更内容はステージングに残す
git reset --soft HEAD~3
# 新しいメッセージでまとめてコミット
git commit -m "機能実装(3つのコミットを集約)"
2-3. 特定の過去コミットの状態まで戻したいとき
ログから取得した具体的なコミットID(ハッシュ値)を指定することで、そのコミット時点まで一気に巻き戻すことができます。
# コミット履歴を確認
git log --oneline
# 出力例:
# a1b2c3d (HEAD -> main) 最新コミット
# e4f5g6h 2つ前のコミット
# 7i8j9k0 戻したい過去のコミット
# 指定したコミット(7i8j9k0)の状態まで強制的に戻す
git reset --hard 7i8j9k0
2-4. git add(ステージング)だけを取り消したいとき
間違えて git add してしまったファイルをステージングエリアから除外したい(コミット対象から外したい)場合、コミット自体は戻さず、特定のファイルだけをリセットできます。
# すべてのステージングを取り消す
git reset
# 特定のファイルだけステージングを取り消す
git reset file_name.txt
※なお、現代のGit(バージョン2.23以降)では、ステージングの解除には git restore --staged <file> の使用が推奨されていますが、git reset も同様に機能します。
3. 直前コミットをその場で修正するなら「git commit –amend」
直前のコミットをやり直す場合、一度 git reset をしてからコミットし直すよりも、「git commit –amend」を使うほうが素早く安全に対応できます。このコマンドは、新しいコミット履歴を作るのではなく、直前のコミット自体を書き換えます。
3-1. コミットメッセージだけを修正する
ファイルの変更はそのままで、メッセージの間違いだけを直したい場合のコマンドです。
# コミットメッセージを変更して直前コミットを上書き
git commit --amend -m "正しいコミットメッセージ"
3-2. 追加し忘れたファイルを含めてコミットし直す
「一部のファイルの修正を入れ忘れたままコミットしてしまった」という場合は、忘れていたファイルを add してから --amend を実行します。
# 追加し忘れたファイルをステージング
git add forgotten_file.js
# コミットメッセージはそのままで、直前のコミットに統合
git commit --amend --no-edit
--no-edit を付与すると、コミットメッセージ編集用のエディタを起動せずに、元のメッセージのままでコミットを上書きできます。
3-3. git reset –soft との使い分け
直前コミットの修正は git reset --soft と git commit --amend のどちらでも可能ですが、目的によって使い分けると作業効率が向上します。
| 状況・目的 | 推奨するアプローチ | 理由 |
|---|---|---|
| 直前のコミットメッセージや構成をサクッと直したい | git commit --amend | 1コマンドで直接書き換えるため手軽で速い |
| 直前だけでなく、数個前のコミットも巻き込んで履歴を再整理したい | git reset --soft HEAD~N | 複数コミットの変更を1箇所に集約して再コミットできる |
| どのファイルをコミットに含めるか、ステージングから吟味し直したい | git reset HEAD^ (mixed) | コミットとステージングの両方を解除して冷静に再確認できる |
4. 【救済策】git reset –hardで消したコミットを「git reflog」で復元する手順
「誤って git reset --hard を実行してしまい、大切なコミットが消えてしまった!」という場合でも、諦める必要はありません。ローカルの作業履歴である「git reflog」を使えば、削除されたコミットを復元できます。
4-1. git reflogとは?
git reflog は、コミットだけでなく reset や checkout、merge など、ローカルで行った「HEADの移動操作」をすべて時系列で記録しているコマンドです。通常の git log には表示されない「削除されたはずのコミット」も、この履歴に残っています。
4-2. 誤操作から元の状態へ復元する3ステップ
誤操作をしてしまった場合、以下のステップで復元を行ってください。
ステップ 1: 操作履歴を表示する
まず、以下のコマンドを実行します。
git reflog
出力結果例:
a1b2c3d HEAD@{0}: reset: moving to HEAD^ <-- 間違えてresetした操作
e4f5g6h HEAD@{1}: commit: 実装完了(戻したいコミット!)
7i8j9k0 HEAD@{2}: checkout: moving from develop to feature-A
ステップ 2: 戻したいポイント(コミットIDまたはHEAD@{n})を特定する
出力された履歴の上から順に見ていき、間違えて reset する直前のコミットを探します。上記の例では、HEAD@{1}(コミットID: e4f5g6h)が戻したい状態です。
ステップ 3: 強制リセットで復元を実行する
特定したコミットIDまたは HEAD@{n} を指定して、--hard でリセットを行います。
# コミットIDで指定して復元する場合
git reset --hard e4f5g6h
# HEAD@{n}のインデックスで指定して復元する場合
git reset --hard HEAD@{1}
これで、削除されたコミットとワーキングツリーのファイルが、無事誤操作前の状態に復元されます。
4-3. 未コミットの変更は復元できない点に注意
git reflog はあくまで「コミット(あるいはHEADの移動)された履歴」をたどるものです。そのため、一度も git commit していない(または一度も git add して記録に残っていない)ファイルの変更内容は、git reset --hard で消してしまうと reflog でも復元できません。リセット前の確認が極めて重要とされるのはこのためです。
5. 類似コマンドとの違いと使い分け(restore / revert)
Gitには reset 以外にも「状態を戻す」ためのコマンドが存在します。これらを正しく使い分けることで、より安全に開発を行うことができます。
5-1. git reset と git revert の違い(プッシュ済みの取り消し)
git revert は、過去のコミット履歴を削除するのではなく、「指定したコミットの変更内容を打ち消すための新たなコミット」を作成するコマンドです。
# 指定したコミットの変更内容を打ち消す新しいコミットを作成
git revert a1b2c3d
使い分け:
すでにリモートリポジトリに push してチームメンバーと共有してしまったコミットは、git reset で履歴から消し去ると他の人のリポジトリと競合が発生し、大混乱を招きます。「プッシュ前なら git reset」「プッシュ後なら git revert」と覚えましょう。
5-2. git reset と git restore(旧 checkout)の違い(ファイル変更の破棄)
git restore は、特定のファイルの変更を取り消したり、ステージングを解除したりするためのコマンドです(Git 2.23で導入され、かつて git checkout が担っていたファイル操作機能を切り離したものです)。
# ファイルの編集内容を破棄して、前回のコミット時点に戻す(ワークツリーの復元)
git restore src/index.js
# 特定のファイルをステージングから除外する(ステージの復元)
git restore --staged src/index.js
使い分け:git reset は「コミット履歴の位置」そのものを戻すコマンドであるのに対し、git restore は「ファイルの編集内容を無かったことにする(ファイルを元の状態に戻す)」ためのコマンドです。履歴をいじる必要がなく、ただファイル単体を戻したいときは git restore を使用します。
6. git resetを使う際の重要ルールと事故防止策
6-1. ルール:プッシュ済みの共有ブランチでは絶対に使わない
git reset は「ローカル専用 of タイムマシン」です。すでにGitHubなどのリモートに送信し、他の開発者がその上にコードを書いている可能性がある共有ブランチ(main, develop など)では、決して使用してはいけません。どうしても取り消す必要がある場合は、前述の git revert を使用してください。
6-2. 対策:実行前に git status と git diff で状態を確認する
特に --hard オプションを使う前には、失われるファイルが無いかを確実に確認してください。ワンクッション置いて確認する習慣をつけるだけで、不要なデータ紛失事故は防げます。
# 現在ステージされているファイル、変更されたファイルの一覧を確認
git status
# 具体的にどのコードが書き換わっているか差分を確認
git diff
6-3. 対策:迷ったら git stash で作業内容を退避しておく
「このコミットは取り消したいが、今書いている書きかけのコードが消えるのは困る」「削除するかどうか迷っている」という場合は、git stash を使って変更内容を一時避難(退避)させてから reset を行いましょう。
# 1. 現在の変更を一時退避
git stash
# 2. resetを実行して履歴を整える
git reset --hard HEAD^
# 3. 退避していた変更をワーキングツリーに戻す
git stash pop
よくある質問(FAQ)
HEAD^ と HEAD~1 に違いはありますか?
基本的に「1つ前のコミット」を指す上では全く同じ意味です。ただし、複数遡る際の表記ルールが異なります。HEAD^^(キャレットを2つ)と HEAD~2(チルダと数値)はともに「2つ前のコミット」を指しますが、さらに多く遡る場合は HEAD~5 のように数値を指定できるチルダ(~)表記の方が便利です。
reset --soft と --mixed はどちらを使うべきですか?
迷ったら --mixed(オプションなしのデフォルト)が安全です。なぜならファイル変更が残るため、データが消える心配がないからです。コミットメッセージの修正だけであれば --soft の方が git add の手間が省けるためスムーズです。
amendした後にプッシュしたら拒否されたのですが?
git commit --amend はローカルのコミット履歴を書き換えます。そのため、すでにリモートにプッシュ済みのコミットに対して amend を行うと、リモートと履歴の不整合が起きるためプッシュが拒否されます。自分だけの作業ブランチであれば git push --force-with-lease で強制上書きできますが、チームで使うブランチでは行わないようにしてください。
コミットを取り消した後にリモートの変更と同期するには?
ローカルで git reset して過去に戻った後、リモートの最新状態を取り込むには git pull --rebase を使うのが推奨されます。単に git pull を行うと、取り消したはずのコミットがマージコミットとして復活してしまいます。
まとめ
git reset は、Gitの基本操作の中でも非常に強力で、使いこなせば開発効率を飛躍的に向上させられます。一方で、履歴を書き換えるため扱いには十分な注意が必要です。
- --soft: コミットだけ取り消し。変更はステージングに残る(最速の再コミット向き)。
- --mixed(デフォルト): コミットとステージングを取り消し。変更はファイルに残る(addの再選択向き)。
- --hard: すべて消去。未コミットの変更は失われるため、実行前に必ず状態確認を。
- プッシュ後は revert: 共有ブランチの変更は、履歴を書き換えない
git revertを使用する。 - 誤操作は reflog:
git reset --hardで誤って消してしまった場合でも、git reflogから復元可能。

コメント