T-CREATOR

git reset の使い分け完全ガイド:soft・mixed・hard の違いを理解する

git reset の使い分け完全ガイド:soft・mixed・hard の違いを理解する

Git を使った開発作業をしていると、コミット後に「あれ、間違えた!」と気づく瞬間がありますよね。そんなときに頼りになるのが git reset コマンドです。しかし、--soft--mixed--hard という 3 つのオプションがあって、どれを選べばよいのか迷ってしまうことも多いでしょう。

間違った選択をすると、大切な作業が消えてしまう可能性もあります。でも安心してください。今回は、これらの違いを図解でわかりやすく解説し、実際の開発現場で適切に使い分けられるようになるまでご案内いたします。

背景

Git のコミット管理の仕組み

Git の内部構造を理解することで、git reset がなぜ必要で、どのように動作するのかが見えてきます。

Git は以下の 3 つのエリアでファイルの状態を管理しています。

mermaidflowchart LR
    work[ワーキングディレクトリ] -->|git add| stage[ステージングエリア]
    stage -->|git commit| repo[リポジトリ]

    work -.->|git reset --hard| stage
    stage -.->|git reset --mixed| repo
    repo -.->|git reset --soft| history[コミット履歴]

図で理解できる要点:

  • ワーキングディレクトリ:実際に編集しているファイル
  • ステージングエリア:次のコミットに含める予定のファイル
  • リポジトリ:コミット済みのファイル

ワーキングディレクトリ(Working Directory) 実際にファイルを編集している作業領域です。ここでコードを書いたり、設定ファイルを変更したりします。

ステージングエリア(Staging Area) 次のコミットに含めるファイルを一時的に保管する場所です。git add コマンドでファイルをここに追加します。

javascript// 作業中のファイル example.js
function hello() {
  console.log('Hello World!'); // 新しく追加した行
}

上記のファイルを編集後、以下のコマンドでステージングエリアに追加します。

bashgit add example.js

リポジトリ(Repository) git commit でステージングエリアの内容が永続的に保存される場所です。ここに保存された内容は Git の履歴として管理されます。

bashgit commit -m "Hello World関数を追加"

なぜリセット機能が必要なのか

開発作業では、以下のようなシチュエーションが頻繁に発生します。

コミットメッセージの間違い 急いでコミットしたときに、わかりにくいメッセージを書いてしまうことがあります。

bashgit commit -m "fix"  # これでは何を修正したかわからない

間違ったファイルをコミット 本来コミットすべきでないファイル(設定ファイルやテスト用のダミーデータなど)を誤って含めてしまうケースです。

bashgit add .  # 全ファイルを追加
git commit -m "機能追加"  # 不要なファイルも含まれている

実装のやり直し 一度コミットしたものの、別のアプローチで実装し直したい場合があります。

このような状況で、安全に過去の状態に戻るために git reset が必要になります。

git reset の基本概念

git reset は指定したコミットの状態まで戻すコマンドです。重要なのは「どこまで戻すか」を 3 つのオプションで制御できる点です。

以下の図は、リセット前後の状態変化を示しています。

mermaidflowchart TB
    subgraph before[リセット前]
        b1[コミットA] --> b2[コミットB] --> b3[コミットC HEAD]
        b4[ワーキングディレクトリ:変更あり]
        b5[ステージングエリア:変更あり]
    end

    subgraph after[リセット後 git reset --soft HEAD~1]
        a1[コミットA] --> a2[コミットB HEAD]
        a4[ワーキングディレクトリ:変更あり]
        a5[ステージングエリア:変更あり]
    end

    before --> after

図で理解できる要点:

  • HEAD はカレントブランチの最新コミットを指すポインタ
  • HEAD~1 は 1 つ前のコミットを表す
  • リセットによりコミット履歴が変更される

基本的な構文は以下の通りです。

bashgit reset [オプション] [コミット指定]

主なコミット指定方法

指定方法意味
HEAD~11 つ前のコミット
HEAD~22 つ前のコミット
abc123特定のコミットハッシュ
mainmain ブランチの最新コミット

課題

git reset を間違えて使うとどうなるか

git reset を誤って使用すると、以下のような深刻な問題が発生する可能性があります。

作業内容の完全消失 最も危険なのは --hard オプションの誤用です。未保存の変更がすべて失われてしまいます。

bash# 危険な例:作業中の変更がすべて消える
git reset --hard HEAD~3

Error: fatal: HEAD~3: cannot resolve revision 存在しないコミットを指定した場合のエラーです。

