【完全比較】git reset vs revert vs restore の違いと使い分け|–soft/–mixed/–hard・reflog 復旧・共有ブランチ対応まで徹底解説

【完全比較】git reset vs revert vs restore の違いと使い分け|–soft/–mixed/–hard・reflog 復旧・共有ブランチ対応まで徹底解説

Git で「変更を取り消したい」場面で登場する3つのコマンド、reset / revert / restore:

# 直前のコミットを取り消したい
git reset --soft HEAD~1
git reset --mixed HEAD~1
git reset --hard HEAD~1
git revert HEAD

# ファイルの変更を捨てたい
git restore file.txt
git checkout -- file.txt   # 古いやり方

一見似ていますが、動作も、リスクも、使うべき場面も全く違います。誤用すると:

  • 変更が消えて復旧できない
  • 他人の作業を破壊
  • 共有ブランチで大混乱
  • push できなくなる

現場では:

  • --soft / --mixed / --hard の違いが不明
  • reset と revert、どっちを使うべき?
  • 「共有ブランチでは revert」って本当?
  • Git 2.23 で登場した restore って何?
  • restorecheckout は何が違う?
  • reset –hard で消したコミットを取り戻せる?
  • ORIG_HEAD って何?
  • staging area に間違えて add したのを戻したい

など、混乱と誤解の温床。Git 初中級者と上級者を分ける最大の分岐点の一つです。

本記事では、git reset / git revert / git restore完全な違いと使い分けを、リファレンスとして実用的に整理します。3つのツリー(working / index / HEAD)の概念、reset の 3モード(–soft/–mixed/–hard)の動き、revert の仕組み、restore の新機能、restore vs checkoutreflog による事故復旧、ORIG_HEAD、使い分けフロー、Golden Rule、実践パターン、トラブル対応、FAQまで完全網羅。この1本で3コマンドを自信を持って使い分けられるようになります。


目次

結論:3秒で理解する使い分け

時間がない方向けに、最重要ポイントを先に示します。

早見表

コマンド主な用途履歴を書き換える?共有ブランチで使える?
git resetHEAD を移動、履歴を書き換え❌ 危険
git revert打ち消しコミットを作成❌(新コミット追加)安全
git restoreファイルの状態を戻す✅ 安全

3つの使い分けを図で

┌─────────────────────────────────────┐
│ 状況: 変更を取り消したい               │
└─────────────────────────────────────┘
              │
              ↓
      ┌───────────────┐
      │ push 済み?    │
      └───────────────┘
         │            │
         Yes           No
         │            │
         ↓            ↓
    ┌─────────┐  ┌─────────────────┐
    │ revert  │  │ 変更を残したい?   │
    └─────────┘  └─────────────────┘
                    │           │
                    Yes          No
                    │           │
                    ↓           ↓
              ┌──────────┐  ┌──────────┐
              │ reset    │  │ reset    │
              │ --soft   │  │ --hard   │
              │ --mixed  │  │          │
              └──────────┘  └──────────┘

覚えるべき最重要コマンド Top 10

# 直前のコミットを取り消し、変更は staged で保持
git reset --soft HEAD~1

# 直前のコミットを取り消し、変更は unstaged で保持
git reset HEAD~1     # デフォルトは --mixed

# 直前のコミットと変更を完全破棄(危険)
git reset --hard HEAD~1

# 特定コミットの打ち消しコミット作成(安全)
git revert HEAD
git revert abc1234

# staging area から下げる
git restore --staged file.txt
# または
git reset HEAD file.txt

# working tree の変更を捨てる
git restore file.txt
# または(古いやり方)
git checkout -- file.txt

# reset --hard で消えたコミットを復旧
git reflog
git reset --hard HEAD@{2}

詳細は以下で解説します。


Git の3つのツリー(前提知識)

reset を理解するには Git の3つの状態を押さえる必要があります:

┌──────────────────┐  ← エディタで変更中の状態
│  Working Tree    │
│  (作業ディレクトリ)│
└──────────────────┘
        │
        │ git add
        ↓
