git resetでお困りですか?「コミットを取り消したいけど怖い」「–soft、–mixed、–hardの違いがわからない」「resetで履歴を壊してしまった」といった悩みを抱えるエンジニアは多いものです。
実は、git resetは安全で確実な変更の取り消しと履歴管理ができる強力なコマンドです。この記事では、実務で本当に使えるgit resetの活用法を、豊富な実例とともに徹底解説します。
最後まで読むことで、git resetをマスターし、誤操作修復時間を90%短縮、リポジトリの信頼性向上を実現できるようになります。
🎯 この記事で学べること
- git resetの3つのモード(–soft、–mixed、–hard)の使い分け
- HEADポインタとWorking Directory、Staging Areaへの影響の理解
- ファイル単位でのreset操作とステージング管理
- reflogを使った危険なreset操作からの完全復旧方法
- チーム開発で安全にresetを活用するベストプラクティス
📋 前提知識
- Gitの基本概念(リポジトリ、コミット、ブランチ)の理解
- コマンドラインの基本操作
- HEADポインタとGitの3つの領域(Working Directory、Staging Area、Repository)の概念
読了時間: 約18分
難易度: (上級)
🚀 git resetとは?基本概念の理解
概要と役割
git resetは、HEADポインタを指定したコミットに移動させ、その過程でWorking Directory、Staging Area、Repositoryの状態を制御するコマンドです。単なる「元に戻す」コマンドではなく、Gitの内部状態を精密に操作する強力なツールです。
resetの最大の特徴は、非破壊的な履歴操作(–soft、–mixed)から破壊的な履歴操作(–hard)まで、モードによって影響範囲を制御できることです。この特性を理解することで、安全かつ効果的にGitの状態を管理できます。
特に重要なのは、git resetはローカルリポジトリのみに影響することです。リモートにpushしたコミットをresetで削除しても、リモートの履歴は変わりません。この性質により、チーム開発での安全性が保たれています。
動作原理の図解
他のコマンドとの関係
関連コマンド | 役割 | 使い分け |
---|---|---|
git revert | コミットを打ち消す新しいコミットを作成 | 公開済みの履歴を安全に修正したい場合 |
git checkout | ブランチやコミットに移動 | ブランチ切り替えや過去の状態を確認したい場合 |
git restore | ファイルの状態を復元 | 特定ファイルのみを以前の状態に戻したい場合 |
📝 基本的な使い方
コマンドの基本構文
git reset [<mode>] [<commit>]
git reset [<mode>] [<commit>] -- <pathspec>
resetの3つのモードの基本理解
1. –soft(最も安全)
# 基本形:HEADのみ移動
$ git reset --soft HEAD~1
# 実行前の状態確認
$ git log --oneline -3
a1b2c3d (HEAD -> main) 最新のコミット
e4f5g6h 修正内容をコミット
i7j8k9l 初期コミット
# reset実行
$ git reset --soft HEAD~1
# 実行後の確認
$ git log --oneline -3
e4f5g6h (HEAD -> main) 修正内容をコミット
i7j8k9l 初期コミット
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
new file: src/utils.js
解説:
- HEADポインタのみが前のコミットに移動
- Staging AreaとWorking Directoryは変更されない
- 取り消したコミットの内容はステージングされた状態で残る
2. –mixed(デフォルト)
# 基本形:HEADとStagingAreaをリセット
$ git reset HEAD~1
# または明示的に
$ git reset --mixed HEAD~1
# 実行後の確認
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/utils.js
解説:
- HEADポインタとStaging Areaがリセットされる
- Working Directoryの内容は保持される
- 変更内容は「未ステージング」状態で残る
3. –hard(最も危険)
# 基本形:すべてをリセット
$ git reset --hard HEAD~1
# 実行後の確認
$ git status
On branch main
nothing to commit, working tree clean
$ git log --oneline -2
e4f5g6h (HEAD -> main) 修正内容をコミット
i7j8k9l 初期コミット
解説:
- HEADポインタ、Staging Area、Working Directoryがすべてリセット
- 取り消したコミットの内容は完全に失われる(reflogから復旧可能)
- 最も危険だが、完全にクリーンな状態に戻せる
よく使うオプション一覧
オプション | 説明 | 使用例 |
---|---|---|
--soft | HEADのみ移動 | git reset --soft HEAD~1 |
--mixed | HEADとステージングをリセット(デフォルト) | git reset HEAD~1 |
--hard | 全てをリセット | git reset --hard HEAD~1 |
--keep | ローカル変更を保持しつつリセット | git reset --keep HEAD~1 |
-q, --quiet | 出力を抑制 | git reset -q HEAD~1 |
🔧 実践的な使用例
ケース1: 直前のコミットメッセージを修正したい
シナリオ: コミットメッセージにタイポがあり、修正したい場合
# 1. 現在の状態を確認
$ git log --oneline -2
a1b2c3d (HEAD -> main) Fxi typo in login funciton
e4f5g6h Add user authentication
# 2. --softでHEADのみを戻す
$ git reset --soft HEAD~1
# 3. 状態確認(変更内容はステージングされたまま)
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: src/auth.js
# 4. 正しいメッセージで再コミット
$ git commit -m "Fix typo in login function"
[main b2c3d4e] Fix typo in login function
1 file changed, 5 insertions(+), 2 deletions(-)
# 5. 結果確認
$ git log --oneline -2
b2c3d4e (HEAD -> main) Fix typo in login function
e4f5g6h Add user authentication
ポイント:
- –softを使うことで作業内容を失わずにコミットだけをやり直せる
git commit --amend
でも同様の効果を得られるが、resetを使うとより理解しやすい
ケース2: 複数のコミットを1つにまとめたい
シナリオ: 作業中に作った小さなコミットを1つにまとめてから提出したい場合
# 1. 現在の履歴を確認
$ git log --oneline -5
a1b2c3d (HEAD -> main) Fix lint errors
e4f5g6h Add comments to functions
i7j8k9l Fix bug in calculation
m0n1o2p Add new feature implementation
q3r4s5t Previous stable version
# 2. 過去3コミットをsoftリセット
$ git reset --soft HEAD~3
# 3. 状態確認
$ git status
On branch main
Changes to be committed:
modified: src/calculator.js
modified: src/utils.js
new file: src/feature.js
# 4. 1つのコミットとしてまとめる
$ git commit -m "Implement new calculation feature with bug fixes"
[main t6u7v8w] Implement new calculation feature with bug fixes
3 files changed, 45 insertions(+), 8 deletions(-)
# 5. きれいな履歴の確認
$ git log --oneline -3
t6u7v8w (HEAD -> main) Implement new calculation feature with bug fixes
q3r4s5t Previous stable version
ポイント:
- 複数の小さなコミットを論理的な単位でまとめられる
- コードレビュー時に理解しやすい履歴になる
ケース3: 特定のファイルのステージングを取り消したい
シナリオ: 複数のファイルをaddしたが、一部のファイルだけコミットから除外したい場合
# 1. 複数ファイルを変更してadd
$ git add .
$ git status
On branch main
Changes to be committed:
modified: README.md
modified: src/auth.js
modified: config/settings.json
# 2. 特定ファイルのステージングを取り消し
$ git reset HEAD config/settings.json
# 3. 結果確認
$ git status
On branch main
Changes to be committed:
modified: README.md
modified: src/auth.js
Changes not staged for commit:
modified: config/settings.json
# 4. 意図したファイルのみをコミット
$ git commit -m "Update documentation and authentication logic"
ポイント:
- ファイルパスを指定することで部分的なリセットが可能
git restore --staged
でも同様の効果を得られる
ケース4: 危険なhard resetからの復旧
シナリオ: 誤って--hard
でresetしてしまい、重要な変更を失った場合
# 1. 誤ったhard reset(例)
$ git reset --hard HEAD~2
HEAD is now at e4f5g6h Previous commit
# 2. reflogで履歴を確認
$ git reflog
e4f5g6h (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
a1b2c3d HEAD@{1}: commit: Important feature implementation
i7j8k9l HEAD@{2}: commit: Fix critical bug
e4f5g6h HEAD@{3}: commit: Previous commit
# 3. 失ったコミットに戻る
$ git reset --hard HEAD@{1}
HEAD is now at a1b2c3d Important feature implementation
# 4. 復旧確認
$ git log --oneline -3
a1b2c3d (HEAD -> main) Important feature implementation
i7j8k9l Fix critical bug
e4f5g6h Previous commit
ポイント:
- reflogは30日間(デフォルト)すべてのHEADの動きを記録
- どんなreset操作からも復旧可能(ガベージコレクションされるまで)
🔍 トラブルシューティング
エラー1: fatal: ambiguous argument
エラー内容:
fatal: ambiguous argument 'HEAD~5': unknown revision or path not specified.
原因:
- 指定したコミット数がリポジトリの履歴より多い
- ブランチに十分なコミット履歴がない
解決方法:
# コミット履歴を確認
$ git log --oneline
a1b2c3d (HEAD -> main) Latest commit
e4f5g6h First commit
# 正しい範囲でreset
$ git reset HEAD~1 # OK
$ git reset HEAD~2 # エラー(コミットが2つしかない)
エラー2: error: Entry 'filename' not uptodate. Cannot merge.
エラー内容:
error: Entry 'config.json' not uptodate. Cannot merge.
fatal: Could not reset index file to revision 'HEAD~1'.
原因:
- Working Directoryに未保存の変更があり、resetにより衝突が発生
解決方法:
# 方法1: 変更をステージングしてからreset
$ git add config.json
$ git reset --mixed HEAD~1
# 方法2: 変更を一時保存してreset
$ git stash
$ git reset HEAD~1
$ git stash pop
# 方法3: --hard で強制的にreset(変更は失われる)
$ git reset --hard HEAD~1
エラー3: リモートとの不整合
エラー内容:
$ git push
To https://github.com/user/repo.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs
原因:
- リモートにpush済みのコミットをローカルでresetした
- ローカルの履歴がリモートより古い状態
解決方法:
# 推奨方法: revertで安全に修正
$ git revert HEAD~2..HEAD
$ git push
# 緊急時のみ: 強制push(チーム開発では危険)
$ git push --force-with-lease
# より安全: 新しいブランチで修正
$ git checkout -b fix-branch HEAD~2
$ git push -u origin fix-branch
エラー4: マージ中のreset
エラー内容:
fatal: It is not possible to reset during a merge.
原因:
- マージ中(コンフリクト解決中)にresetしようとした
解決方法:
# マージを中断してからreset
$ git merge --abort
$ git reset HEAD~1
# またはマージを完了してからreset
$ git add .
$ git commit
$ git reset HEAD~1
トラブル予防のチェックリスト
- [ ] resetする前に必ず
git status
で現在の状態を確認 - [ ]
git log --oneline -5
で履歴を確認 - [ ] 重要な変更がある場合は事前に
git stash
でバックアップ - [ ]
--hard
使用前に本当に必要か再検討 - [ ] チーム開発では共有ブランチでのresetを避ける
- [ ] リモートにpush済みのコミットはrevertを検討
💡 ベストプラクティス
1. 段階的なreset戦略
推奨される方法:
# Step1: まず状況を確認
$ git status
$ git log --oneline -5
# Step2: 最も安全な--softから試す
$ git reset --soft HEAD~1
# Step3: 必要に応じて--mixedに変更
$ git reset HEAD~1
# Step4: 最後の手段として--hardを検討
$ git reset --hard HEAD~1
避けるべき方法:
# Bad ❌ いきなり--hardを使用
$ git reset --hard HEAD~3
理由: 段階的にアプローチすることで、予期しないデータ損失を防げます
2. reflogを活用した安全網
推奨設定:
# reflogの保持期間を延長
$ git config gc.reflogExpire 90.days
$ git config gc.reflogExpireUnreachable 30.days
# reflogの確認を習慣化
$ git reflog --oneline -10
復旧テンプレート:
# 1. reflogで目的のコミットを特定
$ git reflog | grep "commit:"
# 2. ハッシュを使って復旧
$ git reset --hard <commit-hash>
# 3. 復旧確認
$ git log --oneline -3
3. チーム開発での安全なreset運用
ルール1: 共有ブランチでのreset禁止
# Good ✅ ローカルブランチでのみreset使用
$ git checkout -b feature-branch
$ git reset --soft HEAD~1
# Bad ❌ mainブランチでreset後にpush
$ git checkout main
$ git reset --hard HEAD~1
$ git push --force # 危険!
ルール2: push前のクリーンアップ
# 作業ブランチでの整理
$ git reset --soft HEAD~3
$ git commit -m "Implement user authentication feature"
# mainブランチへの安全なマージ
$ git checkout main
$ git merge feature-branch
ルール3: チーム通知の徹底
# 危険な操作前に必ずチームに連絡
# .gitconfig でエイリアス設定
[alias]
force-push = !echo "Are you sure? This will rewrite history!" && read && git push --force-with-lease
4. 自動化とエイリアス設定
# ~/.gitconfig に追加する便利なエイリアス
[alias]
# 安全なreset系エイリアス
undo-commit = reset --soft HEAD~1
unstage = reset HEAD
discard = reset --hard HEAD
# reflog確認系
history = reflog --oneline -10
rescue = !git reflog | head -10
# 安全確認付きreset
safe-reset = !sh -c 'echo "Current status:" && git status && echo "Continue? (y/N)" && read answer && [ "$answer" = "y" ] && git reset "$@"' -
# 使用例
$ git undo-commit # 直前のコミットを取り消し
$ git unstage file.js # 特定ファイルのステージングを取り消し
$ git safe-reset --hard HEAD~1 # 確認付きhard reset
🔍 高度な活用法
インタラクティブreset(Git 2.25+)
# インタラクティブモードでファイルごとに選択
$ git reset -p HEAD~1
# 表示例
diff --git a/src/auth.js b/src/auth.js
index abc1234..def5678 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,7 +10,12 @@ function authenticate(user) {
return false;
}
- return validateUser(user);
+ if (!validateUser(user)) {
+ logFailedAttempt(user);
+ return false;
+ }
+
+ return true;
}
Reset this hunk [y,n,q,a,d,j,J,g,/,s,e,?]?
選択肢の意味:
y
: このハンクをresetn
: このハンクをそのまま保持s
: ハンクを分割e
: 手動編集モード
パッチモードでの部分reset
# 特定の変更ブロックのみをreset
$ git reset --patch HEAD~1 src/utils.js
# 複数ファイルの異なる部分をリセット
$ git reset -p HEAD~2 -- src/ tests/
条件付きreset(scripting)
#!/bin/bash
# safe-reset.sh - 安全なreset スクリプト
TARGET_COMMIT=${1:-HEAD~1}
BACKUP_BRANCH="backup-$(date +%Y%m%d-%H%M%S)"
# バックアップブランチ作成
git branch $BACKUP_BRANCH
echo "Created backup branch: $BACKUP_BRANCH"
echo "Current HEAD: $(git rev-parse --short HEAD)"
echo "Target commit: $(git rev-parse --short $TARGET_COMMIT)"
echo ""
# 影響するファイルを表示
echo "Files that will be affected:"
git diff --name-only $TARGET_COMMIT HEAD
echo ""
read -p "Continue with reset? (y/N): " confirm
if [ "$confirm" = "y" ]; then
git reset --mixed $TARGET_COMMIT
echo "Reset completed. Use 'git checkout $BACKUP_BRANCH' to restore if needed."
else
git branch -D $BACKUP_BRANCH
echo "Reset cancelled."
fi
使用法:
$ chmod +x safe-reset.sh
$ ./safe-reset.sh HEAD~3
📊 コマンドオプション完全リファレンス
主要オプション詳細
オプション一覧を展開
オプション | 長い形式 | 説明 | Working Directory | Staging Area | Repository |
---|---|---|---|---|---|
--soft | HEADのみ移動 | 変更なし | 変更なし | 変更あり | |
--mixed | デフォルト動作 | 変更なし | リセット | 変更あり | |
--hard | 全てリセット | リセット | リセット | 変更あり | |
--merge | マージ状態をリセット | 部分的 | リセット | 変更あり | |
--keep | ローカル変更を保持 | 保持 | リセット | 変更あり | |
-q, --quiet | --quiet | 出力を抑制 | – | – | – |
-p, --patch | --patch | インタラクティブモード | – | – | – |
モード別の安全性評価
モード | 安全性 | データ損失リスク | 推奨用途 |
---|---|---|---|
--soft | なし | コミットメッセージ修正、コミット統合 | |
--mixed | 低 | ステージング取り消し、一般的な修正 | |
--hard | 高 | 完全なやり直し、緊急時のみ | |
--keep | 低 | ローカル変更を保持したいリセット |
特殊なreset操作
# 特定のパスのみをreset
$ git reset HEAD -- src/ # srcディレクトリのみ
$ git reset HEAD~1 -- "*.js" # JSファイルのみ
# 複数コミットにわたる特定ファイル
$ git reset HEAD~3 -- README.md config.json
# マージコミットのreset
$ git reset --hard ORIG_HEAD # マージ直後の取り消し
$ git reset --merge HEAD~1 # マージ状態をリセット
🎯 実践演習
演習1: 基本操作の練習
課題: 以下の状況を解決してください
- 直前のコミットメッセージに間違いがある
- ファイルの内容は変更したくない
- 正しいメッセージで再コミットしたい
解答を見る
# 解答例
$ git log --oneline -2
a1b2c3d (HEAD -> main) Wrnog speling in commit message
e4f5g6h Previous commit
$ git reset --soft HEAD~1
$ git commit -m "Correct spelling in commit message"
# 確認
$ git log --oneline -2
f7g8h9i (HEAD -> main) Correct spelling in commit message
e4f5g6h Previous commit
解説: –softを使うことでファイル内容とステージング状態を保持しつつ、コミットのみをやり直せます。
演習2: 複雑なリセット操作
課題: 以下の履歴から、最新2つのコミットを1つにまとめてください
c3d4e5f Fix typo in documentation
b2c3d4e Add error handling
a1b2c3d Implement new feature
解答を見る
# 解答例
# 1. 現在の状態確認
$ git log --oneline -3
c3d4e5f (HEAD -> main) Fix typo in documentation
b2c3d4e Add error handling
a1b2c3d Implement new feature
# 2. 2つのコミットを取り消してステージング状態に
$ git reset --soft HEAD~2
# 3. 統合されたメッセージでコミット
$ git commit -m "Implement new feature with error handling and documentation"
# 4. 結果確認
$ git log --oneline -2
x9y8z7w (HEAD -> main) Implement new feature with error handling and documentation
a1b2c3d Implement new feature
解説: –softを使うことで複数のコミットの内容をステージング状態で統合し、論理的にまとまったコミットとして再構成できます。
演習3: 緊急復旧シナリオ
課題: 誤ってgit reset --hard HEAD~5
を実行してしまいました。重要な作業を復旧してください。
解答を見る
# 解答例
# 1. reflogで失われたコミットを探す
$ git reflog --oneline -10
e4f5g6h (HEAD -> main) HEAD@{0}: reset: moving to HEAD~5
a1b2c3d HEAD@{1}: commit: Important work - DO NOT LOSE
b2c3d4e HEAD@{2}: commit: Critical bug fix
...
# 2. 重要な作業があったコミットに復旧
$ git reset --hard HEAD@{1}
# 3. 復旧確認
$ git log --oneline -3
a1b2c3d (HEAD -> main) Important work - DO NOT LOSE
b2c3d4e Critical bug fix
c3d4e5f Previous stable version
解説: reflogはすべてのHEAD移動履歴を記録しているため、–hardでリセットした内容も復旧可能です(通常30日間保存)。
🔗 関連リソース
公式ドキュメント
関連記事
次に学ぶべきコマンド
- git revert: 公開済み履歴の安全な修正方法
- git reflog: 高度な履歴復旧テクニック
- git cherry-pick: 特定コミットの選択的適用
📌 まとめ
この記事で学んだこと
- ✅ git resetの3つのモード(–soft、–mixed、–hard)の完全理解
- ✅ HEADポインタとGitの3領域への影響の把握
- ✅ ファイル単位でのreset操作とステージング管理
- ✅ reflogを使った危険な操作からの復旧方法
- ✅ チーム開発での安全なreset活用法
重要なポイントの再確認
- 基本: –soft → –mixed → –hard の順で影響範囲が拡大
- 応用: reflogを使えばどんなreset操作からも復旧可能
- 注意: 共有ブランチでのresetは避け、revertを使用する
実務での活用チェックリスト
- [ ] 各resetモードの違いを理解し、適切に使い分けられる
- [ ] reset前の状態確認(git status、git log)を習慣化した
- [ ] reflogを使った復旧方法をマスターした
- [ ] チームでのreset使用ルールを確認・設定した
- [ ] 便利なエイリアスを設定した
🚀 次のステップ
git resetをマスターしたら、次はgit revertやgit reflogとの組み合わせも学んでいきましょう。特にチーム開発では、resetとrevertの使い分けが重要になります。
また、git resetは強力なコマンドである反面、誤用すると重要なデータを失うリスクもあります。練習用のリポジトリで十分に操作を確認してから、本番環境で使用することを強く推奨します。
安全で効率的なGit操作をマスターして、より良い開発体験を実現してください!
Happy Git Life! 🎉