iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Software Development

深入一點點認識 Git系列 第 16 篇

Day 16-深入一點點認識 Git:用 git stash 先暫存進度

  • 分享至 

  • xImage
  •  

Medium 好讀版點此。


當我們正在進行某項工作,突然因為老闆/專案經理等人要求或種種原因,要先把手上事情擱著,先處理更急的事情(作業系統中的「上下文交換(context switch)」?),那既有進度要怎麼辦?

第一個方法是先 git commit 成一個版本,之後再回來用 git reset 把這暫時的版本退掉,但這樣如果沒弄好(忘記退版或沒整理好 commits),就會變成有個「事情做到一半的 commit」,有沒有什麼可以先把進度存著、但不要成為 commit 的方法?

要達到這個目的,可以改用 git stash 指令,以下透過動手做實驗,觀察 git stash 的運作方式跟 git commit 有何不同。

沒有 git stash 的情況

現在假設我們同時在 main 跟 feature 兩分支上工作。首先是在 main 分支上建立檔案:

echo "Hello, world" > hello_world.txt

接著把 hello_world.txt 加進預存區:

git add hello_world.txt

再形成一個 commit:

git commit -m "Initial commit"

再創建 feature 分支:

git branch feature

切換到 feature 分支上:

git switch feature

在 feature 分支建立同樣名為 hello_world.txt 的檔案,但內文與 main 分支不同,為 "Feature version":

echo "Feature version" > hello_world.txt

在這裡一樣把檔案加到預存區:

git add hello_world.txt

再形成一個 commit:

git commit -m "Feature change"

現在 .git/ 資料夾長這樣:

.git/
├── hooks/
├── info/
│
├── logs/
│   ├── refs/
│   │    └── heads/       # 有main跟feature兩分支的歷史紀錄
│   │         ├── feature
│   │         └── main
│   └── HEAD              # 隨著分支切換,會有兩分支的commit在歷史紀錄中
│
├── objects/
│   ├── 9e/                                         # tree物件
│   │   └── 206bdfe0f75cf9011bc37c3a77528b50993a7d
│   ├── 70/                                         # commit物件
│   │   └── 1703a5c1a78cac150449da66bb8975249db1f7
│   ├── a5/                                         # blob物件
│   │   └── c19667710254f835085b99726e523457150e03
│   ├── db/                                         # blob物件
│   │   └── c69068b98a226af5471ad0543d5ad3316b4cb7
│   ├── e5/                                         # tree物件
│   │   └── 9896eb1f48ef9c75e675692e23966e5ba7cdfb
│   ├── fe/                                         # commit物件
│   │   └── a8f64e7cfad13fe622553464a3df357a28c919
│   └── info/
│   └── pack/
│
├── refs/
│   ├── heads/
│   │     ├── feature # 指向fea8f64e7cfad13fe622553464a3df357a28c919
│   │     └── main # 指向701703a5c1a78cac150449da66bb8975249db1f7
│   └── tags/
│
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD      # ref: refs/heads/feature
└── index

圖示如下:
https://ithelp.ithome.com.tw/upload/images/20250916/20178513gLaQJCZY0q.png

這時切回 main 分支:

git switch main

假設繼續針對這個 hello_world.txt 做一些改動:

echo "Main version" > hello_world.txt

https://ithelp.ithome.com.tw/upload/images/20251022/20178513mBB0b0YqMW.png

此時老闆突然說 feature 分支還有一些急事要處理,要我們先擱置 main 分支上的事情,於是切回 feature 分支:

git switch feature

這時卻發現:切不過去!
https://ithelp.ithome.com.tw/upload/images/20250916/20178513uA7biG6SbF.png

怎麼會這樣?

原因與解決方法

這其實是 git 在保護我們!在 main 分支上,我們已經針對 hello_world.txt 做了一些改動,但並沒有將這個改動變成一個 commit,如果直接切到 feature 分支,feature 分支並沒有記錄到 main 上的最新改動,因此如果貿然切換分支,main 的最新版本會被 feature 的舊版本蓋過去(如提示訊息說的 would be overwritten)。