┌──────────────────┐  ← コミット予定リスト
│  Index (Staging) │
│  (ステージングエリア)│
└──────────────────┘
        │
        │ git commit
        ↓
┌──────────────────┐  ← 確定履歴
│  HEAD (Repository)│
│  (最新コミット)   │
└──────────────────┘

各ツリーの役割

ツリー意味確認方法
Working Tree現在のファイルの状態ファイルエディタで見える
Index (Staging)git add した内容git diff --staged
HEAD (Commit)直近の commit の状態git loggit show HEAD

reset モードとの関係

git reset の 3モードは、どのツリーを動かすかの違い:

モードHEADIndexWorking Tree
--soft動かす触らない触らない
--mixed(デフォルト)動かすリセット触らない
--hard動かすリセットリセット

これが理解できれば reset の 3モードは完全に理解できます。


git reset の詳細

基本構文

git reset [--soft | --mixed | --hard] <commit>

--soft:HEAD だけ動かす

git reset --soft HEAD~1

動作:

  • HEAD が1つ前のコミットに移動
  • Index は変更しない(staged 状態そのまま)
  • Working Tree は変更しない(ファイル内容そのまま)

用途:

  • 直前のコミットをやり直す(メッセージ変更、追加ファイル等)
  • 複数コミットを1つに統合

: メッセージ書き換え

git commit -m "Fix bug"
git reset --soft HEAD~1
git commit -m "Fix critical bug in payment flow"
# 内容は同じで、より良いメッセージに

: 3つのコミットを1つに

git log --oneline
# a1b2c3d Fix typo
# e4f5g6h Add validation  
# i7j8k9l Add feature

git reset --soft HEAD~3
# 3つ分の変更が全て staged 状態に

git commit -m "Add complete feature with validation"
# 1つの意味のあるコミットに

--mixed(デフォルト):Index も戻す

git reset HEAD~1
# または明示的に
git reset --mixed HEAD~1

動作:

  • HEAD が移動
  • Index が HEAD と一致するようにリセット
  • Working Tree は変更しない

つまり、コミット取り消し + 変更が unstaged に戻る。

用途:

  • git add を取り消す
  • コミットとステージを分けて再構成

: staging を戻す

git add .
git status
# Changes to be committed: file1, file2, file3

git reset
# staging をすべて解除、変更は残る

git status
# Changes not staged: file1, file2, file3

: 特定ファイルだけ unstage

git reset HEAD -- src/config.js
# または(Git 2.23+)
git restore --staged src/config.js

--hard:すべて破棄(危険)

git reset --hard HEAD~1

動作:

  • HEAD が移動
  • Index が HEAD と一致するようにリセット
  • Working Tree も HEAD と一致するようにリセット

つまり、未commit の変更は全て消える

⚠️ 未commit の変更は復旧困難。実行前に必ず git status で確認。

用途:

  • 実験的な変更を完全に破棄
  • リモートと同期し直したい

: リモートに戻す

git fetch origin
git reset --hard origin/main
# ローカルの変更を全て捨てて main に一致

HEAD の動き図解

Before:
  A ─ B ─ C ─ D ← HEAD, main

After git reset --hard B:
  A ─ B ← HEAD, main
        \
         C ─ D ← reflog に一時保存

C と D は削除ではなく到達不能になるだけ。reflog から復旧可能(後述)。

ORIG_HEAD

reset 前の HEAD 位置は自動で ORIG_HEAD に保存されます:

git reset --hard HEAD~3
# ミス!取り消したい

git reset --hard ORIG_HEAD
# reset 前の状態に戻る

merge や rebase の実行前の HEAD も ORIG_HEAD に保存されます。緊急の救済策として覚えておくと便利。

–keep / –merge

--soft / --mixed / --hard の他にも:

# ローカル変更を維持しつつリセット
git reset --keep <commit>

# マージ中の変更を維持
git reset --merge <commit>

用途は限定的。通常は3モードで十分。


git revert の詳細

基本構文

git revert <commit>
git revert HEAD           # 直前のコミット
git revert abc1234        # 特定コミット

動作

打ち消しコミットを新規作成:

Before:
  A ─ B ─ C ← HEAD

