Git Stash 完全ガイド
機能開発の途中、リファクタリングに没頭していると、チームメイトから本番環境の重大なバグについて連絡が来る。作業ディレクトリは中途半端な変更で散らかっている。コミットする準備はできていないが、未コミットの作業を残したままブランチを切り替えることもできない。
こうした状況こそ git stash の出番です。本ガイドでは、実際に使う git stash コマンドのすべて、コンフリクトの対処法、そして stash をリスキーではなく自然な作業に感じさせるワークフローのコツを解説します。
重要なポイント
git stashは未コミットの変更をローカルスタックに保存し、作業ディレクトリをHEADの状態に戻します。これにより、中途半端な作業をコミットせずにコンテキストを切り替えられます。- デフォルトでは、stash には追跡対象の変更のみが含まれます。未追跡ファイルを含めるには
-u、無視されているファイルも含めるには-aを使用します。 - 変更を戻して stash を削除したい場合は
git stash popを、再利用のために stash を残しておきたい場合はgit stash applyを使用します。 - stash の適用中にコンフリクトの解決が手に負えなくなった場合、
git stash branchを使えば元のコミットから新しいブランチを作成し、stash をクリーンに適用できます。 - 必ず
-mで説明的なメッセージを付け、stash リストを短く保ち、stash はバックアップではなく一時保管場所として扱いましょう。
Git Stash とは?
git stash は、ステージング済み・未ステージングを問わず未コミットの変更をローカルスタックに保存し、作業ディレクトリを最後のコミット (HEAD) の状態に戻します。これらの変更は後から、同じブランチでも別のブランチでも復元できます。
stash は ローカル限定 です。git push でリモートにプッシュされることはなく、チームメイトと共有されることもありません。
デフォルトで stash されるもの:
- ステージング済みの変更 (index)
- 追跡対象ファイルの未ステージング変更
デフォルトでは stash されないもの:
- 未追跡ファイル (まだ Git に追加されていない新規ファイル)
- 無視されているファイル
Git Stash の基本コマンド
git stash push で変更を保存する
git stash
シンプルな git stash は git stash push の省略形です。作業ディレクトリをクリーンにする最速の方法です。
後から何が入っているか分かるよう、説明的なメッセージを付けましょう:
git stash push -m "WIP: refactor auth middleware"
注意: 古い
git stash save構文は非推奨です。代わりにgit stash pushを使用してください。
未追跡ファイルを stash する
デフォルトでは、まだステージングしていない新規ファイルは残されます。含めるには -u を使用します:
git stash push -u -m "WIP: new login component"
.gitignore でマッチするファイルも stash したい場合は、-a (--all) を使用します。これが必要になることは稀ですが、無視されているファイルがブランチ切り替えやビルドを妨げる場合に役立ちます。
特定のファイルだけ stash する
pathspec を使って特定のファイルだけを stash することもできます:
git stash push -m "WIP: api changes" -- src/api/client.js src/api/utils.js
これにより、他のファイルは作業ディレクトリ内で手つかずのまま残ります。
Stash 一覧を表示する
git stash list
出力例:
stash@{0}: On main: WIP: refactor auth middleware
stash@{1}: WIP on feature/login: 9ab3c12 Add login form
stash は後入れ先出し (LIFO) のスタックで管理されます。stash@{0} は常に最新のものです。
適用前に stash の中身を確認する
git stash show stash@{0} # サマリー表示
git stash show -p stash@{0} # 完全な diff
Git Stash Pop vs Apply: どちらを使うべきか?
これは git stash コマンドで最も混乱しやすいポイントの一つです。
git stash pop | git stash apply | |
|---|---|---|
| 変更を復元する | ✅ はい | ✅ はい |
| スタックから削除する | ✅ はい (コンフリクトなく適用に成功した場合のみ) | ❌ いいえ |
| コンフリクト時 | stash をスタックに残す | stash をスタックに残す |
| 適した用途 | 一度きりの復元 | 複数のブランチに適用する場合 |
pop を使う のは、stash の用が済んで単に変更を戻したい場合。
apply を使う のは、同じ stash を複数のブランチに適用したい場合、または削除を確定する前に結果をテストしたい場合です。
特定の stash を適用するには:
git stash pop stash@{1}
git stash apply stash@{1}
--index でステージング状態を復元する
デフォルトでは、stash を適用するとすべての変更が未ステージング状態として復元されます。stash した時点のステージング済み/未ステージングの区別を復元したい場合は --index を使用します:
git stash pop --index
Discover how at OpenReplay.com.
Stash 適用時のコンフリクトを解決する
stash 後にブランチが変更されている場合、コンフリクトが発生することがあります:
CONFLICT (content): Merge conflict in src/api/client.js
The stash entry is kept in case you need it again.
コンフリクトが発生した場合、pop と apply のどちらも stash をスタックに残します。解決手順:
git statusを実行してコンフリクトしているファイルを特定します。- 各ファイルを開き、コンフリクトマーカーを解決します。
- 解決したファイルをステージング:
git add src/api/client.js。 - 問題がなければ手動で stash を削除します:
git stash drop stash@{0}。
コンフリクトが厄介になった場合: git stash branch を使う
コンフリクトが複雑に絡み合っている場合、git stash branch が最もクリーンな脱出口です。これは stash した時点の正確なコミットから新しいブランチを作成し、そこに stash を適用します。stash が作成されたコミットからブランチが始まるため、多くの場合コンフリクトを回避できます:
git stash branch fix/auth-refactor stash@{0}
適用が成功すると、stash は自動的に削除されます。
Stash の管理とクリーンアップ
git stash drop stash@{0} # 特定の stash を削除
git stash clear # すべての stash を削除 (取り消し不可)
⚠️
git stash clearは永続的です。元に戻す方法はありません。実行前に必ず確認してください。
応用: 部分的な stash と staged のみの stash
対話型パッチモード では、stash する hunk を選択できます:
git stash push -p
Git が各変更を一つずつ確認し、stash するかどうかを尋ねてくれます。ファイル内に関連する変更と無関係な変更が混在している場合に便利です。
ステージング済みの変更だけを stash する (Git 2.35 以降):
git stash push --staged
これは index にある変更だけを stash し、未ステージングの作業はそのまま残します。現在の進捗を失わずに、脇に置いておきたい変更だけを分離するのに便利です。
ステージング済みの変更を index に残したまま、それ以外をすべて stash する:
git stash push --keep-index
--keep-index はステージング済みと未ステージング両方の変更を stash する点に注意してください。単に stash 後もステージング済みのものを作業ディレクトリ内に残すだけです。
削除した Git Stash を復元する
誤って stash をドロップしたりクリアしたりした場合でも、リポジトリ内の dangling commit として復元できる可能性があります。stash エントリはマージコミットとして保存されているため、以下のコマンドで検索できます:
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --grep=WIP
これは stash エントリのように見える到達不能なマージコミットを一覧表示します。目的のコミットハッシュが見つかったら、以下で復元します:
git stash apply <commit-hash>
復元は保証されません。Git のガベージコレクタが最終的に到達不能なオブジェクトを削除するため、迅速に行動しましょう。
マシン間で Stash を転送する
最近の Git バージョンでは git stash export および git stash import が追加され、通常の fetch および push ワークフローを使って stash を転送できるようになりました。
古いバージョンの Git、または単発の簡単な転送には、以下の現実的な代替手段があります:
- パッチとしてエクスポート:
git stash show -p stash@{0} > my-stash.patchし、別の場所でgit apply my-stash.patchを適用します。 - 使い捨てブランチにコミット: ブランチを作成して作業をコミットし、リモートにプッシュ。別のマシンで pull して reset します。
単発の転送には、通常パッチが最もシンプルな方法です。
Git Stash のベストプラクティス
- 必ずメッセージを付ける。
git stash push -m "WIP: 何を、なぜ"とすることで、匿名のWIP on mainエントリの羅列を眺めて途方に暮れることがなくなります。 - stash はバックアップではなく一時保管場所。 論理的な区切りに達したら作業をコミットしましょう。
- stash リストを短く保つ。 定期的に見直し、不要な stash は削除しましょう。
- pop する前に pull する。 stash を復元する前にブランチを更新しておくと、コンフリクトの可能性を減らせます。
- 迷ったら
popよりapplyを選ぶ。 問題がないことを確認してから、手動で stash を削除すれば良いのです。
クイックリファレンス: Git Stash コマンド
| コマンド | 動作 |
|---|---|
git stash | 追跡対象の変更を stash (push の省略形) |
git stash push -m "msg" | 説明的なメッセージ付きで stash |
git stash push -u | 未追跡ファイルも含める |
git stash push -a | 未追跡および無視されているファイルも含める |
git stash push -- <path> | 特定のファイルだけを stash |
git stash push -p | 対話的に stash する hunk を選択 |
git stash push --staged | ステージング済みの変更だけを stash |
git stash push --keep-index | すべてを stash するが、ステージング済みは index に残す |
git stash list | すべての stash を一覧表示 |
git stash show -p stash@{n} | stash の完全な diff を表示 |
git stash pop | 最新の stash を適用して削除 |
git stash apply stash@{n} | stash を削除せずに適用 |
git stash pop --index | ステージング済み/未ステージング状態を復元 |
git stash drop stash@{n} | 特定の stash を削除 |
git stash clear | すべての stash を削除 |
git stash branch <name> [stash] | stash からブランチを作成 |
まとめ
git stash は、本当に必要になるまでは任意のコマンドに感じられるかもしれません。しかし一度必要に迫られると、日々のワークフローの一部になります。重要な習慣は、stash には必ず名前を付けること、リストを清潔に保つこと、そして単純な pop が難しくなるようなコンフリクトに直面したら git stash branch に頼ることです。うまく使えば、stash は Git の履歴をクリーンに保ち、コンテキストの切り替えを苦痛のないものにしてくれます。
FAQ
いいえ。デフォルトでは git stash は追跡対象の変更のみを保存します。つまり、Git がすでに認識しているファイルへのステージング済みおよび未ステージングの変更です。git add で追加されていない新規ファイルは作業ディレクトリに残されます。これらを含めるには git stash push -u または --include-untracked を使用します。.gitignore でマッチするファイルも stash したい場合は、-a または --all を使用します。
どちらも stash した変更を作業ディレクトリに復元します。違いはその後の挙動です。git stash pop はクリーンに適用された場合、stash をスタックから削除します。一方、git stash apply は stash を残しておくため、後で再適用したり、別のブランチで適用したりできます。pop 中にコンフリクトが発生した場合、作業を失わないように stash は保持されます。
迅速に行動すれば、多くの場合は可能です。stash エントリはマージコミットとして保存されており、削除された stash は到達不能なオブジェクトとなって、最終的に Git のガベージコレクタによって削除されます。git fsck --unreachable を実行して dangling commit を見つけ、WIP メッセージから stash を特定し、git stash apply とコミットハッシュで復元できます。
いいえ。stash は厳密にローカルのものであり、リポジトリの refs/stash 参照に存在します。git push、git fetch、git pull には含まれません。進行中の作業をチームメイトと共有する必要がある場合は、一時ブランチにコミットしてプッシュするか、git stash show -p で変更をパッチとしてエクスポートしてください。
Gain control over your UX
See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.