章節 ▾ 第二版

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 stash` 或 `git 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 會給出合併衝突。

對檔案的更改已重新應用,但你之前暫存的檔案沒有重新暫存。要做到這一點,你必須執行帶 `--index` 選項的 `git stash apply` 命令,以告訴命令嘗試重新應用已暫存的更改。如果你運行了那個選項,你就會回到原來的位置

$ 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` 檔案以便進行一次完全乾淨的構建,你可以為 `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` 更改為 `-f` 實際執行之前,先用 `-n` 執行它進行雙重檢查。另一種謹慎操作的方法是使用 `-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