iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Software Development

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

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

  • 分享至 

  • xImage
  •  

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

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

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

沒有 git stash 的情況

現在假設我們同時在 mainfeature 兩分支上工作。首先是在 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/20250916/20178513p8tc7kZWBr.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 把進度存到哪了?
系列文
深入一點點認識 Git22
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言