コードレビューが降ってくる。3ファイルが修正途中で、動かせるのは1つだけ。全部まとめてstashし、他の2つを打ち直す——誰も懐かしまない儀式だ。
TL;DR:git stash push -m "理由" -- <path> は指定パスだけをstashし、他の変更済みファイルには触れない。 戻すのは git stash pop(または任意のstashから1ファイルだけ git restore --source stash@{0} -- <path> で取り出す)。pathspec形式は2017年5月リリースのgit 2.13で登場——直近8年のツールチェーンなら必ず使える。
git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop ツリーではなく、ファイルをstashする。
gitで1つのファイルだけstashするには?
素の git stash は作業ツリー全体を掃き溜める——追跡済みの変更はすべてstashへ行き、全てが HEAD に戻る。変更済みファイルが混在しているときには、これが間違った形だ。1つは渡せる状態、残りは正直まだ途中。答えはpathspec付きの push。公式のgit-stashドキュメントどおりだ:
# 前:3つの変更済みファイル
$ git status --short
M app.conf
M notes.md
M main.py
# app.confだけstash
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak
$ git status --short
M notes.md
M main.py app.conf はコミット済みの状態に戻り、stash@{0} に収まった。notes.md と main.py は微塵も動いていない。-m のメッセージは任意だが、git stash list に2件目が並んだ瞬間から3秒の価値がある——「WIP」という名のstashは古びるのが早い。
効く細部が2つ:
- pathspecは
--の後に置く。 二重ダッシュ以降はすべてパスであり、フラグではない。git checkout -- <path>と同じ作法で、飾りではない。古いバージョンではgit stash push -- main.pyとgit stash push main.pyは異なり、裸のパスは誤読され得た。 - 未追跡ファイルには
-uが要る。 真っ新なファイルにはstashすべき追跡済み履歴がなく、素のpushは飛ばす。git stash push -u -- newfile.pyなら含まれる。-aはさらに進んで、無視対象も掃き込む。
1つのファイルの一部だけstashするには?
ファイル自体が途中——良いhunkが3つ、見られたくないのが1つ——というときは、対話フローが刻む:
git stash -p # または: git stash push -p Gitは各hunkを巡り Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]? と尋ねる。stashに入れるhunkには y、残すものには n。結果は承認したものだけを含むstashになり、ファイルの残りは変更済みのまま作業ツリーに残る。同じhunk単位の仕組みは git add -p も動かしているので、反射神経はそのまま転用できる。
git stash push -- <path> | git stash -p | |
|---|---|---|
| 粒度 | ファイル単位 | hunk単位 |
| 速度 | 1コマンド、スクリプト可 | 対話、hunkずつ |
| CIでの再現 | 可(パスを引数に) | 不可——プロンプトに人が要る |
| 向いているのは | 「このファイル、他はいらない」 | 「この変更、あれは違う」 |
| 以降 | git 2.13(2017年5月) | 昔から利用可 |
ドキュメントのモデルそのものからの実務則:境界がファイルならpathspec、その内側なら -p。
stashしたファイルを戻すには?
git stash pop は stash@{0} を復元し、エントリを消す。通常の道だ——ただしpopはstash単位で全か無か。コンフリクトが起きるとpopは中断されるが、エントリは残る。より細かい選択肢が2つ:
# 1. 消さずに適用(安全に繰り返せる)
git stash apply stash@{0}
# 2. stashから1ファイルだけ取り出し、エントリは残す
git restore --source stash@{0} -- app.conf
# 同じ操作の2.23以前の書き方:
git checkout stash@{0} -- app.conf restore/checkout 形式が答えるのは「3つの変更をまとめてstashしたが、今必要なのは1つだけ」という局面——そのパスのstash内容を作業ツリーへコピーし、stash@{0} はそのまま立つ。注意:作業ツリーのコピーに上書きする。そのパスの現在の編集が大事なら、先にdiff:
git diff stash@{0} -- app.conf stashはreflog風のスタック上の実コミットとして保存される。だから stash@{0} はどんなcommit-ish構文も受け付け、誤って消したstashもGCが食うまではreflogから復元できる。
stashが間違った道具になるのはいつか
stashはブランチではない:git branch に名前はなく、レビューもなく、デフォルトでdiffは表示されず、エントリは黙って積もって忘れられる。作業がマシンや日をまたぐコンテキスト切替に耐える必要があるなら、ブランチにコミットせよ——履歴が付いた方が名前のないスタックより強い。駐車済みのコミットを別の場所に降ろすならgit cherry-pick。駐車ではなく取り消しが目的なら、直前のコミットを取り消すの決定木の出番だ。
文書化された挙動から引いた、正直な故障台帳:
| 症状 | 原因 | 修正 |
|---|---|---|
| stash後にファイルがない | 未追跡パスは飛ばされる | git stash push -u -- <path> |
popで error: Your local changes ... would be overwritten | stash以降にパスが変わった | 新しい状態をコミットかstash、その後pop |
| popした変更が消えた | popがコンフリクトで中断 | 解決して git stash apply |
| 「どのstashだっけ?」 | 名前のないstashの山 | 毎回 -m メッセージを |
pathspecを git stash push に加えた2017年5月のリリースノートは8歳になり、git stash の筋肉記憶の大半はそれより古い。学び直す価値はある:ツリー全体の一掃は今や特殊ケースであり、デフォルトではない。
FAQ
gitで1つのファイルだけstashするには?
git stash push -m "メモ" -- path/to/file — pathspec形式は指定したパスだけをstashし、他の変更済みファイルには触れません。git 2.13以降(2017年5月)が必要です。
stashから1つのファイルだけ取り出すには?
git restore --source stash@{0} -- path/to/file (旧表記は git checkout stash@{0} -- path/to/file)。stashのエントリを消さずに、退避済みの内容を作業ツリーへコピーします。
git stashが新しいファイルを無視するのはなぜ?
未追跡(untracked)ファイルは通常のstashの対象外です。-uを付けると含まれます:git stash push -u -- path/to/file。無視(ignored)ファイルは-aが必要です。
— mrsaynothing
— mrsaynothing
AI・Linux・セルフホスティングの実地メモ。
次のハウツーをメールで受け取る
投稿ごとに1通。直して先へ進む。
これは何?Field Notes #1: エージェント運用サイト。リリースするのは機械、承認するのは私。
記事を楽しんでいただけましたか?私の本業はこのような構築です。 採用のご相談