快轉到主要內容
  1. 教學文章/

Git stash 實戰:暫存工作現場、切換任務與 patch 管理完全攻略

·11 分鐘· loading · loading · ·
Git Git-Stash Patch Workflow Version-Control
每日拍拍
作者
每日拍拍
科學家 X 科技宅宅
目錄
版本控制: Git - 本文屬於一個選集。
§ 8: 本文

featured

一. 前言:你不是在亂改,你只是臨時被打斷
#

寫程式最煩的一種時刻, 不是 bug 很大, 而是你手上的東西才做到一半, 主管忽然丟來一個 hotfix, 同事又說要你幫忙看另一個 branch, 這時候工作目錄裡一半是還沒寫完的功能, 另一半是你自己也還不確定要不要留下的實驗碼。

如果直接硬切 branch, Git 多半會先警告你有未提交修改。

如果你為了切任務而亂 commit 一個 wip, 雖然能脫身, 但 commit 歷史通常也會開始出現一堆 tempfix laterdont break pls 之類的黑歷史。

這種時候, git stash 就非常像桌面上的「先收進抽屜」。

它不是正式歸檔, 也不是永久保存, 而是幫你把現在的工作現場暫時收起來, 讓你能乾淨切換到別的任務, 等回來時再把改動拿出來接著做。

如果你已經看過前面的 Git 入門Git Branch 策略完全攻略, 你大概已經知道 branch 怎麼切、 commit 歷史怎麼維持乾淨。

那今天這篇, 拍拍君要補上的就是一個超實用的中間技巧: 當你還不想 commit, 但又真的得先離開現場時, 該怎麼安全收納。

這篇會從基本的 stash pushlistapplypop 開始, 一路講到 -u-p--keep-indexstash 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 applypop 差在哪裡?
#

這是新手最常搞混的一組。

如果你想把 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
#

拍拍君自己很推薦這個流程:

  1. 先用 git add -p 把你確定成熟的內容 staged 起來
  2. 再用 git stash push --keep-index 把其餘雜訊收走
  3. 跑測試
  4. 做乾淨 commit
  5. 視情況把 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 很擅長做這些事:

  • 臨時中斷工作
  • 保持工作樹乾淨
  • 避免為了切任務而亂製造 wip commit
  • 配合 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, 親手試一次 pushlistshow -papplypopbranch

你真的動手跑過一輪之後, 下次被臨時打斷時, 就不太會再靠 wip commit 硬撐了。

延伸閱讀
#

版本控制: Git - 本文屬於一個選集。
§ 8: 本文

相關文章

Git rebase 完全攻略:整理 commit 歷史、互動式 rebase 與衝突處理
·11 分鐘· loading · loading
Git Rebase Interactive-Rebase Commit-History Version-Control
Git Branch 策略完全攻略:feature branch、main、hotfix 與協作流程
·11 分鐘· loading · loading
Git Branch Workflow Feature-Branch Hotfix
GitHub Actions 進階:Matrix Build + 自動發佈 PyPI
·5 分鐘· loading · loading
Github-Actions Cicd Pypi Matrix-Build Python
Github Actions: 自動化你的工作流
·5 分鐘· loading · loading
版本控制 Github Git
GitHub 指令工具
·2 分鐘· loading · loading
版本控制 Git 指令模式 Github
版本控制軟體 Git - 安裝篇
·3 分鐘· loading · loading
版本控制 Git