一. 前言:你不是在亂改,你只是臨時被打斷 #
寫程式最煩的一種時刻, 不是 bug 很大, 而是你手上的東西才做到一半, 主管忽然丟來一個 hotfix, 同事又說要你幫忙看另一個 branch, 這時候工作目錄裡一半是還沒寫完的功能, 另一半是你自己也還不確定要不要留下的實驗碼。
如果直接硬切 branch, Git 多半會先警告你有未提交修改。
如果你為了切任務而亂 commit 一個 wip, 雖然能脫身, 但 commit 歷史通常也會開始出現一堆 temp、fix later、dont break pls 之類的黑歷史。
這種時候, git stash 就非常像桌面上的「先收進抽屜」。
它不是正式歸檔, 也不是永久保存, 而是幫你把現在的工作現場暫時收起來, 讓你能乾淨切換到別的任務, 等回來時再把改動拿出來接著做。
如果你已經看過前面的 Git 入門 和 Git Branch 策略完全攻略, 你大概已經知道 branch 怎麼切、 commit 歷史怎麼維持乾淨。
那今天這篇, 拍拍君要補上的就是一個超實用的中間技巧: 當你還不想 commit, 但又真的得先離開現場時, 該怎麼安全收納。
這篇會從基本的 stash push、list、apply、pop 開始, 一路講到 -u、-p、--keep-index、stash branch, 最後再聊什麼情況適合 stash, 什麼情況其實更該直接 commit。
一句話先講結論: git stash 很好用, 但它最適合「短暫切換」, 不是拿來當長期倉庫。
二. 安裝與基本準備:先把觀念跟工具擺正 #
git stash 是 Git 內建功能, 所以不需要額外安裝套件。
先確認版本:
git --version
如果你還沒安裝 Git, 可以這樣裝:
# macOS
brew install git
# Ubuntu / Debian
sudo apt update
sudo apt install git
# Windows
winget install --id Git.Git -e
第一次安裝完, 也別忘了設定自己的身份:
git config --global user.name "拍拍君"
git config --global user.email "pypy@example.com"
2.1 git stash save 還能用,但建議你改用 push
#
老文章常會寫:
git stash save "message"
這個語法很多環境還能跑, 但現在更推薦用的是:
git stash push -m "message"
原因很單純: push 這個語法比較一致, 也支援更多選項, 像是 pathspec、-u、-p 之類的搭配都更直觀。
所以今天整篇, 拍拍君都用 git stash push 來示範。
2.2 先準備一個示範 repo #
我們先建一個小型測試環境:
mkdir stash-demo
cd stash-demo
git init
printf '# Stash Demo\n' > README.md
git add README.md
git commit -m 'chore: init repo'
再加兩個檔案:
mkdir -p src tests
printf "def login():\n return True\n" > src/auth.py
printf "def test_login():\n assert True\n" > tests/test_auth.py
git add .
git commit -m 'feat: add initial auth module'
接著切一條功能分支:
git switch -c feature/session-timeout
現在這個 repo 就很適合拿來練 stash。
2.3 先記住一句最重要的話 #
stash 暫存的是「你目前工作樹和索引區的改動」。
白話一點就是: 它收的是你還沒 commit 的修改。
所以使用前, 先養成看狀態的習慣:
git status
這個動作真的很重要。
因為很多 stash 的誤會, 不是指令本身危險, 而是你以為自己收進去了, 結果某些 untracked 檔案其實根本沒一起收。
三. 先學會三個核心動作:stash、list、apply / pop #
如果你只想先學會日常最常用的那 20%, 那就是這三件事:
- 把現場收起來
- 看目前抽屜裡有什麼
- 把某一份現場再拿回來
3.1 最基本的 stash #
假設你正在改登入逾時邏輯:
printf "\nTIMEOUT_MINUTES = 30\n" >> src/auth.py
printf "\ndef should_refresh(token_age):\n return token_age > 25\n" >> src/auth.py
這時候 git status 會看到:
git status --short
輸出可能像這樣:
M src/auth.py
如果這時候突然要切去修另一個 bug, 你可以先把現場收起來:
git stash push -m "wip: session timeout logic"
這個動作完成後, 工作目錄會回到乾淨狀態。
再看一次:
git status
你應該會看到:
nothing to commit, working tree clean
3.2 看目前 stash 裡有哪些東西 #
收進抽屜之後, 你可以用 list 檢查:
git stash list
例如:
stash@{0}: On feature/session-timeout: wip: session timeout logic
stash@{0} 代表最新的一筆。
數字越大, 通常越舊。
這個表示法之後會一直用到, 因為你可以指定要還原哪一筆 stash。
3.3 apply 跟 pop 差在哪裡?
#
這是新手最常搞混的一組。
如果你想把 stash 套回來, 但先不要刪掉 stash 紀錄, 用:
git stash apply stash@{0}
如果你想套回來, 而且成功後就把那筆 stash 從清單拿掉, 用:
git stash pop stash@{0}
拍拍君的建議很簡單:
- 第一次拿回重要改動時,先用
apply - 確認沒問題後,再手動
drop - 很熟、很確定只會用一次時,再用
pop
因為 apply 比較保守, 而保守通常比較不會把自己搞哭。
3.4 看 stash 裡到底存了什麼 #
只看 list 通常不夠, 你還會想知道內容差異。
可以用:
git stash show stash@{0}
這會顯示摘要。
如果你想看完整 diff:
git stash show -p stash@{0}
這招超級實用。
尤其當你連自己半天前到底收了什麼都忘了時, 先看 diff, 再決定要不要套回來, 會比直接 pop 安心得多。
四. 真正常用的切任務技巧:訊息、untracked、部分檔案 #
基本 stash 會用了之後, 你很快就會遇到三個實務問題:
- 我有新檔案,怎麼一起收?
- 我有很多改動,只想收部分?
- 我想留下已 staged 的東西,只收剩下的?
這些才是 stash 真正開始變好用的地方。
4.1 為每一筆 stash 寫訊息,真的很值得 #
你當下可能覺得自己記得, 但兩天後通常不記得。
所以請盡量不要只寫:
git stash
而是寫成:
git stash push -m "wip: session timeout middleware"
或:
git stash push -m "hotfix interrupted: login token refresh"
這樣 git stash list 才看得懂。
不然你很快就會擁有:
stash@{0}:不知道是什麼stash@{1}:也不知道是什麼stash@{2}:看起來更可疑
這種抽屜不是收納, 是未來的考古現場。
4.2 把 untracked 檔案一起收進去:-u
#
預設情況下, git stash push 不會收沒有被 Git 追蹤的新檔案。
假設你新增了一個本來還沒 git add 的檔案:
printf "def test_refresh():\n assert True\n" > tests/test_refresh.py
這時候 git status --short 可能是:
M src/auth.py
?? tests/test_refresh.py
如果你直接 stash:
git stash push -m "wip: timeout logic"
你會發現 src/auth.py 被收走了, 但 tests/test_refresh.py 還留在工作目錄。
如果你想連 untracked 檔案一起收, 請加上 -u:
git stash push -u -m "wip: timeout logic with new test"
這裡的 -u 是 --include-untracked。
實務上超常用。
因為很多「我明明 stash 了怎麼還有東西」的來源, 就是你忘了新檔案預設不會被收。
4.3 只收特定檔案 #
有時候你不是整個現場都想藏起來, 只是某幾個檔案暫時不想帶去另一條 branch。
這時候可以指定 pathspec:
git stash push -m "stash auth experiment" src/auth.py tests/test_auth.py
這代表只把這兩個路徑相關的改動收進 stash。
其他工作目錄裡的修改會保留。
這個技巧在 monorepo 特別好用。
例如你同時在改:
backend/frontend/infra/
但現在只想把 frontend/ 收起來, 就可以用 pathspec 精準操作。
4.4 只想暫時收未 staged 改動:--keep-index
#
這是另一個很容易被低估的神技。
先想像一個情境:
- 你已經把「確定要進下一個 commit」的內容
git add好了 - 但工作目錄還有一些還沒整理完的實驗改動
- 你現在想先跑測試,或暫時切走處理別的事
這時候你可以用:
git stash push --keep-index -m "unstaged experiment only"
--keep-index 的意思是: 保留索引區內容, 只把未 staged 的改動收起來。
這招很適合:
- 你想用 staged 內容做一個乾淨 commit
- 但還有一些半成品不想混進去
- 或你想先在已 staged 內容上跑測試
配合 git add -p 使用時, 會非常舒服。
五. Patch 管理才是 stash 的精髓:-p 讓你只藏起某幾塊改動
#
如果你已經開始習慣小步提交, 那你很快就會發現: 你不只是想 stash 某個檔案, 你其實只想 stash 檔案裡的一部分。
這時候就輪到 -p 登場了。
5.1 git stash push -p
#
假設 src/auth.py 裡同時有兩種改動:
- 一部分是登入流程修正
- 另一部分是你還在實驗的 refresh 邏輯
你不想整個檔案都收起來, 可以用互動式 patch 模式:
git stash push -p -m "stash only refresh experiment"
Git 會一塊一塊問你要不要把該 hunk 收進 stash。
常見選項像這樣:
y - stash this hunk
n - do not stash this hunk
q - quit
s - split current hunk into smaller hunks
這跟 git add -p 的操作感很像。
如果某一塊 diff 太大, 可以用 s 拆小。
這樣你就能精準控制: 哪些修改留下, 哪些修改先收走。
5.2 一個很實用的 workflow #
拍拍君自己很推薦這個流程:
- 先用
git add -p把你確定成熟的內容 staged 起來 - 再用
git stash push --keep-index把其餘雜訊收走 - 跑測試
- 做乾淨 commit
- 視情況把 stash 再拿回來繼續修
命令大概會像這樣:
git add -p
git stash push --keep-index -m "leftover experiments"
pytest -q
git commit -m 'feat: add session timeout middleware'
git stash pop
這套流程比「先全部 commit 成 wip 再回頭修」乾淨很多。
5.3 套回來之後,不一定要整包拿 #
你也可以不直接 apply 整個 stash, 而是從 stash 中挑特定檔案出來。
例如:
git restore --source=stash@{0} -- src/auth.py
或在舊版 Git 習慣裡, 你也可能會看到:
git checkout stash@{0} -- src/auth.py
這表示你只從 stash 把某個檔案拉回工作目錄。
如果你的 stash 裡混了很多東西, 但這次只想救一個檔案, 這招會超省事。
5.4 想先看 patch,再決定怎麼拿 #
別忘了前面提過的:
git stash show -p stash@{0}
很多時候你不是不會恢復, 而是你忘了那筆 stash 到底裝了哪些改動。
先看 patch, 再選擇:
- 整筆
apply - 整筆
pop - 單檔
restore - 或乾脆
drop
這樣比較不會亂套回一堆早就不相干的東西。
六. 切 branch、解衝突、救現場:進階實戰比想像中常用 #
當你已經會 stash 基本操作之後, 真正讓你覺得「原來這東西不是小配角」的, 通常是下面這幾個場景。
6.1 最經典場景:做一半被叫去修 hotfix #
假設你正在 feature/session-timeout, 結果 production 爆了登入 bug。
流程可以這樣走:
git status
git stash push -u -m "wip: session timeout before login hotfix"
git switch main
git pull --ff-only
git switch -c hotfix/login-null-token
你修完 hotfix 後:
git add .
git commit -m 'fix: guard null token in login flow'
git push -u origin hotfix/login-null-token
等 hotfix 結束, 再切回原本分支:
git switch feature/session-timeout
git stash pop
這就是 stash 最標準的使用場景。
不是為了偷懶, 而是為了讓你中途切出去時, 不要把半成品帶得到處都是。
6.2 直接從 stash 開一條 branch:git stash branch
#
這招很多人不知道, 但其實超好用。
如果你發現某筆 stash 其實值得獨立做成一條分支, 可以直接:
git stash branch feature/recover-timeout-experiment stash@{0}
它會做幾件事:
- 建立新 branch
- 以 stash 原本的基底切出去
- 把那筆 stash 套回來
- 若成功,通常會把 stash 刪掉
這很適合這種情況:
你本來只是暫存, 但後來發現那個實驗其實值得獨立開發, 不該硬塞回原來分支。
比起你自己手動切 branch、再 apply, stash branch 更不容易出錯。
6.3 pop 發生衝突怎麼辦?
#
這也是高頻問題。
如果你 git stash pop 時遇到衝突, Git 會把檔案標成 conflict 狀態。
這時候不要慌, 流程跟一般 merge / rebase 衝突很像:
git status
打開衝突檔案, 把內容整理好之後:
git add src/auth.py
git add tests/test_auth.py
接著你可以自己決定:
- 繼續保留目前工作目錄,手動 commit
- 或如果是
apply,確認完成後再git stash drop
一個容易忽略的小點是: 如果 pop 沒有完全成功, 那筆 stash 往往還會留著。
這其實是好事。
因為 Git 知道你還沒真正安全套回去, 所以不急著幫你刪抽屜裡那份備份。
6.4 stash 不是備份系統,別把它放到忘記 #
雖然 stash 很方便, 但它不是長期保存機制。
如果你把某筆重要改動丟在 stash 裡兩週、 三週、 一個月, 最後通常只會有三種結果:
- 你完全忘了它存在
- 你已經忘了它是為什麼存在
- 你套回來時發現上下文早就變了
所以拍拍君的原則很簡單:
- 幾小時到一兩天內會回來的東西,可以 stash
- 超過這個時間,通常更適合開 branch 或 commit
6.5 手滑 drop 了,還有機會救嗎? #
有時候會。
但不保證。
如果你不小心 git stash drop, 理論上還有可能透過底層物件找回來, 例如:
git fsck --no-reflogs | rg commit
不過這種救援就已經偏考古了, 而且不保證一定成功。
所以與其期待救援, 不如在重要內容上優先用:
apply而不是pop- 命名清楚的 stash
- 真正重要時直接開 branch
七. 什麼時候該 stash,什麼時候其實更該 commit? #
這一題很重要。
因為很多人把 stash 用得不順, 不是 stash 不好, 而是它被拿去做本來就不適合它的事。
下面先給你一個簡單判斷表。
| 情境 | 比較適合 stash | 比較適合 commit / branch |
|---|---|---|
| 臨時被打斷,要先切去修別的 bug | ✅ | |
| 只是想先收起半成品,幾小時後回來 | ✅ | |
還沒整理好,不想留下 wip commit |
✅ | |
| 改動已經有清楚意圖,值得保留歷史 | ✅ | |
| 可能幾天後才回來做 | ✅ | |
| 需要跟別人分享目前進度 | ✅ | |
| 想拿它當備份 | ✅ |
7.1 stash 的強項 #
git stash 很擅長做這些事:
- 臨時中斷工作
- 保持工作樹乾淨
- 避免為了切任務而亂製造
wipcommit - 配合 patch 模式做更細緻的改動管理
它像是短期緩衝區。
很靈活, 很方便, 但不該變成長住的倉庫。
7.2 commit / branch 的強項 #
如果你的改動:
- 已經有清楚語意
- 想保留歷史
- 需要跨機器同步
- 可能之後要拿來 review
- 可能需要跟同事協作
那通常應該直接 commit, 或至少切一條 branch。
因為 stash 主要存在於你的本地工作流裡, 它不是拿來當協作介面。
7.3 一個很實際的判斷句 #
如果你問自己:
「這份改動如果我明天睡醒忘了, 會不會出事?」
如果答案是會, 那八成不要只 stash。
請 commit。
甚至 push。
這樣比較像工程流程, 也比較像在善待未來的自己。
八. 一個完整實戰流程:半成品功能 + 緊急 hotfix + 回來續做 #
最後, 拍拍君把整個常見場景串成一套命令給你。
假設你正在做 session timeout 功能, 目前狀態是:
src/auth.py有半成品修改tests/test_refresh.py是新建未追蹤檔案README.md還有一點不重要的筆記改動
突然 production 要你先修 login hotfix。
你可以這樣做:
git status --short
git stash push -u -m "wip: session timeout interrupted by login hotfix"
git switch main
git pull --ff-only
git switch -c hotfix/login-guard
修完 hotfix:
git add .
git commit -m 'fix: prevent login crash on missing token'
git push -u origin hotfix/login-guard
回到原本工作:
git switch feature/session-timeout
git stash list
git stash show -p stash@{0}
git stash apply stash@{0}
如果確認內容沒問題, 再刪掉那筆 stash:
git stash drop stash@{0}
如果你套回來後發現, 這份改動其實更值得獨立整理成另一條 branch, 也可以改走:
git stash branch feature/session-timeout-v2 stash@{0}
這整套流程的核心精神其實只有兩件事:
- 切任務時保持現場整潔
- 回來時用最少風險把上下文接回來
而 git stash 就是在這兩件事之間, 提供了一個非常實用的緩衝層。
結語:把 stash 當抽屜,不要當倉庫 #
git stash 真正厲害的地方, 不是它能把東西藏起來, 而是它讓你在多工切換時, 仍然能維持乾淨、有秩序的工作流。
你可以用它:
- 暫時收起半成品
- 快速切去修 hotfix
- 透過
-u收新檔案 - 透過
--keep-index保留 staged 內容 - 透過
-p精準只收某些 patch - 透過
stash branch把臨時想法轉成正式分支
但也別忘了, 它的定位是短期緩衝, 不是長期備份。
當你的改動已經成熟、 有清楚意圖、 值得留下歷史時, 還是該回到 branch 與 commit 這些更正式的工具。
把 stash 用對, 你會覺得 Git 沒那麼煩。
把 stash 用成黑洞, 你會覺得未來的自己很可憐。
拍拍君的建議是: 今天就開一個小 repo, 親手試一次 push、list、show -p、apply、pop、branch。
你真的動手跑過一輪之後, 下次被臨時打斷時, 就不太會再靠 wip commit 硬撐了。