因此,要切換分支前,我們得先確保 main 分支上已經記錄了最新的版本。

方法一:git commit 再 git reset

既然得先形成一個版本,那就直接把 main 的最新版變成一個 commit、等在 feature 分支完成工作要切回來 main 分支時,再 git reset 不就得了?

可以!但在這篇文章,我們將探討另外一招,不會形成真正的 commit,但會把最新狀態保留下來,如此可避免 commit 中出現「事情做一半」版本的困擾。

方法二:git stash

要把狀態暫存,但用不形成 commit 可以用 git stash,把 main 分支上仍在進行中(Work In Progress,簡稱 WIP)的工作目錄與預存區暫存,就可以順利切到 feature 分支:
https://ithelp.ithome.com.tw/upload/images/20250916/20178513mcpLRnKqD8.png

現在我們在 feature 分支上也做一些改動:

echo "Feature second version" > hello_world.txt

假設又有臨時任務要切回 main 分支,也可以下 git stash 後切回(沒有 git stash 一樣切不回):
https://ithelp.ithome.com.tw/upload/images/20250916/20178513f0O1yiYUbR.png
上半部因沒有 git stash,切不回 main 分支;下半部有經過 git stash 才能切

這時候透過 git stash list 可以查看目前有的暫存狀態:
https://ithelp.ithome.com.tw/upload/images/20250916/201785136eLqnjdNDU.png

現在我們在 main 分支,要把剛剛在 main 分支的進度(也就是 stash@{1},表示距離當下前前一個 stash)拿出來繼續,可以用以下指令:

git stash pop stash@{1}

這時就可以回到 main 分支剛剛暫存的狀態(內文為 "Main version")繼續工作。目前從暫存回來的狀態還沒被 git add、也還沒被 git commit:
https://ithelp.ithome.com.tw/upload/images/20250916/20178513Ly9IVgo6jJ.png

而因為 stash@{1} 已經被撿回來用,所以這筆暫存不見,在 git stash list 指令中,只剩下 feature 分支上的 stash:
https://ithelp.ithome.com.tw/upload/images/20250916/20178513ZePHCiOn1P.png

現在我們把 main 分支上的改動直接加到預存區:

git add hello_world.txt

並讓其形成一個 commit:

git commit -m "Modified on main branch"

因為 main 分支上已經存成一個版本,可以輕鬆切回 feature 分支:
https://ithelp.ithome.com.tw/upload/images/20250916/20178513eblIB47BYT.png

在 feature 分支上把剛剛的暫存狀態 stash@{0} 以 git stash pop 叫回來:
https://ithelp.ithome.com.tw/upload/images/20250916/2017851391TXI5D4bf.png

就可以在 feature 分支上次暫存的狀態(內文為 "Feature second version")繼續工作啦!

小結

在以下狀況時,將無法順利切換分支:

  1. 目前在某分支。
  2. 在某分支上修改了特定檔案。
  3. 要切過去的目標分支有同一檔案的不同版本。

為了避免另一分支的版本蓋過目前分支的,將會無法順利切換,解法有二:

  • 一、git commit
    先在原分支以 git commit 做成一個版本再切換分支,待之後從其他分支切回來,再以 git reset 復原。

  • 二、git stash
    把當前工作目錄與預存區狀態存到一個可用 git stash list 查詢的地方,待從其他分支切回來,直接用 git stash pop 指令復原,就不會形成事情做一半的 commit。

參考資料

  1. git-stash
  2. 【狀況題】手邊的工作做到一半,臨時要切換到別的任務

上一篇
Day 15 - 深入一點點認識 Git:可以是、也可以不是物件的 tag
下一篇
Day 17-深入一點點認識 Git:git stash 把進度存到哪了?
系列文
深入一點點認識 Git 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言