日本熟妇hd丰满老熟妇,中文字幕一区二区三区在线不卡 ,亚洲成片在线观看,免费女同在线一区二区

如何寫好提交,做一個有品位的開發者

本文由《Git 權威指南》的作者蔣鑫老師撰寫(全文以第一人稱分享),介紹了代碼提交的最佳實踐建議。

—— 問:“能夠寫出正確代碼的程序員就是有品味的程序員么?”

—— 答:“還不夠。”

品味來自于每一個細節,有品位的程序員會把每一次提交做小、做對、做好,讓人看懂,且無可挑剔,這樣才夠專業,才可以稱為有品位。

熟練使用 Git,會讓您更有品味。

1. 提交做小

小提交就是將問題解耦:“Do one thing and do it well”。開源項目的提交通常都很小,每個提交只修改一個到幾個文件,每次只修改幾行到幾十行。

找一個您熟悉的開源項目,在倉庫中執行下面的命令,可以很容易地統計出來每個提交的修改量。

$ git log --no-merges --pretty= --shortstat
2 files changed, 25 insertions(+), 4 deletions(-)
1 file changed, 4 insertions(+), 12 deletions(-)
2 files changed, 30 insertions(+), 1 deletion(-)
3 files changed, 15 insertions(+), 5 deletions(-)

而很多公司內的項目則不是這樣,一個提交動輒修改成百上千的文件,涉及成千上萬行的源代碼。試問這樣的提交能Show出來給人看么?

可是在開發過程中,程序員一旦進入狀態,往往才思如泉涌,不經意間就寫出一個大提交。比如我又一次向Git貢獻代碼時,提交還不算太大,就被Git的維護者Junio吐槽,要我拆分提交,便于評審:

“I think this patch should be in at least two parts:

  • Introduce the two-phase "collect in del_list, remove in a separate loop at the end" restructuring.

  • (optional, if you are feeling ambitious) Change the path that is stored in del_list relative to the prefix, so that all functions that operate on the string in the del_list do not have to do *_relative() thing. Some functions may instead have to prepend prefix but if they are minority compared to the users of *_relative(), it may be an overall win from the readability's point of view.

  • Add the "interactively allow you to reduce the del_list" bit between the two phases.”

那么如何將提交拆分為若干個小提交呢?

1.1 拆分當前提交(松耦合)

先以拆分最新的提交為例,可以如下操作:

  1. 將當前提交撤銷,重置到上一次提交。撤銷提交的改動保留在工作區中。

    $ git reset HEAD^
  2. 通過補丁塊揀選方式選擇要提交的修改。Git會逐一顯示工作區更改,如果確認此處改動要提交,輸入“y“。

    $ git add -p

  3. 以撤銷提交的提交說明為藍本,撰寫新的提交。

    $ git commit -e -C HEAD@{1}
  4. 對于工作區剩余的修改進行提交。這樣就完成一個提交拆分為兩個、或者多個的操作。

    $ git add -u
    $ git commit

1.2 拆分當前提交(緊耦合)

如果要拆分的提交,不同的實現邏輯耦合在一起,難以通過補丁塊揀選(git add -p)的方式修改提交,怎么辦?這時可以直接編輯文件,刪除要剝離出此次提交的修改,然后執行:

$ git commit --amend

然后執行下面的命令,還原原有的文件修改,然后再提交。如下:

$ git checkout HEAD@{1} -- .
$ git commit

同樣完成了緊耦合時的一個提交拆分為多個提交的操作。

1.3 拆分歷史某個提交

如果要拆分的是歷史提交(如提交 54321),而非當前提交,則可以執行交互式變基(git rebase -i),如下:

$ git rebase -i 54321

Git會自動將參與變基的提交寫在一個動作文件中,還會自動打開編輯器(比如 vi 編輯器)。

在編輯器中顯示內容示例如下:

pick 54321 要拆分的提交
pick ...   其他參與變基的提交

將要拆分的提交54321前面的關鍵字 pick修改為 edit,保存并退出。變基操作隨即開始執行。

首先會在提交54321處停下來,這時要拆分的提交成為了當前提交,參照前面“拆分當前提交”的方法對提交54321進行拆分。拆分結束再執行 git rebase --continue完成整個變基操作。

2. 提交做對

好的文章不是寫出來的,而是改出來的, 代碼提交也是如此。

2.1 git commit --amend

