使用 git add -p 整理 patch

all發表於2020-06-01

背景

當我們修改了程式碼準備提交時,本地的改動可能包含了不能提交的除錯語句,還可能需要拆分成多個細粒度的 pactch

本文將介紹如何使用 git add -p 來互動式選擇程式碼片段,輔助整理出所需的 patch

官方介紹

先貼個幫助資訊供參考

英文版本:

 -p, --patch
           Interactively choose hunks of patch between the index and the work tree and add them to
           the index. This gives the user a chance to review the difference before adding modified
           contents to the index.

           This effectively runs add --interactive, but bypasses the initial command menu and
           directly jumps to the patch subcommand. See “Interactive mode” for details.

中文版本:

-p, --patch
互動地在索引和工作樹之間選擇補丁塊並將它們新增到索引中。這讓使用者有機會在將修改後的內容新增到索引之前檢視差異。

這可以有效地執行 add --interactive,但是會繞過初始命令選單,而直接跳轉到 patch 子命令。有關詳細資訊,請參見`‘互動模式’'。

demo 視訊版

以下文字版例子對應的視訊演示

demo 文字版

我們造個例子來說明,假設我們本次完成了兩個功能,fun1 和 fun2,希望分開提交。另外在修改過程中還引入了一些除錯的列印,是不需要提交的。

程式碼的 diff 如下

 --git a/demo.c b/demo.c
index 0473c1a..76cfb22 100644
--- a/demo.c
+++ b/demo.c
@@ -1,16 +1,31 @@
 #include <stdio.h>

+void fun1()
+{
+       printf("before hello world\n");
+}
+
 void demo()
 {
        ;
 }

+void fun2()
+{
+       printf("after hello world\n");
+}
+
 int main()
 {
+       fun1();
        printf("hello world\n");
+       printf("debug %s %d\n", __func__, __LINE__);
        printf("hello world\n");
        printf("hello world\n");
        printf("hello world\n");
+       printf("debug %s %d\n", __func__, __LINE__);
        printf("hello world\n");
+       fun2();
        demo();
+       printf("debug %s %d\n", __func__, __LINE__);
 }

此時直接 git add 會把整個檔案的改動都加進來,不符合需求。

這正是 patch mode 發揮作用的地方,我們可以挑選一部分改動進行提交。

輸入 git add -p 進入 patch mode , 此時 git 會自動將改動切分成多個片段,並展示第一個片段,提示你進行選擇。

提示語句是 Stage this hunk [y,n,q,a,d,/,s,e,?]?

這些字母都是什麼意思呢? 輸入?回車,可以檢視詳細的幫助資訊。

英文版本:

y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later 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 - 不暫存此區塊
q - 退出;不暫存包括此塊在內的剩餘的區塊
a - 暫存此塊與此檔案後面所有的區塊
d - 不暫存此塊與此檔案後面所有的 區塊
g - 選擇並跳轉至一個區塊
/ - 搜尋與給定正則表達示匹配的區塊
j - 暫不決定,轉至下一個未決定的區塊
J - 暫不決定,轉至一個區塊
k - 暫不決定,轉至上一個未決定的區塊
K - 暫不決定,轉至上一個區塊
s - 將當前的區塊分割成多個較小的區塊
e - 手動編輯當前的區塊
? - 輸出幫助

對於我們的例子,git第一次自動給出的hunk很大,可以先執行 s 分割下。分割後第一個區塊就只包含增加的 fun1 函式了。

/* 太佔篇幅,此處省略原始 hunk */
Stage this hunk [y,n,q,a,d,/,s,e,?]? s /* 詢問我們對第一個片段的處理,我們覺得太大,按 s 要求分割 */
Split into 7 hunks. /* 可以看到,s 讓 git 將原始片段進一步切分成了 7 個片段,接下來就是自動展示第一個片段 */
@@ -1,7 +1,12 @@
 #include <stdio.h>

+void fun1()
+{
+       printf("before hello world\n");
+}
+
 void demo()
 {
        ;
 }

Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?  /* 詢問我們對第一個片段的處理 */

輸入 y 回車選中這個 fun1 的改動,git 就會自動展示下一個片段,繼續詢問我們。

這樣對片段使用 yn,我們就可以只挑選出涉及 fun1 的改動,當我們確認後續沒有 fun1 相關的改動時,就可以按 q 退出挑選了。

此時 git status 可以看到部分改動在暫存區中。

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

        modified:   demo.c

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:   demo.c

使用 git diff --cached 可以具體確認要提交的內容,是否符合我們的預期,只包含 fun1 的改動,不包含 fun2 和除錯語句。

$ git diff --cached
diff --git a/demo.c b/demo.c
index 0473c1a..b9fd4d4 100644
--- a/demo.c
+++ b/demo.c
@@ -1,5 +1,10 @@
 #include <stdio.h>

+void fun1()
+{
+       printf("before hello world\n");
+}
+
 void demo()
 {
        ;
@@ -7,6 +12,7 @@ void demo()

 int main()
 {
+       fun1();
        printf("hello world\n");
        printf("hello world\n");
        printf("hello world\n");

確認無誤就可以提交第一個patch, 即 fun1 的改動了。

git commit -m "fun1"

接下來繼續使用 git add -p,配合s,y,'n'就可以進一步挑選出fun2的改動了。

如果要挑選的改動比較明確,可以直接使用 /來搜尋到目標hunk,省去逐個片段判斷的麻煩。例如執行 /fun2 來搜尋包含 fun2 的程式碼片段。

git add -p 挑選完之後,建議使用 git diff --cached 確認下,或者在提交之後 git show 確認下改動,如有錯漏,及時修正,多退少補。

大部分情況使用s y n就足夠了。但如果有些改動是混合在一起的,無法使用s來分割,那就得用 e 來手工編輯了,下回分解吧。

blog: https://www.cnblogs.com/zqb-all/p/13020293.html
公眾號:https://sourl.cn/MDcrJA

相關文章