現代化 Web 開發技術學習分享

0%

Git 版本控制系統 - stash 暫存修改內容

前言

有時候我們會遇到需臨時修改它分支的狀況,這邊的臨時代表正在開發的項目並不是它分支的內容,比如說 master 分支需做緊急修改,但我們目前正在跑 feature 分支的進度,這時你可能會將 feature 尚未提交成 commit 的修改內容複製到一個空白文件上,以確保修改內容不會不見,最後當 master 分支修改完成時,再將修改內容貼回到 feature 上,這確實也是個辦法,但其實不用這麼麻煩,Git 本身就有專門處理此情況的指令,名為 stash,可將當下分支的修改內容暫存起來,日後有需要再將它取出即可。

筆記重點

  • stash 暫存修改內容
  • reset 模擬暫存修改內容
  • Git 指令回顧

stash 暫存修改內容

讓我們先新增一個專案並提交兩次 commit 紀錄:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mkdir project

cd project

git init

touch index.html

git add .

git commit -m 'add index.html'

touch all.css

git add .

git commit -m 'add all.css'

建立 feature 分支並提交數次 commit 紀錄

1
2
3
4
5
6
7
git checkout -b feature

touch all.js

git add .

git commit -m 'add all.js'

假設 feature 分支第一階段的需求已經達到了:

1
2
3
git checkout master

git merge feature --no-ff

此時的線路圖狀態為:

查看目前 commit 紀錄-1

feature 分支已被合併到 master 分支,接著進行第二階段的工作:

1
2
3
4
5
git checkout feature

... edit index.html

touch db.json

此時的檔案狀態為:

查看檔案狀態-1

假設老闆臨時叫你去修改 master 分支的內容,我們可使用以下指令將當前分支的修改給暫存下來:

  • 暫存當前分支修改內容:
1
git stash
  • 暫存當前分支修改內容 (包含未追蹤):
1
git stash -u

因為 db.json 屬於未追蹤的檔案,所以在這邊需要添加 -u 參數告知需包含未追蹤檔案,在這邊補充一點,你可能看過下面這道類似的命令:

1
git stash save

這道命令在 v2.16 的 Git 版本已被棄用,取而代之的是使用 push 命令:

1
git stash push

事實上,這兩道命令都可以達到相同的目的,只差在 save 無法擁有 push 的 pathspec 參數功能,由於較冷門,這邊我們不討論 pathspec 參數,你可能會好奇,那 git stash push 的功能是什麼?簡單來講,git stash 就等效於 git stash push,少打 push 只是方便你輸入而已,但我建議還是輸入完整指令比較好,等等會在說明,此時的檔案狀態為:

查看檔案狀態-2

你會發現修改內容都不見了,如同使用 git checkout .git clean -f 一般,事實上,它是把修改內容都暫存到了 /.git/refs/stash 檔案內,我們可使用以下指令查看所有暫存內容:

1
git stash list

暫存以清單呈現:

git stash list 列出暫存清單-1

為什麼會是以清單呈現呢?暫存檔總不可能只有一個吧,這邊我們到 master 分支也新增一個暫存檔看看:

1
2
3
4
5
git checkout master

... edit all.css

git stash push -m 'edit all.css'

這邊有一個大雷點,-m 參數是用來告知 Git 使用自訂的暫存訊息,必須使用完整的 git stash push 才能作用,你沒辦法寫做 git stash -m,這樣會跳找不到命令,這是我實測的結果,所以為什麼前面說盡量寫成完整的命令,就是這個原因,此時的暫存內容就會多一筆:

git stash list 列出暫存清單-2

目前我們已經把修改內容暫存到 Git 內了,假設你已經完成臨時的任務,此時可使用以下指令取出暫存內容:

  • 將最新暫存套用至當前分支,成功後銷毀暫存 (即 stash@{0}):
1
git stash pop
  • 將指定暫存套用至當前分支,成功後銷毀暫存:
1
git stash pop stash@{1}

以下為 feature 分支取出暫存內容的結果:

使用 pop 取出暫存內容

此時你當時所做的修改就都回來了,是不是很方便?使用 pop 可以把某個 stash 拿出來並套用在目前的分支上,套用完成即銷毀這個暫存內容,如果你不想銷毀暫存內容,可改用以下指令:

  • 將最新暫存套用至當前分支,成功後保留暫存:
1
git stash apply
  • 將指定暫存套用至當前分支,成功後保留暫存:
1
git stash apply stash@{1}

apply 只會將暫存做套用的動作,並不會銷毀暫存,如果你不想要某個暫存了,可使用以下指令:

  • 刪除最新暫存 (即 stash@{0}):
1
git stash drop
  • 刪除指定暫存:
1
git stash drop stash@{1}
  • 刪除全部暫存:
1
git stash clear

以上就是 stash 命令的相關操作。

reset 模擬暫存修改內容

這邊主要參考 為你自己學 Git 文章,使用 reset 來模擬暫存修改內容,讓我們先看目前的狀態:

查看檔案狀態-3

由於剛剛我們只有套用 feature 分支的暫存檔,這邊我們也連同套用 master 分支的暫存檔。

其實關於暫存的操作並不一定要使用 stash 來完成,我們也可以透過土法煉鋼的方式來操作,直接將修改內容提交至本地庫:

1
2
3
git add .

git commit -m 'stash file'

此時的 Git 就會是 nothing to commit 狀態,與使用 git stash 結果相同,假設我們完成它分支的任務了,使用以下指令把 commit 還原到工作目錄:

1
git reset HEAD^ --mixed

--mixed 為 reset 預設模式,即丟回工作目錄,這邊千萬不要使用 --hard 模式阿,你的修改內容就要說掰掰了 (還是可以用 reflog 查詢並還原拉)。

此時的結果就會與使用 git stash pop 相同,並沒有說哪一種方式比較好,只要你夠熟悉 Git 指令,確實有相當多的方法可以達到同樣效果。

Git 指令回顧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 暫存當前分支修改內容
git stash

# 暫存當前分支修改內容 (包含未追蹤)
git stash -u

# 暫存當前分支修改內容 (已棄用)
git stash save

# 暫存當前分支修改內容 (完整指令)
git stash push

# 查看所有暫存項目
git stash list

# 暫存當前分支修改內容 (修改預設暫存訊息)
git stash push -m 'message'

# 將最新暫存套用至當前分支,成功後銷毀暫存 (即 stash@{0})
git stash pop

# 將指定暫存套用至當前分支,成功後銷毀暫存
git stash pop stash@{1}

# 將最新暫存套用至當前分支,成功後保留暫存 (即 stash@{0})
git stash apply

# 將指定暫存套用至當前分支,成功後保留暫存
git stash apply stash@{1}

# 刪除最新暫存 (即 stash@{0})
git stash drop

# 刪除指定暫存
git stash drop stash@{1}

# 刪除全部暫存
git stash clear