程序員寫完代碼,往往迫不及待地敲下:git commit,然后發現提交中少了一個文件,或者提交了多余的文件,或者發現提交中包含錯誤無法編譯,或者提交說明中出現了錯別字。

Git 提供了一個修改當前提交的快捷命令:git commit --amend,相信很多人都用過,不再贅述。

2.2 git commit --fixup 和 git rebase -i

如果您發現錯誤出現在上一個提交或其他歷史提交中怎么辦呢?我有一個小竅門,在《Git權威指南》里我沒有寫到哦。

比如發現歷史提交 a1234567中包含錯誤,直接在當前工作區中針對這個錯誤進行修改,然后執行下面命令。

$ git commit --fixup a1234567

您會發現使用了 --fixup參數的提交命令,不再詢問您提交說明怎么寫,而是直接把錯誤提交 a1234567的提交說明的第一行拿來,在前面增加一個前綴“fixup!”,如下:

fixup! 原提交說明

如果一次沒有改對,還可以再接著改,甚至您還可以針對這個修正提交進行fixup,產生如下格式的提交說明:

fixup! fixup! 原提交說明

當開發工作完成后,待推送/評審的提交中出現大量的包含“fixup!”前綴的提交該如何處理呢?

如果您執行過一次下面的命令,即針對錯誤提交 a1234567及其后面所有提交執行交互式變基(注意其中的 --autosquash參數),您就會驚嘆Git設計的是這么巧妙:

$ git rebase -i --autosquash a1234567^

交互式變基彈出的編輯器內自動對提交進行排序,將提交 a1234567 連同它的所有修正提交壓縮為一個提交。

說明

執行 git config --global rebase.autoSquash true命令設置配置變量 rebase.autosquash,執行 git rebase -i命令會自動帶上 --autosquash參數。

2.3 TAP(test anything protocol)和 Git 測試框架

對于“提交做對”,很多開源項目還通過單元測試、集成測試用例提供保障。對于這樣的項目,在提交代碼時往往要求提供相應的測試用例。

Git項目本身就對測試用例有著很高的要求,其測試框架就是由Git的maintainer Junio寫的,基于Perl的 TAP(test anything protocol)寫的一個測試框架。用起來非常順手。

示例:

我曾經針對Git的單元測試框架寫過博客,參見復用 git.git 測試框架

2.4 sharness

Git的測試框架代碼經過重構,已經成為一個單獨的項目:Sharness,更加方便重用了。

3. 提交做好

僅僅做到提交做小、提交做對,往往還不夠,還要通過撰寫詳細的提交說明讓評審者信服,這樣才能夠讓提交盡快通過評審合入項目倉庫中。

3.1 提交說明的結構

幾年前,發現Git服務器上的一個異常,最終將問題定位到Git工具本身,整個代碼改動只有區區一行,提交為例:

receive-pack: crash when checking with non-exist HEAD

您能猜到提交說明寫了多少么?寫了20多行!

01    receive-pack: crash when checking with non-exist HEAD
02    
03    If HEAD of a repository points to a conflict reference, such as:
04    
05    * There exist a reference named 'refs/heads/jx/feature1', but HEAD
06      points to 'refs/heads/jx', or
07  
08    * There exist a reference named 'refs/heads/feature', but HEAD points
09      to 'refs/heads/feature/bad'.
10
11    When we push to delete a reference for this repo, such as:
12    
13            git push /path/to/bad-head-repo.git :some/good/reference
14  
15    The git-receive-pack process will crash.
16  
17    This is because if HEAD points to a conflict reference, the function
18    `resolve_refdup("HEAD", ...)` does not return a valid reference name,
19    but a null buffer.  Later matching the delete reference against the null
20    buffer will cause git-receive-pack crash.
21  
22    Signed-off-by: Jiang Xin <worldhello***.net@gmail.com>
23    Signed-off-by: Junio C Hamano <gitster***@pobox.com>

這一端提交分成幾個部分: 1. 第 01 行是提交標題。英文、可包含前綴、長度小于50(建議)、結尾不要有句號。

2. 第 02 行要有一個空行,分隔提交標題和提交主體。 3. 第 03 到 20 行是提交的 Body,說明為什么要做這個提交。 4. 第 03 到 15 行說的是要解決的問題,什么情況下會引發這個Bug。 5. 第 17 到 20 行是原因分析和解決方案。 6. 第 22 行開始是簽名區。有作者的簽名(我的簽名),和maintainer將我的郵件補丁合入Git代碼倉時添加簽名(Junio),說明這個提交是經過Junio認可的提交。

