章節 ▾ 第二版

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 <new branchname>,它會為你建立一個具有你選擇的分支名稱的新分支,檢出你暫存工作時所在提交,在那裡重新應用你的工作,然後在成功應用後丟棄暫存。

$ 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 檔案以進行完全乾淨的構建,你可以在 clean 命令中新增 -x

$ 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 選項來強調。