After git revert C:
  A ─ B ─ C ─ C' ← HEAD
              ↑
              C の変更を打ち消すコミット

履歴は残ったまま、C を打ち消すコミット C’ が追加される。

なぜ revert が「安全」か

  • 既存コミットのハッシュを変えない
  • 他人の履歴と整合が取れる
  • push しても問題ない

「Golden Rule of Rewriting History」を守るなら、共有ブランチでは必ず revert。

主な用途

  • 本番にバグを含むコミットを打ち消す
  • 共有ブランチの過去のコミットを取り消す
  • リリース後の緊急ロールバック

単一コミットを revert

git revert HEAD
# デフォルトメッセージ: "Revert 'original commit message'"

エディタが開き、メッセージ編集可能。

範囲指定

# HEAD~3 から HEAD までを revert
git revert HEAD~3..HEAD

# 個別に指定
git revert abc1234 def5678

# 逆順に処理される(新しい方から)

–no-commit(一括処理)

git revert --no-commit HEAD~3..HEAD
# 打ち消し変更が staging に集約される
git commit -m "Revert last 3 commits due to issue X"
# 1つの意味あるコミットに

マージコミットの revert

main:    A ─ B ─ M ← HEAD
              /
feature:    X ─ Y

M を revert する場合、どちらの親と比較するか指定が必要:

git revert -m 1 M
# -m 1: main 側(最初の親)に戻す
# -m 2: feature 側(2番目の親)に戻す

⚠️ マージコミットの revert は再マージ時に問題を起こす。慎重に。

revert 中の conflict

git revert abc1234
# CONFLICT (content): Merge conflict in ...

# 通常の conflict と同じ対応
vim src/config.js
git add src/config.js
git revert --continue

# 中止
git revert --abort

詳細はgit merge conflict の解決方法の記事も参照。


git restore(Git 2.23+)

背景

Git 2.23(2019年)で git checkout の責務が分割されました:

  • git switch: ブランチ切り替え専用
  • git restore: ファイル復元専用

理由:

  • checkout多機能すぎて混乱の元(ブランチ切替、ファイル復元、コミット確認)
  • 明示的な新コマンドで意図を明確に

対応表

旧 checkout新 コマンド
git checkout branchgit switch branch
git checkout -b new-branchgit switch -c new-branch
git checkout -- filegit restore file
git checkout abc1234 -- filegit restore --source=abc1234 file
git checkout abc1234(detached)git switch --detach abc1234

基本構文

git restore [options] <path>

working tree の変更を破棄

# ファイルの変更を破棄(Index の内容に戻す)
git restore file.txt

# 全ファイル
git restore .

# 特定ディレクトリ
git restore src/

動作:

  • Working Tree の変更を破棄
  • Index の内容(= 最新の staged 状態 or HEAD)に戻す

staging area から下げる(–staged)

# staged 状態を解除(変更は残す)
git restore --staged file.txt

これは以下と同じ:

git reset HEAD file.txt

git restore --staged の方が意図が明確(Git 2.23+ 推奨)。

特定コミットから復元(–source)

# 特定コミット時点のファイルを復元
git restore --source=abc1234 file.txt

# 特定ブランチのファイルを取り出す
git restore --source=main src/config.js

# HEAD~3 時点のファイル
git restore --source=HEAD~3 src/config.js

「あの時のあのファイル」を復元したい時に強力。

staging と working tree 両方に適用

git restore --staged --worktree file.txt
# または
git restore -SW file.txt

Index も Working Tree も HEAD の状態に戻す。

–patch(対話的)

git restore -p file.txt

hunk 単位で「破棄する / しない」を選択。

restore vs checkout

機能的にはほぼ同じですが:

観点git checkoutgit restore
登場初期からGit 2.23+
役割多目的ファイル復元専用
明確さ曖昧明確
エラー時の挙動上書きしがち安全
推奨互換性重視新規推奨

新規スクリプトは restore を使うのがベストプラクティス。


reset –hard で消えたコミットの復旧

reflog の存在

Git はあらゆる HEAD の動きreflog に記録します:

git reflog

出力:

