取り消し操作の完全ガイド — reset・revert・restore
「間違ってコミットしてしまった」「変更を戻したい」「ブランチを巻き戻したい」。Gitでもっとも混乱するのがこの取り消し操作です。使うコマンドによって戻る範囲と履歴への影響がまったく違います。
この章で3つのコマンドを整理して、必要なときに迷わず使えるようにしましょう。
3つのコマンドの役割
それぞれの役割を一言で表すと以下のようになります。
| コマンド | 役割 | 履歴への影響 |
|---|---|---|
git restore | ファイルの変更を取り消す | なし(作業ディレクトリ/ステージングのみ) |
git reset | コミットを巻き戻す(履歴から消す) | あり(コミットが消える) |
git revert | 指定コミットを打ち消すコミットを作る | あり(新しいコミットが追加される) |
どれを使うべきかの判断基準はシンプルです。
git restore — 未コミットの変更を取り消す
git restore は、まだコミットしていない変更を元に戻すコマンドです。前章で出てきた git checkout -- file の新しい書き方です。
作業ディレクトリの変更を捨てる
# app.js の編集内容を捨てて、最後のコミット時点に戻す
git restore src/app.js
# すべてのファイルの変更を捨てる
git restore .この操作は変更を完全に破棄するので注意してください。restore を実行した瞬間、編集内容はGitからは復元できなくなります。
ステージングを取り消す
git add しちゃったけど戻したい、というときは --staged オプションを使います。
# app.js のステージングだけ解除(ファイル内容はそのまま)
git restore --staged src/app.jsこれはステージング状態を解除するだけで、ファイルの編集内容には影響しません。
整理すると
git restore file # 作業ディレクトリの変更を捨てる
git restore --staged file # ステージングを取り消す
git restore --source HEAD~1 file # 1つ前のコミットの状態にするgit reset — コミットを巻き戻す
git reset は、コミットをなかったことにするコマンドです。ブランチの先端ポインタを過去のコミットに戻します。
3つのモード
reset には3つのモードがあり、取り消す範囲が変わります。
| モード | 履歴 | ステージング | 作業ディレクトリ |
|---|---|---|---|
--soft | 巻き戻す | 保持 | 保持 |
--mixed(デフォルト) | 巻き戻す | リセット | 保持 |
--hard | 巻き戻す | リセット | 全消去 |
図で見ると以下のようになります。
よく使うパターン
直前のコミットを取り消したい(変更内容は残す)
git reset --soft HEAD~1ステージング状態に戻るので、コミットメッセージを書き直してやり直せます。
直前のコミットを取り消して、変更を作業ディレクトリに戻す
git reset HEAD~1
# --mixed はデフォルトなので省略可直前のコミットを完全に破棄する
git reset --hard HEAD~1--hard は編集内容も完全に消えます。一度使うと戻せないので慎重に。
HEAD~1 と HEAD^ の違い
どちらも「1つ前のコミット」を指します。好みで使い分けてOKです。
git reset HEAD~1 # 1つ前
git reset HEAD~3 # 3つ前
git reset HEAD^ # 1つ前(`~1` と同じ)
git reset HEAD^^ # 2つ前resetの危険性
resetはコミットを履歴から消します。ローカルでまだpushしていないコミットなら問題ありませんが、既にpushしたコミットをresetすると、他人の履歴と食い違いが発生します。
rebaseと同じ理由で、共有済みのコミットにresetを使ってはいけません。その場合は次の revert を使います。
git revert — 打ち消すコミットを作る
git revert は、指定したコミットを打ち消す「新しいコミット」を作るコマンドです。履歴は書き換えず、代わりに「あのコミットを取り消す」という意味のコミットを追加します。
git revert abc1234実行すると、以下のような履歴になります。
Revert C というコミットが追加され、結果として C の変更が打ち消されます。履歴には C も Revert C も両方残るので、誰の目にも「いったんこの変更を入れて、後で取り消した」と分かるのが特徴です。
revertのメリット
- 履歴を書き換えないので、共有ブランチでも安全
- 取り消した経緯が残るので、監査やレビューに強い
- 間違えたらさらにrevertすれば戻せる
いつrevertを使うか
- 既にmainにマージされた変更を取り消したい
- 本番リリース済みの変更を戻したい
- チームメンバーと共有しているブランチで過去のコミットを取り消したい
要するに「他の人がそのコミットを持っている可能性がある」なら revert です。
マージコミットをrevertする
マージコミットをrevertするときは少し特殊で、-m オプションでどちらの親を残すか指定します。
git revert -m 1 <マージコミットのハッシュ>1 は「1つ目の親(通常はマージ先のブランチ)を残す」という意味です。これを指定しないとエラーになります。
操作の違いを比較
3つのコマンドの違いをまとめます。
| 状況 | restore | reset | revert |
|---|---|---|---|
| ファイル編集を捨てたい | ◯ | △ | × |
| ステージングを取り消したい | ◯ | ◯ | × |
| 直前のローカルコミットを消したい | × | ◯ | △ |
| 共有済みのコミットを取り消したい | × | × | ◯ |
| 履歴を綺麗に保ちたい | ◯ | ◯ | × |
| 取り消した経緯を残したい | × | × | ◯ |
reflogという最終防衛ライン
「reset --hard してしまった、どうしよう」というときの救済策が git reflog です。
reflog は、あなたのHEADが過去にどのコミットを指していたかの記録です。ブランチを切り替えたり、resetしたり、rebaseしたりした履歴がすべて残っています。
git reflog
# abc1234 HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: feat: 新機能を追加
# ghi9012 HEAD@{2}: commit: fix: バグ修正うっかり消したコミットも、reflogに残っているハッシュを使えば復元できます。
git reset --hard def5678reflogは通常90日程度保存されるので、慌ててreset --hardをしても、当日や翌日なら復元できる可能性が高いです。「やらかしたらreflog」と覚えておきましょう。
よくあるハマりどころ
1. git reset --hard で変更が消えた
reflogを使って復元を試みてください。
git reflog
git reset --hard HEAD@{1}2. コミットメッセージだけ直したい
直前のコミットなら --amend が使えます。
git commit --amend -m "正しいメッセージ"pushする前なら安全、pushした後はrebaseと同じく履歴書き換えになるので注意。
3. git revert でコンフリクトした
revertも通常のマージと同様にコンフリクトすることがあります。解決方法は前章と同じです(編集 → git add → git revert --continue)。
ちゃんと使うためのポイント
- 未コミットの変更を戻すなら
restore - ローカルのコミットを消すなら
reset - 共有済みのコミットを取り消すなら
revert reset --hardは強力だが危険、reflogで救済できると覚える- pushする前なら割と自由、pushした後は慎重に
次の章では、ローカルとリモートの同期について、fetch・pull・push を正しく使い分ける方法を学びます。