Git
章節 ▾ 第二版

7.3 Git 工具 - 儲藏與清理

儲藏與清理

通常,當您在專案的某部分工作時,事情會處於混亂的狀態,而您想要暫時切換分支來處理其他事情。問題是,您不想提交半成品的工作,只是為了稍後可以回到這個狀態。這個問題的解決方案是 git stash 命令。

儲藏會取得您工作目錄的髒亂狀態(也就是您已修改的追蹤檔案和已暫存的變更),並將其儲存在未完成的變更堆疊中,您可以隨時重新套用(即使在不同的分支上)。

注意
移轉到 git stash push

截至 2017 年 10 月底,Git 郵件清單上進行了廣泛的討論,其中命令 git stash save 已被棄用,轉而支持現有的替代方案 git stash push。這樣做的主要原因是 git stash push 引入了儲藏選定的 *路徑規格* 的選項,而 git stash save 不支援此選項。

git stash save 並不會很快消失,因此不用擔心它會突然消失。但您可能需要開始移轉到使用 push 替代方案以使用新功能。

儲藏您的工作

為了示範儲藏,您將進入您的專案,開始處理幾個檔案,並可能暫存其中一個變更。如果您執行 git status,您可以看到您的髒亂狀態

$ git status
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

現在您想要切換分支,但您還不想提交您一直在處理的工作,因此您將儲藏變更。若要將新的儲藏推送到您的堆疊,請執行 git stashgit stash push

$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 Create index file"
HEAD is now at 049d078 Create index file
(To restore them type "git stash apply")

您現在可以看到您的工作目錄是乾淨的

$ git status
# On branch master
nothing to commit, working directory clean

此時,您可以切換分支並在其他地方工作;您的變更會儲存在您的堆疊中。若要查看您已儲存的儲藏,您可以使用 git stash list

$ git stash list
stash@{0}: WIP on master: 049d078 Create index file
stash@{1}: WIP on master: c264051 Revert "Add file_size"
stash@{2}: WIP on master: 21d80a5 Add number to log

在此情況下,先前已儲存了兩個儲藏,因此您可以存取三個不同的儲藏工作。您可以使用原始儲藏命令的說明輸出中顯示的命令重新套用您剛才儲藏的變更:git stash apply。如果您想要套用較舊的儲藏之一,您可以透過命名來指定它,例如:git stash apply stash@{2}。如果您未指定儲藏,Git 會假設為最近的儲藏並嘗試套用它

$ git stash apply
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   index.html
	modified:   lib/simplegit.rb

no changes added to commit (use "git add" and/or "git commit -a")

您可以看到,當您儲存儲藏時,Git 會重新修改您還原的檔案。在此情況下,當您嘗試套用儲藏時,您有一個乾淨的工作目錄,並且您嘗試將其套用到您儲存它的同一個分支上。擁有乾淨的工作目錄並將其套用到同一個分支並非成功套用儲藏的必要條件。您可以在一個分支上儲存儲藏,稍後切換到另一個分支,然後嘗試重新套用變更。當您套用儲藏時,您的工作目錄中也可以有已修改和未提交的檔案 — 如果有任何內容不再能順利套用,Git 會給您合併衝突。

您對檔案的變更已重新套用,但您之前暫存的檔案並未重新暫存。若要執行該操作,您必須執行 git stash apply 命令,並搭配 --index 選項,以告知該命令嘗試重新套用已暫存的變更。如果您改為這樣執行,您就能回到原始狀態。

$ git stash apply --index
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

apply 選項僅嘗試套用儲藏的工作,您仍然將其保留在堆疊中。若要移除它,您可以執行 git stash drop 並指定要移除的儲藏名稱。

$ git stash list
stash@{0}: WIP on master: 049d078 Create index file
stash@{1}: WIP on master: c264051 Revert "Add file_size"
stash@{2}: WIP on master: 21d80a5 Add number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

您也可以執行 git stash pop 來套用儲藏,然後立即將其從堆疊中移除。

創造性儲藏

還有一些可能有用的儲藏變體。第一個相當受歡迎的選項是 git stash 命令的 --keep-index 選項。這會告知 Git 不僅將所有暫存的內容包含在正在建立的儲藏中,同時也會將其保留在索引中。

$ git status -s
M  index.html
 M lib/simplegit.rb

$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
M  index.html

您可能想要對儲藏執行的另一個常見操作是同時儲藏未追蹤的檔案和已追蹤的檔案。預設情況下,git stash 只會儲藏已修改和暫存的已追蹤檔案。如果您指定 --include-untracked-u,Git 會將未追蹤的檔案包含在正在建立的儲藏中。但是,將未追蹤的檔案包含在儲藏中仍然不會包含明確忽略的檔案;若要額外包含忽略的檔案,請使用 --all (或僅使用 -a)。