下面再詳細介紹一下Git對于提交說明的這些約定俗成的規定。

3.2 提交標題(Subject,即提交說明的第一行)

  • 提交說明第一行是提交標題,是整個提交的概要性描述。

  • 提交標題的長度要求:盡量不要超過 50 個字符。 這是因為對于像Linux、Git這樣的開源項目,是以郵件列表作為代碼評審的平臺,提交標題要作為郵件的標題,而郵件標題本身有長度上的限制。

  • 提交標題使用英文,不要出現中文。這是因為一些Git工具如 git format-patch講提交轉換補丁,或以郵件方式交換提交補丁的時候,會丟失中文(中文轉換為空白字符)。

  • 建議在提交標題中添加前綴,對改動范圍進行區分(例如用模塊名做前綴:receive-pack)。

  • 不要在提交標題后面添加標點符號(如句號),一個原因是提交標題的長度要求,不要浪費;另外一個是提交標題在郵件發送補丁時,作為郵件的標題,您見過郵件標題要添加結尾句號的么?

3.3 提交標題后的空行

必須要在提交說明的第一行(subject)和后續的提交說明(body)中間留一個空行。如果沒有這個空行,很多Git客戶端會將連續幾行的提交說明合在一起作為提交的簡要描述(git log --oneline),這樣顯然太糟了。

3.4 提交說明主體(body)

  • 提交標題之外的提交說明也有長度的限制,最好以72字節為限,超過則斷行。

  • 提交說明主體中要寫什么內容呢? 例如:本次提交要解決什么問題?如何解決的?為什么這么做是最合理的。

  • 因為GitHub等代碼托管平臺支持Markdown語法,所以作為一個有品位的程序員學些Markdown的語法,讓您的提交說明的可讀性變得更強吧。關于 Markdown和其他文本標記語言的語法說明,我寫過一個速記卡片輕量級標記語言語法參考,可供參考。

3.5 簽名區

  • git commit命令有一個 -s參數,自動在提交說明最后添加 "sob" 簽名(不是您想的那個縮寫)。

  • 為什么要在提交后面添加簽名呢?因為一個提交的元信息中只有作者(author)、提交者(committer)兩個字段,而一段代碼的誕生,參與的人往往不止于此,還可能有問題報告者(Reported-by)、代碼評審者(Reviewed-by)、上游Committer 的簽名(Signed-off-by)。為此一些開源項目(如 Git、Linux)的一個約定俗成的習慣,是在提交的最后加上簽名,每個貢獻者一行,從上到下可以看到這段代碼誕生的過程。

  • 對幫助過您的人致謝吧,加上您的代碼簽名。

3.6 示例

最新在Git社區寫了幾個提交:

  1. pack-redundant: new algorithm to find min packs,這個提交的說明寫得足夠詳細,幾乎不會有人來challenge。如果不是Junio感冒加搬家,可能會更早合入社區。

  2. pack-redundant: consistent sort method,這個提交的簽名區,就出現了 Reported-by的簽名,是我手動敲進去的,對貢獻者致謝。

正確的代碼評審方式

代碼評審要關注過程,要由遠及近地看每一個提交,不能只看前后兩個版本之間的差異。

有人認為這樣的代碼評審多此一舉,認為這樣可能是浪費時間。有的時候,給一個提交不規范的開發者做代碼評審,的確頭疼又浪費時間:看到一個提交中的代碼問題,花了幾分鐘寫評論,然后發現下一個提交中這個問題被修正(fixup)了。

如果評審的代碼來自提交規范的開發者,逐提交評審可能是一件賞心悅目的事情:

  1. 一些重構操作(修改方法名、變量名;代碼塊在文件之間移動;文件改名),單獨作為一個提交,評審起來工作量很小,對后續提交評審的干擾也小。

  2. 一個提交干一件事,由遠及近的評審的過程,能夠看到開發者工作的邏輯性和思路。

  3. 因為有 git rebase --interactive等神器,不會出現后一個提交修改前一個提交中錯誤的實現,讓每個提交把事情一次做對。

  4. 因為每個提交能夠把事情一次做對,在代碼調試過程中 git bisect神器就可以派上用場。

  5. 提交cherry-pick或者rebase到新的基線(如定制開發型項目中,遷移到上游新的版本),工作量小。

最后,讓我們一起學習成為一名有品位的程序員,依靠您對代碼的品味和高質量嚴要求,持續守護您的項目質量!