Learn Git

互動式暫存

Git 提供了很多腳本來輔助某些命令列任務。這裡,你將看到一些互動式命令,它們幫助你方便地構建只包含特定組合和部分檔案的提交。在你修改了一大批檔案然後決定將這些變更分佈在幾個有聚焦的提交而不是單個又大又亂的提交時,這些工具非常有用。用這種方法,你可以確保你的提交在邏輯上劃分為相應的變更集合,以便於和你一起工作的開發者審閱。 如果你執行 git add 時加上 -i 或者 --interactive 選項,Git 就進入了一個互動式的 shell 模式,顯示一些類似於下面的資訊:

$ git add -i
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now>

你會看到這個命令以一個完全不同的視圖顯示了你的暫存區——主要是你通過 git status 得到的那些資訊但是稍微簡潔但資訊更加豐富一些。它在左側列出了你暫存的變更,在右側列出了未被暫存的變更。

在這之後是一個命令區。這裡你可以做很多事情,包括暫存檔案(stage)、撤回檔案(unstage)、暫存部分檔案、加入未被追蹤的文件、查看暫存文件的差別。

暫存和撤回檔案

如果你在 What now> 的提示後輸入 2 或者 u,這個腳本會提示你那些檔你想要暫存:

What now> 2
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Update>>

如果想暫存 TODO 和 index.html,你可以輸入相應的編號:

Update>> 1,2
           staged     unstaged path
* 1:    unchanged        +0/-1 TODO
* 2:    unchanged        +1/-1 index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Update>>

每個檔旁邊的 * 表示選中的檔將被暫存。如果你在 update>> 提示後直接敲入 Enter,Git會替你把所有選中的內容暫存:

Update>>
updated 2 paths

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now> 1
           staged     unstaged path
  1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb

現在你可以看到 TODO 和 index.html 檔被暫存了,同時 simplegit.rb 檔仍然未被暫存。如果這時你想要撤回 TODO 檔,就使用 3 或者 r(代表 revert,恢復)選項:

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now> 3
           staged     unstaged path
  1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Revert>> 1
           staged     unstaged path
* 1:        +0/-1      nothing TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb
Revert>> [enter]
reverted one path

再次查看 Git 的狀態,你會看到你已經撤回了 TODO 檔

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now> 1
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:        +1/-1      nothing index.html
  3:    unchanged        +5/-1 lib/simplegit.rb

要查看你暫存內容的差異,你可以使用 6 或者 d(表示diff)命令。它會顯示你暫存檔的列表,你可以選擇其中的幾個,顯示其被暫存的差異。這跟你在命令列下指定 git diff --cached 非常相似:

*** Commands ***
  1: status     2: update      3: revert     4: add untracked
  5: patch      6: diff        7: quit       8: help
What now> 6
           staged     unstaged path
  1:        +1/-1      nothing index.html
Review diff>> 1
diff --git a/index.html b/index.html
index 4d07108..4335f49 100644
--- a/index.html
+++ b/index.html
@@ -16,7 +16,7 @@ Date Finder

 <p id="out">...</p>

-<div id="footer">contact : support@github.com</div>
+<div id="footer">contact : email.support@github.com</div>

 <script type="text/javascript">

通過這些基本命令,你可以使用互動式增加模式更加方便地處理暫存區。

暫存補丁 (Staging Patches)

只讓 Git 將檔案的某些部分暫存,而忽略其他部份也是有可能的。例如,你對 simplegit.rb 檔作了兩處修改但是只想暫存其中一個而忽略另一個,在 Git 中實現這一點非常容易。在互動式的提示符下,輸入 5 或者 p(表示 patch,補丁)。Git 會詢問哪些檔你希望部分暫存;然後對於被選中檔案的每一節,他會逐個顯示檔案的差異區塊並詢問你是否希望暫存他們:

diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index dd5ecc4..57399e0 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -22,7 +22,7 @@ class SimpleGit
   end

   def log(treeish = 'master')
-    command("git log -n 25 #{treeish}")
+    command("git log -n 30 #{treeish}")
   end

   def blame(path)
Stage this hunk [y,n,a,d,/,j,J,g,e,?]?

此處你有很多選擇。輸入 ? 可以顯示清單:

Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

如果你想暫存各個區塊,通常你會輸入 y 或者 n,但是暫存特定檔裡的全部區塊或者暫時跳過對一個區塊的處理同樣也很有用。如果你暫存了檔案的一個部分而保留另外一個部分不被暫存,你的狀態輸出看起來會是這樣:

What now> 1
           staged     unstaged path
  1:    unchanged        +0/-1 TODO
  2:        +1/-1      nothing index.html
  3:        +1/-1        +4/-0 lib/simplegit.rb

simplegit.rb 的狀態非常有意思。它顯示有幾行被暫存了,有幾行沒有。你部分地暫存了這個檔。這時候,你可以退出互動式腳本然後執行 git commit 來提交部分暫存的檔。

最後,你也可以不通過互動式增加的模式來實現檔案部分暫存——你可以在命令列下使用 git add -p 或者 git add --patch 來啟動同樣的腳本。