復旧困難な状況 一度実行してしまうと、元の状態に戻すのが非常に困難になるケースがあります。

bash# 間違った例:重要なコミットを削除
git reset --hard abc123  # abc123が存在しない場合

チーム開発での影響 既にプッシュ済みのコミットをリセットすると、他のメンバーとの履歴に齟齬が生じます。

各オプションの違いがわからない問題

多くの開発者が混乱する理由は、3 つのオプションの動作範囲の違いが直感的でないことです。

以下の表で、各オプションの影響範囲を比較します。

オプションコミット履歴ステージングエリアワーキングディレクトリ
--softリセット保持保持
--mixedリセットリセット保持
--hardリセットリセットリセット

よくある勘違い

bash# 勘違い例1:--softでファイルの変更も戻ると思い込む
git reset --soft HEAD~1
# 実際:コミットのみリセット、ファイルの変更は残る

# 勘違い例2:--mixedで作業ディレクトリも変わると思い込む
git reset --mixed HEAD~1
# 実際:ステージングエリアのみリセット、ファイルの変更は残る

データ損失の危険性

Git は基本的にデータを失わないように設計されていますが、git reset --hard は例外的にデータを完全に削除する可能性があります。

回復不可能なケース

bash# 未コミットの変更がある状態で実行
echo "重要な変更" > important.txt
git reset --hard HEAD~1  # important.txtの変更が失われる

Error: error: Your local changes to the following files would be overwritten このエラーは Git が親切に出してくれる警告です。無視してはいけません。

安全な回復方法の限界 git reflog で履歴を確認できますが、ワーキングディレクトリの変更は記録されません。

bash# reflogで確認(コミットのみ)
git reflog
# ab1234c HEAD@{0}: reset: moving to HEAD~1
# def5678 HEAD@{1}: commit: 重要な機能追加

解決策

git reset --soft の使い方

--soft オプションは最も安全なリセット方法です。コミット履歴のみを変更し、ステージングエリアとワーキングディレクトリの内容は保持されます。

基本的な使用方法

bash# 直前のコミットを取り消す
git reset --soft HEAD~1

実行後の状態確認をしてみましょう。

bash# 現在の状態を確認
git status
# On branch main
# Changes to be committed:
#   modified: example.js
#   new file: feature.js

この例では、コミットは取り消されましたが、変更内容はステージングエリアに残っています。そのため、すぐに新しいコミットメッセージで再コミットできます。

コミットメッセージの修正に最適

bash# 元のコミット
git commit -m "typo fix"

# より適切なメッセージで再コミット
git reset --soft HEAD~1
git commit -m "ユーザー入力バリデーション機能のtypo修正"

複数のコミットをまとめる場合

bash# 3つのコミットを1つにまとめる
git reset --soft HEAD~3
git commit -m "ユーザー認証機能の実装"

git reset --mixed の使い方

--mixed はデフォルトオプションで、コミット履歴とステージングエリアをリセットしますが、ワーキングディレクトリの内容は保持します。

基本構文

bash# 以下は同じ意味
git reset HEAD~1
git reset --mixed HEAD~1

ステージングエリアのリセット用途

間違ってステージングエリアに追加してしまったファイルを取り消したい場合に便利です。

bash# 誤って全ファイルを追加
git add .

# 特定のファイルのステージングを取り消し
git reset HEAD sensitive-config.txt

実行後の確認をしてみます。

bashgit status
# On branch main
# Changes not staged for commit:
#   modified: example.js
#   modified: feature.js
# Untracked files:
#   sensitive-config.txt

段階的なコミット作成 大きな変更を複数の小さなコミットに分割したい場合に活用できます。

bash# 複数ファイルの変更を含むコミットをリセット
git reset --mixed HEAD~1

# 段階的に再コミット
git add feature-auth.js
git commit -m "認証機能の追加"

git add feature-validation.js
git commit -m "バリデーション機能の追加"

git reset --hard の使い方

--hard オプションは最も強力で、同時に最も危険な方法です。コミット履歴、ステージングエリア、ワーキングディレクトリのすべてを指定したコミットの状態に戻します。

基本的な使用方法

bash# すべてを指定コミットの状態に戻す
git reset --hard HEAD~1

使用前の安全確認

bash# 現在の変更を確認
git status
git diff

# 重要な変更がある場合は一時保存
git stash push -m "作業中の変更を一時保存"

完全なやり直しが必要な場合

実装を根本的に変更したい場合や、実験的な変更を完全に破棄したい場合に使用します。

