背景
當我們修改了程式碼準備提交時,本地的改動可能包含了不能提交的除錯語句,還可能需要拆分成多個細粒度的 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
就會自動展示下一個片段,繼續詢問我們。
這樣對片段使用 y
和 n
,我們就可以只挑選出涉及 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