はじめに#
「git resetで消したコミットを復元したい」「作業中にブランチを切り替える必要があるけど、変更をコミットしたくない」「どこかで失くしたはずのコミットを取り戻したい」。Gitを使った開発では、こうした場面に遭遇することがあります。
git reflogとgit stashは、このような状況で変更を失わないための強力な安全網です。reflogはGitのすべての操作履歴を記録し、stashは作業中の変更を一時的に退避させます。これらを使いこなすことで、安心してGit操作を行えるようになります。
本記事では、git reflogによる履歴復元とgit stashによる一時退避の技術を体系的に解説します。この記事を読み終えると、以下のことができるようになります。
- git reflogで操作履歴を確認し、失われたコミットを復元できる
- git stashで作業中の変更を一時退避し、必要なときに復元できる
- stash管理コマンド(list、pop、drop、apply)を使いこなせる
- resetやrebaseで消えたコミットを安全に取り戻せる
実行環境と前提条件#
本記事の内容は、以下の環境で動作確認を行っています。
| 項目 |
要件 |
| Git |
2.40以上 |
| OS |
Windows 10/11、macOS 12以上、Ubuntu 22.04以上 |
| ターミナル |
コマンドプロンプト、PowerShell、Terminal.app、bash等 |
| エディタ |
VS Code推奨 |
前提条件として、以下の知識があることを想定しています。
- コマンドライン操作の基礎知識(
cd、ls/dir、mkdir等)
- テキストエディタの基本操作
- Gitの基本コマンド(
git add、git commit、git reset、git branch)の理解
git reflog - すべての操作履歴を記録する安全網#
git reflogは、リポジトリ内でHEADが指した履歴をすべて記録するコマンドです。git logがコミットの親子関係に基づく履歴を表示するのに対し、reflogはブランチの切り替え、reset、rebase、amendなど、あらゆるHEADの移動を記録します。
reflogの基本概念#
reflog(Reference Log)は、参照の変更履歴を保持する仕組みです。以下の操作がすべてreflogに記録されます。
| 操作 |
reflogへの記録 |
git commit |
新しいコミットへのHEAD移動 |
git reset |
指定コミットへのHEAD移動 |
git checkout / git switch |
ブランチ切り替え |
git rebase |
リベース中のHEAD移動 |
git merge |
マージコミットへのHEAD移動 |
git pull |
fetch後のHEAD移動 |
git commit --amend |
新しいコミットへの置き換え |
reflogの確認方法#
reflogを確認する基本コマンドは以下のとおりです。
1
2
3
4
5
6
7
8
9
10
11
|
# reflogを表示(デフォルトはHEADのreflog)
git reflog
# より詳細な情報を表示
git reflog show --all
# 特定のブランチのreflogを表示
git reflog show main
# 日時付きで表示
git reflog --date=iso
|
実行例を見てみましょう。
1
2
3
4
5
6
7
|
$ git reflog
a1b2c3d (HEAD -> main) HEAD@{0}: commit: 新機能を追加
e4f5g6h HEAD@{1}: commit: バグを修正
i7j8k9l HEAD@{2}: reset: moving to HEAD~2
m1n2o3p HEAD@{3}: commit: 削除されたコミット
q4r5s6t HEAD@{4}: commit: もう一つの削除されたコミット
u7v8w9x HEAD@{5}: checkout: moving from feature to main
|
各行の意味は以下のとおりです。
| 要素 |
説明 |
a1b2c3d |
コミットハッシュ(短縮形) |
(HEAD -> main) |
現在のHEADの位置 |
HEAD@{0} |
reflogのインデックス(0が最新) |
commit: / reset: |
実行された操作の種類 |
| メッセージ |
操作の詳細説明 |
reflogの保持期間#
reflogのエントリはデフォルトで以下の期間保持されます。
| 条件 |
保持期間 |
| 到達可能なコミット |
90日(gc.reflogExpire) |
| 到達不可能なコミット |
30日(gc.reflogExpireUnreachable) |
保持期間を確認・変更するには以下のコマンドを使用します。
1
2
3
4
5
6
|
# 現在の設定を確認
git config --get gc.reflogExpire
git config --get gc.reflogExpireUnreachable
# 保持期間を変更(例:到達不可能なコミットを60日保持)
git config gc.reflogExpireUnreachable 60.days
|
git reflogを使った履歴復元の実践#
reflogの真価は、通常の方法では復元できない変更を取り戻せる点にあります。
git resetで消したコミットを復元する#
git reset --hardでコミットを消してしまった場合の復元手順です。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 現在の状態:3つのコミットがある
$ git log --oneline
a1b2c3d (HEAD -> main) 3番目のコミット
e4f5g6h 2番目のコミット
i7j8k9l 1番目のコミット
# 誤って2つ前のコミットまでリセット
$ git reset --hard HEAD~2
HEAD is now at i7j8k9l 1番目のコミット
# コミットが消えた
$ git log --oneline
i7j8k9l (HEAD -> main) 1番目のコミット
|
この状態から復元するには、reflogを確認して元のコミットを特定します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# reflogで履歴を確認
$ git reflog
i7j8k9l (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
a1b2c3d HEAD@{1}: commit: 3番目のコミット
e4f5g6h HEAD@{2}: commit: 2番目のコミット
i7j8k9l HEAD@{3}: commit: 1番目のコミット
# リセット前の状態(HEAD@{1})に戻す
$ git reset --hard HEAD@{1}
HEAD is now at a1b2c3d 3番目のコミット
# コミットが復元された
$ git log --oneline
a1b2c3d (HEAD -> main) 3番目のコミット
e4f5g6h 2番目のコミット
i7j8k9l 1番目のコミット
|
rebaseで失われたコミットを復元する#
インタラクティブリベースやコンフリクト解消中に失敗した場合も、reflogから復元できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# リベース前のコミット履歴
$ git log --oneline
c1d2e3f (HEAD -> feature) 機能C
b4a5b6c 機能B
a7b8c9d 機能A
# リベースを実行(途中で失敗したと仮定)
$ git rebase -i main
# ... 何らかの問題が発生 ...
# リベースを中断
$ git rebase --abort
# もしabortできなかった場合、reflogから復元
$ git reflog
x1y2z3a (HEAD -> feature) HEAD@{0}: rebase (abort): ...
c1d2e3f HEAD@{1}: rebase (start): checkout main
c1d2e3f HEAD@{2}: commit: 機能C
# リベース前の状態に戻す
$ git reset --hard HEAD@{2}
|
amendで上書きされたコミットを復元する#
git commit --amendで元のコミットを取り戻したい場合もreflogが有効です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# 元のコミット
$ git log --oneline -1
a1b2c3d (HEAD -> main) 元のコミットメッセージ
# amendでコミットを修正
$ git commit --amend -m "修正後のコミットメッセージ"
[main x9y8z7w] 修正後のコミットメッセージ
# reflogで元のコミットを確認
$ git reflog
x9y8z7w (HEAD -> main) HEAD@{0}: commit (amend): 修正後のコミットメッセージ
a1b2c3d HEAD@{1}: commit: 元のコミットメッセージ
# 元のコミットを新しいブランチとして復元(内容を確認したい場合)
$ git branch recover-original HEAD@{1}
# または元の状態に完全に戻す
$ git reset --hard HEAD@{1}
|
reflogから特定のファイルを復元する#
特定のファイルだけを過去の状態から復元することも可能です。
1
2
3
4
5
6
7
8
9
10
11
|
# reflogでコミットを特定
$ git reflog
a1b2c3d HEAD@{0}: commit: ファイルを削除
e4f5g6h HEAD@{1}: commit: ファイルを編集
i7j8k9l HEAD@{2}: commit: ファイルを追加
# 特定のコミット時点のファイルを復元
$ git restore --source=HEAD@{2} -- path/to/file.txt
# または checkout を使用(古い書式)
$ git checkout HEAD@{2} -- path/to/file.txt
|
git stash - 作業中の変更を一時退避する#
git stashは、コミットしていない変更を一時的に保存し、作業ツリーをクリーンな状態にするコマンドです。ブランチ切り替え時や緊急対応時に、現在の作業を失わずに別の作業を行いたい場合に使用します。
stashの基本概念#
stashは、変更を一時的なスタック構造で保存します。
| 対象 |
stashへの保存 |
| 作業ツリーの変更 |
デフォルトで保存 |
| ステージングエリアの変更 |
デフォルトで保存 |
| 未追跡ファイル |
--include-untrackedオプションで保存 |
| 無視されたファイル |
--allオプションで保存 |
基本的なstash操作#
変更をstashに保存する基本コマンドです。
1
2
3
4
5
6
7
8
9
10
11
|
# 変更をstashに保存
git stash
# メッセージ付きで保存(識別しやすくなる)
git stash push -m "作業中の機能A"
# 未追跡ファイルも含めて保存
git stash push --include-untracked -m "未追跡ファイル含む"
# 特定のファイルのみstash
git stash push -m "特定ファイル" -- path/to/file.txt
|
実行例を見てみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
# 現在の状態
$ git status
On branch feature
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.js
modified: index.html
Untracked files:
(use "git add <file>..." to include in what will be committed)
new-feature.js
# 未追跡ファイルを含めてstash
$ git stash push --include-untracked -m "新機能開発中"
Saved working directory and index state On feature: 新機能開発中
# 作業ツリーがクリーンになった
$ git status
On branch feature
nothing to commit, working tree clean
|
stash管理コマンドの使い方#
stashには複数の変更を保存でき、それぞれを管理するためのコマンドが用意されています。
git stash list - stash一覧の確認#
保存されたstashの一覧を確認します。
1
2
3
4
5
6
7
8
9
10
11
12
|
$ git stash list
stash@{0}: On feature: 新機能開発中
stash@{1}: On main: バグ修正の途中
stash@{2}: On feature: 実験的な変更
# 詳細情報を表示
$ git stash list --stat
stash@{0}: On feature: 新機能開発中
app.js | 15 +++++++++++++++
index.html | 5 +++++
new-feature.js | 30 ++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+)
|
stashはスタック構造のため、stash@{0}が最新です。
git stash show - stash内容の確認#
stashに保存された変更内容を確認します。
1
2
3
4
5
6
7
8
9
10
11
|
# 最新のstashの概要を表示
$ git stash show
app.js | 15 +++++++++++++++
index.html | 5 +++++
2 files changed, 20 insertions(+)
# 詳細な差分を表示
$ git stash show -p
# 特定のstashを指定
$ git stash show stash@{1} -p
|
git stash pop - stashを復元して削除#
stashから変更を復元し、同時にstashリストから削除します。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 最新のstashを復元
$ git stash pop
On branch feature
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: app.js
modified: index.html
Dropped refs/stash@{0} (a1b2c3d4e5f6...)
# 特定のstashを復元
$ git stash pop stash@{1}
|
popはstashを削除するため、復元に成功した場合のみ使用することをおすすめします。
git stash apply - stashを復元(削除しない)#
stashから変更を復元しますが、stashリストには残ります。
1
2
3
4
5
6
7
8
9
10
11
12
|
# 最新のstashを復元(削除しない)
$ git stash apply
On branch feature
Changes not staged for commit:
modified: app.js
modified: index.html
# 特定のstashを復元
$ git stash apply stash@{2}
# ステージング状態も復元
$ git stash apply --index
|
applyはstashを残すため、複数のブランチに同じ変更を適用したい場合に便利です。
git stash drop - stashを削除#
不要になったstashを削除します。
1
2
3
4
5
6
7
8
9
|
# 最新のstashを削除
$ git stash drop
Dropped refs/stash@{0} (a1b2c3d4e5f6...)
# 特定のstashを削除
$ git stash drop stash@{1}
# すべてのstashを削除
$ git stash clear
|
git stash branch - stashから新しいブランチを作成#
stashした変更を新しいブランチで作業したい場合に使用します。
1
2
3
4
5
6
7
8
|
# stashから新しいブランチを作成
$ git stash branch new-feature-branch
Switched to a new branch 'new-feature-branch'
On branch new-feature-branch
Changes not staged for commit:
modified: app.js
Dropped refs/stash@{0} (a1b2c3d4e5f6...)
|
コンフリクトが発生しそうな場合に、安全にstashを復元する方法として有効です。
stash活用の実践シナリオ#
実際の開発でstashを活用するシナリオを紹介します。
シナリオ1:緊急のバグ修正対応#
新機能の開発中に、本番環境で緊急のバグが発見された場合の対応です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
# 現在の状況:featureブランチで開発中
$ git branch
* feature/new-dashboard
main
$ git status
Changes not staged for commit:
modified: src/dashboard.js
modified: src/components/Chart.js
# 1. 現在の作業をstash
$ git stash push -m "ダッシュボード開発中"
Saved working directory and index state On feature/new-dashboard: ダッシュボード開発中
# 2. mainブランチに切り替え
$ git switch main
# 3. 緊急修正用ブランチを作成
$ git switch -c hotfix/critical-bug
# 4. バグを修正してコミット
$ vim src/auth.js
$ git add src/auth.js
$ git commit -m "fix: 認証エラーを修正"
# 5. mainにマージしてpush
$ git switch main
$ git merge hotfix/critical-bug
$ git push origin main
# 6. 元のブランチに戻り、stashを復元
$ git switch feature/new-dashboard
$ git stash pop
|
シナリオ2:複数の作業を切り替える#
複数の機能を並行して開発する場合のstash活用です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 機能Aの作業中
$ git stash push -m "機能A: ユーザー認証"
# 機能Bの作業を開始
$ git switch feature/feature-b
# ... 作業 ...
$ git stash push -m "機能B: 通知システム"
# 機能Aに戻る
$ git switch feature/feature-a
$ git stash list
stash@{0}: On feature/feature-b: 機能B: 通知システム
stash@{1}: On feature/feature-a: 機能A: ユーザー認証
# 機能Aのstashを復元
$ git stash apply stash@{1}
|
シナリオ3:実験的な変更を一時保存#
試行錯誤中の変更を一時的に保存し、別のアプローチを試す場合です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 実験的な変更を保存
$ git stash push -m "実験: アプローチA"
# 別のアプローチを試す
# ... 作業 ...
$ git stash push -m "実験: アプローチB"
# stash一覧を確認
$ git stash list
stash@{0}: On feature: 実験: アプローチB
stash@{1}: On feature: 実験: アプローチA
# 各アプローチの差分を比較
$ git stash show stash@{0} -p > approach-b.diff
$ git stash show stash@{1} -p > approach-a.diff
# 良かった方を採用
$ git stash apply stash@{1}
$ git stash drop stash@{0}
$ git stash drop stash@{0} # インデックスがずれる点に注意
|
stashのコンフリクト解消#
stashを復元する際にコンフリクトが発生することがあります。
コンフリクトが発生した場合#
1
2
3
4
|
$ git stash pop
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
The stash entry is kept in case you need it again.
|
popでコンフリクトが発生した場合、stashは削除されません。
コンフリクトの解消手順#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
# 1. コンフリクトしているファイルを確認
$ git status
Unmerged paths:
(use "git restore --staged <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: app.js
# 2. ファイルを編集してコンフリクトを解消
$ vim app.js
# <<<<<<< と >>>>>>> の間を適切に編集
# 3. 解消したファイルをステージング
$ git add app.js
# 4. stashを手動で削除(popの場合)
$ git stash drop
|
reflogとstashを組み合わせた高度な復元#
reflogとstashを組み合わせることで、より高度な復元が可能になります。
誤ってstashをdropした場合の復元#
git stash dropで削除したstashを復元する方法です。
1
2
3
4
5
6
7
8
9
10
|
# stashを削除
$ git stash drop
Dropped refs/stash@{0} (a1b2c3d4e5f6g7h8i9j0...)
# stashのreflogを確認
$ git fsck --no-reflogs | grep commit
dangling commit a1b2c3d4e5f6g7h8i9j0...
# または直接ハッシュがわかっている場合
$ git stash apply a1b2c3d4e5f6g7h8i9j0
|
より確実な方法として、stashのreflogを確認します。
1
2
3
4
5
6
7
|
# stashのreflogを確認(git reflog show stash)
$ git reflog show stash
a1b2c3d stash@{0}: drop: ...
e4f5g6h stash@{1}: WIP on feature: ...
# 削除されたstashを復元
$ git stash apply a1b2c3d
|
複雑な復元シナリオ#
リベース中にstashを使い、問題が発生した場合の復元です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 1. 変更をstash
$ git stash push -m "作業中"
# 2. リベースを開始したが失敗
$ git rebase main
# ... コンフリクト発生 ...
# 3. 元の状態に戻りたい場合
$ git rebase --abort
# 4. reflogでリベース前の状態を確認
$ git reflog
x1y2z3a HEAD@{0}: rebase (abort): ...
a1b2c3d HEAD@{1}: rebase (start): checkout main
e4f5g6h HEAD@{2}: stash: ...
i7j8k9l HEAD@{3}: commit: 元のコミット
# 5. stashを復元
$ git stash pop
|
reflogとstashの注意点#
これらのコマンドを使用する際の注意点をまとめます。
reflogの注意点#
| 注意点 |
説明 |
| ローカル専用 |
reflogはローカルリポジトリにのみ存在し、pushされない |
| 有効期限あり |
デフォルトで30〜90日後に古いエントリは削除される |
| gc実行で削除 |
git gc実行時に古いエントリが削除される可能性がある |
| clone時には存在しない |
リポジトリをcloneした直後はreflogが空 |
stashの注意点#
| 注意点 |
説明 |
| ローカル専用 |
stashはローカルリポジトリにのみ存在し、pushされない |
| 長期保存向きではない |
一時的な退避用であり、長期保存にはブランチを使用すべき |
| コンフリクトの可能性 |
時間が経つとpop時にコンフリクトが発生しやすくなる |
| 未追跡ファイル |
デフォルトでは未追跡ファイルはstashされない |
ベストプラクティス#
| 推奨事項 |
説明 |
| stashにはメッセージを付ける |
git stash push -m "説明"で識別しやすくする |
| stashは早めに処理する |
長期間放置せず、こまめにpopまたはdropする |
| 重要な変更はブランチで管理 |
stashよりもブランチのほうが安全に管理できる |
| reflogを定期的に確認 |
問題発生時の復元ポイントを把握しておく |
まとめ#
本記事では、git reflogによる履歴復元とgit stashによる一時退避の技術を解説しました。
git reflogは、Gitのすべての操作履歴を記録する安全網です。
git reflogでHEADの移動履歴をすべて確認できる
git reset --hard HEAD@{n}で過去の状態に復元できる
- resetやrebase、amendで失われたコミットを取り戻せる
git stashは、コミットしていない変更を一時的に退避する機能です。
git stash push -m "メッセージ"で変更を保存
git stash listでstash一覧を確認
git stash popで復元して削除、git stash applyで復元のみ
git stash dropで不要なstashを削除
これらのコマンドを使いこなすことで、変更を失うリスクを大幅に減らし、安心してGit操作を行えるようになります。日常的な開発フローに組み込んで、効率的なバージョン管理を実現しましょう。
参考リンク#