a1b2c3d HEAD@{0}: reset: moving to HEAD~3
e4f5g6h HEAD@{1}: commit: Add feature C
i7j8k9l HEAD@{2}: commit: Add feature B
m0n1o2p HEAD@{3}: commit: Add feature A

消えたコミットを復旧

# ミス
git reset --hard HEAD~3
# 3コミット分の変更が消えた!

# reflog で確認
git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# e4f5g6h HEAD@{1}: commit: Add feature C   ← ここに戻したい

# 復旧
git reset --hard HEAD@{1}
# または hash 直接
git reset --hard e4f5g6h

到達不能なコミットを検索

reflog にない場合:

git fsck --lost-found
git fsck --unreachable | grep commit | cut -d' ' -f3 | \
  xargs git log --merges --no-walk

90日間残る

reflog はデフォルトで 90日間保存されます。それを過ぎると git gc で削除。

壊れたと思っても、たいてい救済可能。ただし早めに実行。

git reset –hard の落とし穴

⚠️ 以下の場合は救済不可:

  • 未commit の変更を破棄した場合
  • push していない未 commit
  • staging に一度も上がっていない変更

reflog は commit されたものを追跡するので、commit していない変更は履歴に残らない。

事前予防

危険な操作の前:

# 安全策1: ブランチ作成
git branch backup-before-reset

# 安全策2: stash
git stash push -u -m "before dangerous reset"

# 安全策3: reflog を確認する習慣
git log --oneline -10
git reflog

詳細はgit stash 使い方の記事も参照。


使い分けフロー完全図

「変更を取り消したい」時

質問1: すでに push した?

Yes → git revert(安全)
No  → 質問2 へ

質問2: 変更を残したい?

Yes → 質問3 へ
No  → git reset --hard

質問3: staged で残したい?

Yes → git reset --soft
No  → git reset --mixed(デフォルト)

「ファイルの変更を捨てたい」時

質問1: staging に上げた?

Yes → git restore --staged file
      (変更は残る、unstaged になるだけ)

No  → git restore file
      (working tree の変更を捨てる)

「特定のコミットの変更だけ戻したい」

質問1: そのコミットが最新?

Yes → git reset --soft HEAD~1
      + 部分的に選び直して commit

No  → git revert <commit>
      (途中コミットは reset で戻すと危険)

Golden Rule of Rewriting History

Git を扱う上での最重要ルール:

既に他人が持っているコミットを reset しない

なぜ危険か

[あなた] main: A ─ B ─ C ← push 済み

[チームメンバー] main: A ─ B ─ C ← pull 済み

あなたが reset --hard B して push:
main: A ─ B ← あなたのローカル & リモート

チームメンバーは:
main: A ─ B ─ C ← まだ C を持っている

チームメンバーが次に push すると、C を復活させる形になる。あるいは pull で衝突・混乱。

対応

  • 共有ブランチ(main, develop): revert 使用
  • 自分専用の feature ブランチ: reset OK
  • push 前のローカル: reset OK

force-with-lease で少しは安全

git push --force-with-lease は「他人が push していない場合のみ強制」なので、少しは安全:

git reset --hard HEAD~1
git push --force-with-lease origin feature
# 他人が push していれば拒否

ただし共有ブランチの reset はやめようが原則。

詳細はgit push rejected の記事も参照。


実践シナリオ

シナリオ1:直前のコミットメッセージを直したい

git commit --amend
# または
git reset --soft HEAD~1
git commit -m "Better message"

未 push なら amend が最速

シナリオ2:add したファイルを戻したい

git add unwanted_file.txt
git restore --staged unwanted_file.txt
# または
git reset HEAD unwanted_file.txt

シナリオ3:作業中の変更を捨てる

# 特定ファイル
git restore src/config.js

# 全部
git restore .

シナリオ4:リモートに完全同期

git fetch origin
git reset --hard origin/main
# ローカルの全ての変更を捨てて main に一致

シナリオ5:本番の緊急ロールバック

# バグ入りコミット e4f5g6h を打ち消し
git revert e4f5g6h
git push origin main

# 履歴に打ち消しが残る(監査可能)