bash# 実験的なブランチで試行錯誤後
git log --oneline
# abc123 実験的な機能C
# def456 実験的な機能B
# ghi789 実験的な機能A
# jkl012 安定版

# 安定版に完全に戻す
git reset --hard jkl012

リモートブランチとの同期

bash# リモートの最新状態に完全に合わせる
git fetch origin
git reset --hard origin/main

安全にリセットする方法

Git リセットを安全に実行するための手順をご紹介します。

事前バックアップの作成

bash# 現在の状態でブランチを作成(バックアップ)
git branch backup-$(date +%Y%m%d-%H%M%S)

# または現在のコミットハッシュをメモ
git rev-parse HEAD
# 出力例:abc123def456...

段階的な確認手順

  1. 現在の状態を詳細に確認
  2. バックアップの作成
  3. リセットの実行
  4. 結果の検証
bash# 1. 現在の状態確認
git status
git log --oneline -5

# 2. バックアップ作成
git branch backup-before-reset

# 3. リセット実行(例:soft)
git reset --soft HEAD~1

# 4. 結果検証
git status
git log --oneline -5

回復方法の準備

万が一の際の回復手順も把握しておきましょう。

bash# reflogで操作履歴を確認
git reflog
# abc123 HEAD@{0}: reset: moving to HEAD~1
# def456 HEAD@{1}: commit: 重要な機能

# 特定の状態に戻す
git reset --hard HEAD@{1}

具体例

ケース 1:コミットメッセージを修正したい場合

開発中によくある「コミットメッセージをもっとわかりやすくしたい」というケースです。

状況設定 ユーザー認証機能を実装して、以下のようにコミットしたとします。

bashgit add auth.js
git commit -m "fix"  # わかりにくいメッセージ

解決手順

以下の図は、コミットメッセージ修正の流れを示しています。

mermaidsequenceDiagram
    participant W as ワーキングディレクトリ
    participant S as ステージングエリア
    participant R as リポジトリ

    Note over R: コミット: "fix"
    R->>S: git reset --soft HEAD~1
    Note over S: 変更内容が戻る
    S->>R: git commit -m "適切なメッセージ"
    Note over R: コミット: "ユーザー認証機能のバグ修正"

実際のコマンド操作は以下の通りです。

bash# 1. 現在の状態確認
git log --oneline -3
# abc123 fix
# def456 ユーザー登録機能の追加
# ghi789 初期設定

# 2. ソフトリセットでコミットのみ取り消し
git reset --soft HEAD~1

# 3. 状態確認(変更内容はステージングエリアに残る)
git status
# On branch main
# Changes to be committed:
#   modified: auth.js

# 4. 適切なメッセージで再コミット
git commit -m "ユーザー認証機能のセッション管理バグ修正"

確認結果

bashgit log --oneline -3
# bcd234 ユーザー認証機能のセッション管理バグ修正
# def456 ユーザー登録機能の追加
# ghi789 初期設定

ケース 2:ステージングエリアをリセットしたい場合

間違ったファイルをステージングエリアに追加してしまった場合の対処法です。

状況設定 プロジェクトファイルを整理中に、機密情報を含む設定ファイルも一緒にステージングしてしまいました。

bash# 作業ファイルを編集
echo "新機能の実装" > feature.js
echo "SECRET_KEY=12345" > .env  # 機密情報

# 誤って全ファイルを追加
git add .

解決手順

bash# 1. 現在のステージング状況確認
git status
# On branch main
# Changes to be committed:
#   new file: feature.js
#   new file: .env

# 2. 機密ファイルのみステージングから削除
git reset HEAD .env

# 3. 確認
git status
# On branch main
# Changes to be committed:
#   new file: feature.js
# Untracked files:
#   .env

より安全な方法 .gitignore ファイルで事前に除外設定をしておきます。

bash# .gitignoreファイルに追加
echo ".env" >> .gitignore
echo "*.log" >> .gitignore
echo "node_modules/" >> .gitignore

# .gitignoreをコミット
git add .gitignore
git commit -m ".gitignoreファイルの追加"

ケース 3:作業ディレクトリごと元に戻したい場合

実験的な変更を完全に破棄して、安定した状態に戻したいケースです。

状況設定 新しいライブラリを試していたところ、プロジェクトが不安定になってしまいました。

bash# 実験的な変更を複数実施
git log --oneline -5
# abc123 ライブラリBの試行錯誤
# def456 ライブラリAの実験
# ghi789 設定ファイル変更
# jkl012 安定版(ここに戻したい)
# mno345 前回の機能追加

解決手順

