git stashとは何か・コミットするほどでもない作業を安全に脇へどける

前記事では、コミット済みの履歴を整理するrebaseを扱いました。今回はその手前の話、つまり「まだコミットするには早い、けれど今すぐブランチを切り替えたい」という場面で使うgit stashです。急ぎの修正依頼が飛んできたとき、書きかけの変更をどう扱うかで対応の速さが変わってきます。

コミットせずに、作業ツリーの変更をまるごと退避する

ブランチを切り替えるには、今の変更を一度コミットするしかないと思うかもしれません。ですが未完成のコードを「とりあえずコミット」してしまうと、後で見返したときに意味の分からないコミットが履歴に残ってしまいます。git stashを使えば、コミットせずに作業ツリーとインデックスの変更をまるごと一時的に退避し、作業ツリーをHEADの状態(直前にコミットした状態)に戻すことができます。退避した内容はstashという専用の置き場に積まれ、必要なときに呼び戻せます。

# 今の変更(コミットしていないもの)を退避する
git stash

# 別のブランチで緊急対応...
git switch main
git commit -am "hotfix: 緊急修正"
git switch feature

# 退避しておいた変更を呼び戻す
git stash pop
🎯 読み解きポイント:作業台の途中の道具を、いったん脇の棚にまとめて移す

大工仕事に例えると、stashは「作業台の上に散らばった途中の道具と材料を、まとめて脇の棚に移す」動作です。作業台(作業ツリー)は綺麗な状態に戻るので、別の作業(ブランチの緊急対応)にすぐ取りかかれます。棚に置いた道具は、後で作業台に戻せば続きから再開できます。ただし、床に転がっていた新品の材料(untrackedファイル)は、デフォルトでは棚に移されず床に置きっぱなしになる点には注意が必要です。含めたい場合は-uオプションを付けます。

pop・apply・dropは「リストから消えるかどうか」が違う

stashは1回使ったら役目を終える機能だと思うかもしれませんが、実際には複数の退避を同時に積み重ねて保持でき、それぞれをstash@{0}、stash@{1}...という番号で管理します。扱う操作にも3種類あり、違いは「適用するかどうか」と「適用後にリストから消えるかどうか」です。

# 積み上がったstashの一覧を確認する
git stash list
# stash@{0}: WIP on feature: 6ebd0e2 途中のUI修正
# stash@{1}: WIP on feature: 9cc0589 途中のAPI連携

git stash pop            # 最新(stash@{0})を適用し、リストから削除する
git stash apply stash@{1} # 指定したstashを適用するが、リストには残す
git stash drop stash@{1}  # 適用せず、リストから削除するだけ

applyは「試しに何度でも同じ内容を当ててみたい」場合に向いており、popは「もう戻す必要がない」ことが分かっている場合に向いています。ブランチが大きく動いていてapplyがコンフリクトを起こしそうなときは、git stash branch 新ブランチ名を使うと、stash作成時点のコミットから新しいブランチを切ってからstashを適用してくれるため、コンフリクトを避けやすくなります。

🎯 読み解きポイント:複数の一時預かり品を、全部「さっき預けたもの」だと思い込む

コインロッカーに例えると、stash listは「今預けている荷物の一覧」です。stash@{0}が一番新しく預けたもの、番号が大きくなるほど古いものになります。ここで焦って番号を確認せずgit stash popだけを連打すると、本来まだ取り出したくなかった古い荷物まで次々と取り出してしまいます。必ずgit stash listで中身を確認してから、どの番号を取り出すか指定するのが安全です。

よくあるはまりポイント:popしたらコンフリクトが起きて、stashが消えたか分からなくなる

stash popは「適用してから削除する」動作だと説明しましたが、これは常にセットで起きるとは限りません。適用しようとした内容が現在の作業ツリーとコンフリクトを起こした場合、gitはリストからの削除を行わず、stashをそのまま残してくれます。ここを知らずに「popしたのにエラーが出た、stashは消えてしまったのだろうか」と慌てて、コンフリクトを解消しないまま何度もpopを実行してしまうと、意図せず同じ変更を重ねて適用してしまう事故につながります。popでコンフリクトが起きたら、まずgit stash listで該当のstashがまだ残っているかを確認し、落ち着いてコンフリクトを解消してから、必要であれば手動でgit stash dropするという手順を踏むようにしましょう。

この記事のキーワード
git stash
コミットしていない作業ツリー・インデックスの変更を一時的に退避し、作業ツリーをHEADの状態に戻す操作
git stash pop / apply / drop
popは適用してリストから削除、applyは適用のみでリストに残す、dropは適用せず削除するだけ
git stash branch
stash作成時点のコミットから新しいブランチを作り、その上にstashの内容を適用する。applyがコンフリクトしそうな場合に有効
💡 ワンポイントアドバイス:迷ったらメッセージを付けて残す

stashを複数積み重ねる可能性があるなら、git stash push -m "途中のUI修正"のようにメッセージを付けておくと、後でgit stash listを見たときにどれがどの作業だったか一目で分かります。無名のまま何個も積み上げると、後から自分でも判別できなくなります。

退避した変更を元に戻すときも、コミットを取り消すときも、行き過ぎた操作を元に戻せるかどうかが重要になります。続くgit resetとgit revertでは、その「元に戻す」操作そのものを扱います。