「git stash popしたらconflictで混乱してgit stash dropで消してしまった」「git stash listが空っぽ、でもあの変更がない!」——作業途中のstashを失うと血の気が引きますが、焦って追加操作をする前に手を止めれば高確率で救済できます。
stashは内部的に特殊なcommitオブジェクトとして保存されており、dropやpopで「参照から外れた」だけで実体は.git内に残っています。git reflog show stashやgit fsck --lost-foundで辿れば、数週間前のstashでも見つかることが多いのです。
この記事では、stash失い5パターンの救出フローと、reflog・fsckによる段階的探索、そして同じ事故を繰り返さない予防策まで実務で役立つ形でまとめます。
この記事で学べること
- stashの実体(特殊commit+stash reflog)と救済原理
- 5パターンの失い方と復元手順
git reflog show stashによる直近救出git fsck --lost-foundでdanglingから探索stash popconflictで混乱した時の整理手順- 未追跡ファイルを含めてstashする正しい書き方(
-u/-a) - GCと期限:いつまでに救出すべきか
stashの仕組み:失っても救える理由
git stash pushを実行すると、Gitは2〜3個の特殊commitを作り、refs/stashという参照に結びつけます。drop/popではこの参照が外されるだけで、commit本体は一定期間.git/objects/に残存します。この特性を利用して、reflogやfsckから失ったstashを辿ることができます。
# stash は特殊なcommit(親が複数) # parent 1: stashした時点のHEAD # parent 2: indexの状態(ステージされていた変更) # parent 3: (オプション) untrackedファイル群 # stash参照の実体を覗く cat .git/refs/stash # → 40文字のSHA # stash履歴 cat .git/logs/refs/stash # → pushごとに1行記録される
ポイント:stashの「消えた」は正確には「参照だけ消えてオブジェクトは残る」状態です。commit SHAを特定できればgit stash apply <SHA>やgit cherry-pickで復活できます。時間との勝負になるのはGitのgarbage collection(gc)が走ると実体も消えるため。「失ったかも」と気付いたら即時救出を試みるのが鉄則です。
注意:Gitのgcは通常参照されなくなってから30日以上経過し、git gc手動実行またはgc.autoの閾値に達したときに走ります。直近の失いならほぼ100%救えますが、数ヶ月経つと消えている可能性が出てきます。
stash失い5パターンの早見表
ポイント:多くは②drop/pop後の消失。git reflog show stashで直前のSHAを特定→git stash apply <SHA>で復活、というフローが王道です。⑤のような「そもそもstashに入っていなかった」ケースはGit外で復元するしかないので、今回の教訓として次回からは-uを付ける習慣を。
STEP 0:まずはstash状態を正確に確認
# 通常のstash一覧 git stash list # 見つかれば① パターン(pop/applyで戻す) # stash専用のreflog git reflog show stash # または等価の git log -g --pretty=oneline stash # drop/pop後も履歴が残る(数週間単位) # 見つからない場合の最後の手段 git fsck --no-reflogs --lost-found # dangling commit/blob が列挙される
一度止まって手を動かさないこと
- 追加のcommit/checkoutはしない(reflog entriesが押し出される)
git gcは絶対に手動実行しない- まずバックアップ:
cp -r .git .git.backup - 落ち着いてreflog→fsckの順で探索
ケース①:stash listに残っている(見落とし)
「stashが消えた」と思っても、単にgit stash listで確認していなかっただけ、というケースも多いです。まずは通常の一覧を見て、見つかれば普通にpop/applyで復元できます。
# 一覧表示
git stash list
# stash@{0}: On main: wip: fix login
# stash@{1}: WIP on feature: rebase中
# 中身を確認
git stash show -p stash@{0}
# 作業ツリーに適用(stashは残る)
git stash apply stash@{0}
# 適用&stash削除
git stash pop stash@{0}
# ブランチ化して安全に検討
git stash branch feature/recovered stash@{0}
git stash branchのメリット
git stash branch <name> <stash>は、stash適用時点のbaseコミットから新ブランチを作って適用する強力なコマンドです。conflict回避が楽で、成功すればstashは自動削除。「applyでconflictが怖い」ときはbranchに切り替えると安全性が大きく上がります。
ケース②:drop/pop後に消えた(reflogで救出)
git stash dropやgit stash popでstashエントリが削除された場合、refs/stashのreflogに履歴が残っているので、そこからSHAを取り出して救出できます。
# stash専用reflog(pop/drop後も一定期間残る)
git reflog show stash
# 出力例:
# abc1234 stash@{0}: WIP on main: fix login
# def5678 stash@{1}: WIP on feature: authcheck
# 直接apply
git stash apply abc1234
# applyが受け付けない場合、コミットとして扱う
git checkout -b recovered/stash abc1234
# 中身を確認
git log --stat -1
git show -p HEAD
# 必要なら元のブランチにcherry-pick
git switch <original-branch>
git cherry-pick abc1234
reflog entry が出る形式
# 通常の pop だと
# abc1234 stash@{0}: WIP on main: ... ← popで外れた
# messageを付けたstashなら検索しやすい
git reflog show stash | grep "fix login"
# 時刻付きで探す
git reflog --date=iso show stash | head -20
# 履歴が古ければglobal reflogも探す
git reflog --all | grep -i "stash\|wip"
ポイント:reflogはgit gcで消える可能性があるため、「数時間〜数日以内」の直近失いが最も成功しやすい。drop直後ならstash@{0}が空気入れ換わり前の参照として残っているので、git reflog show stashだけでほぼ救えます。
ケース③:stash clearで全消去された(fsckで探索)
git stash clearは全stashを削除し、stash reflogも消します。この場合はgit fsckでぶら下がりcommit(dangling commit)として残っているstash本体を探します。
# 参照されていないcommitを列挙
git fsck --no-reflogs --lost-found
# 出力例:
# dangling commit abc1234...
# dangling blob def5678...
# 各コミットの中身を順に確認
git show --stat abc1234
git show abc1234 | head -30
# stashらしいコミットの見分け方:
# - タイトルが "WIP on branch-name" で始まる
# - parentが2〜3個(通常のcommitは1個)
# 候補が大量な時はgrepで絞る
for sha in $(git fsck --no-reflogs --lost-found | grep "dangling commit" | awk '{print $3}'); do
if git show --no-patch --format="%s" "$sha" | grep -q "^WIP on\|On "; then
echo "Likely stash: $sha -- $(git show --no-patch --format='%s %cr' "$sha")"
fi
done
# 救出用ブランチを作る git branch recovered/stash abc1234 git switch recovered/stash # 中身確認 git log --stat -1 # 元ブランチに必要部分だけ取り込む git switch <original-branch> git cherry-pick abc1234 # または git show abc1234 > /tmp/stash.patch git apply --index /tmp/stash.patch
注意:git fsck --lost-foundは.git内の全ぶら下がりオブジェクトを列挙するので、通常は数百〜数千件見つかります。時刻降順で新しいものから確認するか、「WIP on」で絞り込むのが現実的。メッセージを付けてstashする習慣があれば、検索が格段に楽になります。
dangling commit/blob全般の救出は誤ってmaster/mainを削除したときの復旧方法も類似の手法を使います。
ケース④:pop中のconflictで混乱
git stash popは適用成功時のみstashを削除する仕様です。conflictが出てapply途中で止まっている場合、実はstash entryはまだ残っていることが多い。慌ててdropせず、まず状況確認を。
# 現状確認
git status
# "You have unmerged paths"
# stash entryが残っているか確認
git stash list
# 残っていれば安心して解消に集中
# 方法A: 解消して続行
git add .
git commit # マージコミットに準ずる
# または
git stash drop # stashを消す
# 方法B: やり直す(適用取消)
git checkout . # ファイル修正を捨てる
git reset HEAD . # index戻す
# popをやめてapplyで慎重に
git stash apply stash@{0}
ポイント:pop失敗時もstashは残っているので、「消えた」と誤解してdropしないこと。不安ならgit stash branch recovered stash@{0}でブランチ化して別空間で解決するのが最も安全です。
ケース⑤:そもそもstashに含まれていなかった(untracked)
git stash pushはデフォルトでuntrackedファイルを含めません。「stashしたつもり」が実は未追跡ファイルに対して効いておらず、別ブランチに移動した瞬間にファイルが消えた・見えなくなったケース。Gitの管理外なので復元は難しくなります。
# untrackedも含めてstash git stash push -u -m "wip with untracked" git stash push --include-untracked -m "..." # ignoredまで含める git stash push -a -m "wip with ignored" git stash push --all -m "..." # 既定挙動をuntrackedも含めるように(Git 2.24+) git config --global stash.showIncludeUntracked true # いま作業ツリーにあるuntrackedを把握 git status -u
警告:Gitが一度も把握していないuntrackedファイルをstashに入れず、ブランチ切り替えやcheckoutでローカルから失った場合、Git経由での復元は不可能です。OSのゴミ箱・IDEのLocal History(VS Code Timeline、JetBrains Local History)・Time MachineやWindows File Historyなどのバックアップから探してください。
untracked全般の整理はuntracked filesの解消方法で詳しく解説しています。
実践シナリオ
シナリオ① drop直後に気付いた
git reflog show stash
# abc1234 stash@{0}: WIP on main
git stash apply abc1234
# or
git branch recovered-stash abc1234
シナリオ② stash clearしてしまった
git fsck --no-reflogs --lost-found | grep "dangling commit"
# WIPらしきcommitを絞り込み
for sha in $(git fsck --no-reflogs --lost-found | awk '/dangling commit/ {print $3}'); do
title=$(git show --no-patch --format="%s %cr" "$sha")
echo "$sha: $title"
done | grep -i "WIP on"
# 目的のstashを救出
git branch recovered/stash <SHA>
シナリオ③ pop途中でconflict、パニックで追加drop
# reflogで探す(popのapply + drop 両方記録されている) git reflog show stash # 見つけたら再apply git stash apply <SHA> # conflict解消を今度は慎重に
シナリオ④ untrackedファイルが消えた
# VS CodeのTimeline機能 # JetBrainsのLocal History # Time Machine(Mac) # Windowsファイル履歴 # これ以降はstash時に必ず-uを付ける git stash push -u -m "wip"
再発防止:stashを失わない運用
日常運用のコツ
- 必ず
-m "意味のあるメッセージ"を付ける:後から識別可能 - untrackedファイルも含めたいなら
-uを付ける - 長く持つなら
git stash branchでブランチ化 - 数日以上のstashはscratch branchへ昇格
popよりapply+確認後にdropが安全- 不要stashは定期整理、ただし
clearは慎重に - 長期作業は一時コミットで保全(一時コミット&巻き戻し参照)
# 必ずメッセージ付きでstash
git config --global alias.ss 'stash push -u -m'
# 使用例
git ss "fixing login flow"
# stash listの整形
git config --global alias.sl 'stash list --date=iso'
# 確認してから適用
git config --global alias.sa '!f() { git stash show -p "$@" | less; }; f'
git sa stash@{0}
やってはいけない落とし穴
「消えた」と誤解してdropを追加実行
pop途中でconflictが出て焦ると「消えた」と勘違いしてgit stash dropで実体も消してしまうパターン。popは成功時しか削除しないので、まずはgit stash listで残存を確認してから次の操作を。
gcを手動実行してオブジェクトを削除
stashを失った直後にgit gcを打つと、dangling commitが削除されて復元不可能になります。救出中は絶対にgcを手動実行せず、自動gcもgc.auto=0で一時的に停止しておくと安全です。
reflogを押し出してしまう大量commit
救出前に追加commit/checkout/resetを大量に実行すると、古いreflog entryが押し出されてstashエントリが消えることがあります。「消えたかも」と気付いたらまず手を止めるのが最重要。
untrackedを-u なしstashして消失
デフォルトのgit stashはuntrackedファイルを対象外。「stashしたはず」の新規ファイルが作業ツリーにない状態でcheckoutすると失われます。新規ファイルがある時は必ず-uを付ける習慣を。
stashを長期保管して忘れる
数ヶ月放置したstashは、内容を覚えていないことが多く結局捨てる羽目に。数日以上保管するならブランチ化(git stash branch)して、commitにしてしまう方が管理しやすく、失いにくくなります。
よくある質問
git reflog show stashを実行してください。dropやpopで消したstashの履歴が数週間残っているはずです。全滅ならfsckでdangling commitから探します。git fsckでdanglingが残っていれば救出可能ですが、gcが走ると消えているので成功率は下がります。古いstashは運試しです。git branch recovered/stash <SHA>でブランチ化→中身確認→cherry-pickやgit show <SHA> > patch.diff→git applyでパッチ化も有効。git stash listでまだ残っているか確認。残っていればgit checkout .→git reset HEAD .で適用取消し、今度はgit stash applyかgit stash branchで慎重に適用。.gitに情報が残らないため不可。OSのゴミ箱・VS CodeのTimeline・JetBrainsのLocal History・Time Machine等のバックアップが唯一の頼り。今後は必ず-uオプションを付けましょう。WIP onで始まる、parentが2〜3個)で絞り込むのが有効です。メッセージを付けてstashする習慣があれば検索が楽に。記事内のbashスクリプト例を活用してください。gc.auto=6700(ゆるい自動gc)で、追加commit/pullを繰り返すと条件を満たして実行されます。救出中は一時的にgit -c gc.auto=0 <cmd>で無効化できます。関連記事
- 【Git】ローカル動作確認の一時コミット&巻き戻し方法 — stash以外の検証作業手法
- 【Git】消したファイルを戻す方法 — ファイル復元の全体像
- 【Git】コミットの取り消し方 — reset/revert/amendの使い分け
- 【Git】特定のコミットまで戻す方法 — reflog救済との関連
- 【Git】誤ってmaster/mainを削除したときの復旧方法 — 類似のdangling救出
- 【Git】untracked filesの解消方法 — 未追跡ファイル管理
- 【Git】pushを取り消す方法 — push済み変更の対処
- 【Git】よく使うgitコマンドまとめ — 日常コマンドの早見表
まとめ
- stashは特殊なcommitオブジェクトとして
.gitに残り、失っても救えることが多い - まず
git stash list→git reflog show stash→git fsckの順で探索 - popのconflictは適用成功時のみ削除——慌てずlist確認
- untrackedファイルは
-uなしだとそもそもstashに入らない - 失った直後が最も成功率高、gc実行前に救出
- 予防:メッセージ付きstash(
-m)+-u+長期はbranch化 - 長期作業はstashより一時コミット+scratch branch運用が安全
stash失いは焦りがちですが、Gitの内部構造を知れば復元確率は高い問題です。「消えた」と感じたらgit reflog show stashから始め、段階的にfsckまで進めば直近の失いはほぼ確実に救えます。日頃からメッセージ付きstash・-u利用・長期はブランチ化の運用を徹底し、「消えたかも」の事態自体を減らしていきましょう。