シナリオ6:3コミットを1つに squash(未push)

git log --oneline
# a1b2c3d Fix typo
# e4f5g6h Fix another typo
# i7j8k9l Add feature

git reset --soft HEAD~3
git commit -m "Add feature (with typo fixes)"

git rebase -i でも同等の効果。詳細はgit rebase 使い方の記事も参照。

シナリオ7:昨日の作業を復元

# 昨日の状態
git reflog
# ...
# xyz789 HEAD@{yesterday}: commit: ...

git reset --hard HEAD@{yesterday}

# または時刻指定
git reset --hard HEAD@{1.day.ago}

シナリオ8:特定コミットのファイルを取り出す

# 現在のブランチはそのままで、あるコミットのファイルだけ復元
git restore --source=abc1234 src/config.js
# または
git checkout abc1234 -- src/config.js

シナリオ9:マージを取り消したい

# 未push なら reset
git reset --hard HEAD~1
# or
git reset --hard ORIG_HEAD  # マージ前の HEAD

# push 済みなら revert(マージコミット指定)
git revert -m 1 abc1234

シナリオ10:Rails 開発でのマイグレーション取り消し

# 間違ったマイグレーションをコミット
git commit -m "Add wrong migration"

# コミットとファイルを削除
git reset --soft HEAD~1
rm db/migrate/xxx_wrong_migration.rb
git add -u
git commit -m "Corrected migration"

# DB のマイグレーションも戻す
bin/rails db:rollback

詳細はrails db:migrate 使い方の記事も参照。


トラブルシューティング

reset –hard で消えたコミットを取り戻したい

git reflog
# 消える前の HEAD を探す

git reset --hard HEAD@{2}

restore が「pathspec did not match any file」

git restore file.txt
# error: pathspec 'file.txt' did not match any file(s) known to git.

→ ファイルが tracked でない。まだ commit されたことがないファイル:

# untracked ファイルなら
rm file.txt   # 手動削除

--source で古いコミットのファイルが取れない

git restore --source=very_old_hash file.txt
# fatal: could not resolve 'very_old_hash'

→ hash が間違い、または git gc で削除済み:

git log --all --oneline | head
# 有効な hash を探す

push しようとしたら “non-fast-forward”

! [rejected] main -> main (non-fast-forward)

→ reset で履歴が変わったのに push しようとしている:

# 自分専用ブランチなら
git push --force-with-lease origin feature

# main 等の共有ブランチなら
# NG: force push しない
# → revert に切り替え

詳細はgit push rejected の記事も参照。

revert で conflict

git revert abc1234
# CONFLICT

# 解決 → add → --continue
vim conflict_file
git add conflict_file
git revert --continue

staging から下げたが変更が消える

# ❌ 間違い
git reset --hard HEAD file.txt
# → --hard は変更も消す!

# ✅ 正しい
git reset HEAD file.txt
# または
git restore --staged file.txt

merge を取り消したい

# 直前の merge が問題
git reset --hard ORIG_HEAD
# または(push 済み)
git revert -m 1 <merge_commit>

rebase を取り消したい

git reflog
# rebase 前の HEAD を探す

git reset --hard HEAD@{5}  # 例

詳細はgit rebase 使い方の記事も参照。


よくある質問(FAQ)

Q1. reset と revert、どう使い分ける?

  • push 前: reset OK(自由に履歴書き換え)
  • push 後: revert 必須(安全)
  • 共有ブランチ: 常に revert

Q2. –mixed(デフォルト)が最も一般的な理由

コミット取り消し + 変更保持 + 再ステージング可能。柔軟性最大なため。

Q3. git resetgit restore --staged の違い

git reset HEAD file           # 古い書き方(動く)
git restore --staged file     # 新しい書き方(Git 2.23+)

機能は同じ、restore –staged は意図が明確

Q4. git checkout はもう使わない?

まだ使える(deprecated ではない)。restore / switch は補完的な位置づけ。互換性のために checkout も残る。

新規は restore / switch 推奨、古いスクリプトの checkout はそのままでOK。

Q5. reset –hard で消したコミットを絶対に取り戻したい