$ git status -s
M  index.html
 M lib/simplegit.rb
?? new-file.txt

$ git stash -u
Saved working directory and index state WIP on master: 1b65b17 added the index file
HEAD is now at 1b65b17 added the index file

$ git status -s
$

最後,如果您指定 --patch 標誌,Git 不會儲藏所有已修改的內容,而是會互動式地提示您要儲藏哪些變更,以及要保留在工作目錄中的哪些變更。

$ git stash --patch
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 66d332e..8bb5674 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -16,6 +16,10 @@ class SimpleGit
         return `#{git_cmd} 2>&1`.chomp
       end
     end
+
+    def show(treeish = 'master')
+      command("git show #{treeish}")
+    end

 end
 test
Stash this hunk [y,n,q,a,d,/,e,?]? y

Saved working directory and index state WIP on master: 1b65b17 added the index file

從儲藏建立分支

如果您儲藏了一些工作,將其保留在那裡一段時間,並繼續從您儲藏工作的分支上工作,您可能會在重新套用工作時遇到問題。如果套用嘗試修改您之後修改過的檔案,您會遇到合併衝突,並且必須嘗試解決它。如果您想要一個更簡單的方式來再次測試儲藏的變更,您可以執行 git stash branch <新分支名稱>,它會使用您選取的分支名稱為您建立一個新分支,檢出您儲藏工作時所在的提交,在那裡重新套用您的工作,然後在成功套用時移除儲藏。

$ git stash branch testchanges
M	index.html
M	lib/simplegit.rb
Switched to a new branch 'testchanges'
On branch testchanges
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	modified:   index.html

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   lib/simplegit.rb

Dropped refs/stash@{0} (29d385a81d163dfd45a452a2ce816487a6b8b014)

這是一個方便的快捷方式,可以輕鬆恢復儲藏的工作並在新分支中進行處理。

清理您的工作目錄

最後,您可能不想在您的工作目錄中儲藏某些工作或檔案,而只是想擺脫它們;這就是 git clean 命令的用途。

清理您的工作目錄的一些常見原因是移除合併或外部工具產生的雜亂內容,或移除建置成品以便執行乾淨的建置。

您需要非常小心這個命令,因為它旨在從您的工作目錄中移除未追蹤的檔案。如果您改變主意,通常無法恢復這些檔案的內容。一個更安全的選項是執行 git stash --all 來移除所有內容,但將其儲存在儲藏中。

假設您確實想要移除雜亂檔案或清理您的工作目錄,您可以使用 git clean 來執行此操作。若要移除您的工作目錄中所有未追蹤的檔案,您可以執行 git clean -f -d,它會移除任何檔案,以及因此變為空的任何子目錄。-f 表示「強制」或「真的要這麼做」,如果 Git 配置變數 clean.requireForce 未明確設定為 false,則必須使用此選項。

如果您想了解它會執行什麼操作,您可以執行帶有 --dry-run (或 -n) 選項的命令,這表示「執行試執行並告訴我您將會移除的內容」。

$ git clean -d -n
Would remove test.o
Would remove tmp/

預設情況下,git clean 命令只會移除未被忽略的未追蹤檔案。任何與您的 .gitignore 或其他忽略檔案中的模式匹配的檔案都不會被移除。如果您也想移除這些檔案,例如移除建置產生的所有 .o 檔案以便執行完全乾淨的建置,您可以將 -x 新增到 clean 命令中。

$ git status -s
 M lib/simplegit.rb
?? build.TMP
?? tmp/

$ git clean -n -d
Would remove build.TMP
Would remove tmp/

$ git clean -n -d -x
Would remove build.TMP
Would remove test.o
Would remove tmp/

如果您不知道 git clean 命令會執行什麼操作,請務必先執行 -n 來仔細檢查,然後再將 -n 變更為 -f 並實際執行。另一種確保此過程安全的方法是使用 -i 或「互動式」標誌來執行它。

這將以互動模式執行 clean 命令。

$ git clean -x -i
Would remove the following items:
  build.TMP  test.o
*** Commands ***
    1: clean                2: filter by pattern    3: select by numbers    4: ask each             5: quit
    6: help
What now>

這樣您就可以逐個逐步處理每個檔案,或者互動式地指定刪除模式。

注意

有一個奇怪的情況,您可能需要額外強力地要求 Git 清理您的工作目錄。如果您碰巧在您複製或複製其他 Git 儲存庫(可能作為子模組)的工作目錄下,即使是 git clean -fd 也會拒絕刪除這些目錄。在這種情況下,您需要新增第二個 -f 選項來加強強調。

scroll-to-top