bash# 1. 作業内容を一時保存(必要に応じて)
git stash push -m "実験中の変更を一時保存"

# 2. 安全なコミットまで完全にリセット
git reset --hard jkl012

# 3. 状態確認
git log --oneline -3
# jkl012 安定版
# mno345 前回の機能追加
# pqr678 その他の機能

git status
# On branch main
# nothing to commit, working tree clean

代替手段:新しいブランチでの再開

bash# 別のアプローチ:新しいブランチを作成
git checkout -b feature-new-library jkl012
# 実験を継続する場合の安全な方法

ケース 4:複数コミットを取り消したい場合

機能実装を段階的に進めていたが、設計を見直すことになったケースです。

状況設定 ユーザー管理機能を 3 段階で実装していましたが、要件変更により全体を作り直すことになりました。

bashgit log --oneline -6
# abc123 ユーザー削除機能
# def456 ユーザー編集機能
# ghi789 ユーザー表示機能
# jkl012 基盤となるコミット(ここまで戻す)
# mno345 その他の機能
# pqr678 初期コミット

解決手順

複数コミットのリセットを図で説明します。

mermaidflowchart LR
    subgraph before[リセット前]
        b1[基盤コミット] --> b2[表示機能] --> b3[編集機能] --> b4[削除機能 HEAD]
    end

    subgraph after[リセット後]
        a1[基盤コミット HEAD]
        a2[未コミット: 表示機能コード]
        a3[未コミット: 編集機能コード]
        a4[未コミット: 削除機能コード]
    end

    before --> after

    style b4 fill:#ffcccc
    style a1 fill:#ccffcc

図で理解できる要点:

  • 3 つのコミットが取り消される
  • コードの変更内容は保持される(--soft の場合)
  • 基盤コミットから再スタートできる
bash# 1. バックアップブランチ作成
git branch backup-user-management

# 2. 基盤コミットまでソフトリセット
git reset --soft jkl012

# 3. 状態確認
git status
# On branch main
# Changes to be committed:
#   new file: user-display.js
#   new file: user-edit.js
#   new file: user-delete.js

git log --oneline -3
# jkl012 基盤となるコミット
# mno345 その他の機能
# pqr678 初期コミット

新しい設計でのコミット

bash# 新しい設計に基づいて再実装
git reset HEAD  # ステージングエリアもクリア
rm user-*.js   # 古い実装ファイルを削除

# 新しい設計で実装
echo "新しいユーザー管理クラス" > UserManager.js
git add UserManager.js
git commit -m "新設計によるユーザー管理機能の基盤実装"

まとめ

git reset の使い分けチートシート

適切な git reset オプションを素早く選択できるよう、チートシートをまとめました。

シチュエーション使用オプションコマンド例注意点
コミットメッセージ修正--softgit reset --soft HEAD~1最も安全
ステージング取り消し--mixedgit reset HEAD file.txtデフォルト
完全なやり直し--hardgit reset --hard HEAD~2危険
複数コミットの統合--softgit reset --soft HEAD~3再コミット必要
実験的変更の破棄--hardgit reset --hard origin​/​mainバックアップ推奨

緊急時の判断フロー

mermaidflowchart TD
    start([git reset が必要]) --> q1{コードの変更を保持したい?}

    q1 -->|はい| q2{ステージングエリアも保持したい?}
    q1 -->|いいえ| hard[git reset --hard]

    q2 -->|はい| soft[git reset --soft]
    q2 -->|いいえ| mixed[git reset --mixed]

    soft --> safe1[最も安全]
    mixed --> safe2[比較的安全]
    hard --> danger[注意が必要]

    style soft fill:#ccffcc
    style mixed fill:#ffffcc
    style hard fill:#ffcccc

覚えておきたいポイント

1. 安全性の順序 --soft > --mixed > --hard の順で安全です。迷ったときは --soft から試しましょう。

2. バックアップの重要性

bash# 必ず実行前にバックアップ
git branch backup-$(date +%Y%m%d-%H%M%S)

3. reflog での復旧

bash# 操作履歴の確認
git reflog

# 特定の状態への復旧
git reset --hard HEAD@{2}

4. チーム開発での注意 プッシュ済みのコミットは原則としてリセットしないこと。代わりに git revert を検討してください。

bash# 安全な取り消し方法(プッシュ済みの場合)
git revert HEAD~1

5. 作業フローでの活用

  • 開発中: --soft でコミット整理
  • 実験時: --hard で完全リセット
  • ステージング管理: --mixed で細かい調整

関連リンク