git reflog                   # 履歴確認
git reset --hard HEAD@{N}    # 戻す

# または fsck で
git fsck --lost-found

90日以内なら大抵救済可能。

Q6. revert で複数コミットまとめて

git revert --no-commit HEAD~3..HEAD
git commit -m "Revert last 3 commits"

Q7. マージコミットの revert が難しい

git revert -m 1 <merge_commit>
# -m 1: main 側に戻す

# 再マージすると復活する問題あり
# → 事前に相談してから実行

Q8. git reset 引数なしとの違い

git reset          # = git reset --mixed HEAD(すべて unstage)
git reset HEAD~1   # = git reset --mixed HEAD~1

Q9. Working tree の変更を全部捨てる

git restore .
# または
git checkout -- .   # 古い書き方

Q10. tracked 全部を HEAD 状態に

git reset --hard HEAD

⚠️ 未 commit の変更が全て消える。

Q11. detached HEAD 状態から戻る

git switch main
# または
git checkout main

Q12. IDE / GUI での操作

  • VS Code: Source Control で右クリック
  • GitKraken: 対象コミット右クリック → Reset / Revert
  • SourceTree: 対象コミット右クリック → Reverse commit / Reset current branch

参考リンク・関連資料

Git 公式

関連記事(本サイト)


まとめ

git reset / git revert / git restore の使い分け、要点を再整理します。

3コマンドの本質

コマンド何をどこに履歴書き換え
resetHEAD/Index/Tree過去のコミット
revert打ち消しコミット追加新規コミット
restoreファイル状態を復元Index/Working Tree

reset 3モード

モードHEADIndexWorking Tree用途
--soft動くコミットやり直し
--mixed(デフォルト)動く戻すステージ解除
--hard動く戻す戻す完全破棄

使い分け早見表

状況コマンド
push 済みコミットの取り消しgit revert <commit>
未push コミットのやり直し(メッセージ等)git reset --soft HEAD~1
未push コミットのステージング再構成git reset HEAD~1
変更を完全に捨てるgit reset --hard <commit>
ファイルの working tree 変更を捨てるgit restore <file>
特定ファイルの add 取り消しgit restore --staged <file>
特定コミット時点のファイル復元git restore --source=<commit> <file>
リモートに完全同期git reset --hard origin/main

Golden Rule

共有ブランチ(push 済み)→ revert
自分専用ブランチ / 未push → reset OK

事故時の救済

git reflog                    # 履歴確認
git reset --hard HEAD@{N}     # 過去の状態に戻す
git reset --hard ORIG_HEAD    # 直前の危険操作の前に戻す

reflog は 90日残る。

restore vs checkout

# 古い書き方(動く)
git checkout -- file
git checkout branch
git checkout abc1234 -- file

# 新しい書き方(推奨)
git restore file
git switch branch
git restore --source=abc1234 file

推奨設定

# エイリアス
git config --global alias.rs 'restore'
git config --global alias.rss 'restore --staged'
git config --global alias.undo 'reset --soft HEAD~1'
git config --global alias.uncommit 'reset --mixed HEAD~1'

# 危険な操作の確認プロンプト(zsh の例)
alias 'grh'='echo "reset --hard は危険!本当に?" && git reset --hard'

事故防止

  • git reset --hard の前: git status / git log 確認
  • 共有ブランチ: 常に revert
  • 危険な作業前: git branch backup or git stash
  • ORIG_HEAD を知っておく
  • reflog を活用する習慣

現代の推奨

  • 新規スクリプト: restore / switch を使う
  • checkout は互換性維持のため残す
  • –staged で意図を明確に
  • 共有ブランチには revert

これらの知識は、Git 操作・チーム開発・トラブル対応・履歴管理・緊急ロールバックなど、あらゆる場面で活用できます。本記事をブックマークしておけば、3コマンドの使い分けにもう迷わないようになります。


本記事は2026年6月時点の情報をもとに、Git 2.40+ での動作確認・公式ドキュメントに基づき作成しています。Git のバージョンによって挙動が異なる場合があるため、最新の情報はGit公式ドキュメントもあわせてご確認ください。