個人博客及創作索引頁正在制作中,此處僅釋出本地第一大版本,原檔案基于 Hexo 及相關插件,不兼容于此處的格式暫不統一修復,
《【翻譯】如何撰寫 Git 提交訊息》[1]的簡體中文翻譯版本對應原文為 How to Write a Git Commit Message ,原作者為 Chris Beams ,請注意:
- 正文格式盡可能與原網頁保持一致;
- 譯者注將以腳注 (footnote) 的形式呈現,且其內容應以”譯者注:“起始;
- 原文中若有文內跳轉超鏈接,如無必要,將直接去除,且不在譯文中作進一步說明;
- 原文中若有翻譯后難以傳達作者寫作意圖的詞句 (通常是命令,或由源語言的特殊性質等原因導致) ,此時將不再翻譯,且不在譯文中作進一步說明,
翻譯文本將從隨后的分隔線開始展示,
如何撰寫 Git 提交資訊
原作者: Chris Beams
原文創作日期: 2014 年 8 月 31 日
提交訊息很重要,這里將展示如何寫好它們,

引子:為什么好的提交訊息很重要
如果你曾隨意地瀏覽過一些 Git 倉庫,你很可能會發現它們的提交訊息或多或少有些雜亂,例如,你可以看看我早期提交給 Spring 的一些 Gem[2] 的日志:
$ git log --oneline -5 --author cbeams --before "Fri Mar 26 2009"
e5f4b49 Re-adding ConfigurationPostProcessorTests after its brief removal in r814. @Ignore-ing the testCglibClassesAreLoadedJustInTimeForEnhancement() method as it turns out this was one of the culprits in the recent build breakage. The classloader hacking causes subtle downstream effects, breaking unrelated tests. The test method is still useful, but should only be run on a manual basis to ensure CGLIB is not prematurely classloaded, and should not be run as part of the automated build.
2db0f12 fixed two build-breaking issues: + reverted ClassMetadataReadingVisitor to revision 794 + eliminated ConfigurationPostProcessorTests until further investigation determines why it causes downstream tests to fail (such as the seemingly unrelated ClassPathXmlApplicationContextTests)
147709f Tweaks to package-info.java files
22b25e0 Consolidated Util and MutableAnnotationUtils classes into existing AsmUtils
7f96f57 polishing
呀!再比較一下同一個倉庫近期的一些提交的日志:
$ git log --oneline -5 --author pwebb --before "Sat Aug 30 2014"
5ba3db6 Fix failing CompositePropertySourceTests
84564a0 Rework @PropertySource early parsing logic
e142fd1 Add tests for ImportSelector meta-data
887815f Update docbook dependency and generate epub
ac8326d Polish mockito usage
你更想去閱讀哪一種呢?
前者的提交訊息在長度和形式上各不相同,而后者更加精準和一致;前者是自然而然的結果,而后者絕不會是碰巧寫成的,
當許多提交日志類似于前者的倉庫隨處可見時,也有一些例外存在, Linux 內核和 Git 自身的原始碼正是良好的范例,或是看看 Spring Boot 或由 Tim Pope 管理的倉庫,
這些倉庫的貢獻者們知道,一個經過精心打磨的 Git 提交訊息是將一個更改與其他開發者 (以及未來的自己) 交流其來龍去脈的最好方式[3],一個 diff 的輸出結果將告訴你什么改變了,而只有提交訊息能恰當地告訴你為什么改變了, Peter Hutterer 將這個觀點表達得很好:
重新確定一段代碼的背景關系是浪費的,我們不能完全避免它,因此我們應當努力去[竭盡]所能減少這種情況的發生,提交訊息能準確地做到這一點,所以一條提交訊息能夠展示出一位開發者是不是好的協作者,[4]
如果你還沒有很多思路來撰寫良好的 Git 提交訊息,這或許說明你沒有花費很多時間使用 git log 命令和相關的工具,這里有一個殘酷的回圈:因為提交歷史是缺少結構和一致性的,某個人不會花費很多時間使用或關心它,并且因為它不被使用或關心,它將始終缺少結構和一致性,
然而,被維護得很好的日志是一種優美和實用的東西,這會讓 git blame 、revert 、rebase 、log 、shortlog 和其他子命令充滿活力,會給回顧其他人的提交和 pull requests 帶來一些價值——并且它們突然能被獨立地完成,理解幾個月或幾年前一些事情為什么發生將不但變得可能,還將變得高效,
一個專案能否取得長遠的成功 (相較于其他因素) 不但取決于其可維護性如何,還在于一個維護者是否沒有多少比日志更加有力的工具了,我們值得花費一些時間來學習如何適切地維護專案的日志,起初維護日志時可能的困境不久就會轉變為習慣,并最終成為所有參與者自豪感和生產力的源泉,
在這篇文章中,我將會告訴你保持一份健康的提交歷史的最基礎的要素:如何撰寫一條獨立的提交訊息,還有其他我在這里不會提及的重要習慣,比如說統整提交[5],或許我會在后續的投稿中談到它們,
大多數編程語言都有著已經成形的慣例,它們構建了符合語言習慣的風格,就像命名、格式等等[6],當然,這些慣例有諸多變種,但大部分開發者都認同專注于其中一種的情形遠優于每個人各自選用一種造成的混亂局面,
一個團隊對待提交日志的方式應當沒有絲毫不同,為了創建一份實用的修訂歷史,團隊應該首先在至少符合以下三點的提交訊息慣例上達成共識:
風格,標記語言的句法[7],折行的間隔,語法[8],大小寫,標點符號,將這些都明確給出,去除猜測,并且讓一切都盡可能簡單,最終的結果將會是一份格外一致的日志——不僅閱讀起來很愉悅,而且實際上的確能被定期閱讀[9],
內容,提交訊息的主體 (如果有的話) 應該包含什么資訊?什么是不能包含的?
元資料,諸如 issue 的追蹤編號[10]、 pull request 的編號應當如何被提及?
萬幸的是,已經有一些成形的慣例來創建一條符合語言習慣的 Git 提交訊息,的確,這些都假定在特定的 Git 命令運作的方式下,這里沒有你需要重新發明的地方,只要遵循下方的七條規則,你就推開了像專家一樣提交的大門,
撰寫好的 Git 提交訊息的七條規則
請謹記:這些都已被提出過了,
- 用一個空行分隔標題和主體;
- 標題控制在 50 個字符以內;
- 標題的首字母大寫;
- 標題的末尾不要寫句號;
- 標題使用祈使語氣;
- 主體每 72 個字符折行;
- 用主體解釋做了什么和為什么,而不是如何做到,
例如:
Summarize changes in around 50 characters or less
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.
Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
If you use an issue tracker, put references to them at the bottom,
like this:
Resolves: #123
See also: #456, #789
1. 用一個空行分隔標題和主體
根據 git commit 命令的幫助頁面:
盡管不是必須的,一個好主意是,提交訊息以一行簡短的 (少于 50 個字符) 概括這個更改的文字為開始,緊接著是一個空行,隨后是一段更詳細的描述,整段文本的首行將被認作為提交的標題,那個標題將貫穿整個 Git[11] ,例如, Git-format-patch(1) 將提交轉變為郵件,它將提交的標題作為郵件的主題,將剩余的提交內容作為郵件的主體,
首先,不是每一次提交都需要標題和主體,有時一行也很好,尤其是當更改很簡單以至于進一步的闡釋都不必要的時候,例如:
Fix typo in introduction to user guide
沒有什么是需要解釋的了,如果讀者想知道這個拼寫錯誤是什么,直接看這個更改本身即可,換句話說使用 git show 、 git diff 或 git log -p ,
如果你想用命令提交,給 git commit 命令添加 -m 選項是很容易的:
$ git commit -m"Fix typo in introduction to user guide"
然而,當一個提交需要一點解釋和背景關系時,你需要撰寫它的主體,例如:
Derezz the master control program
MCP turned out to be evil and had become intent on world domination.
This commit throws Tron's disc into MCP (causing its deresolution)
and turns it back into a chess game.
用 -m 選項來寫提交訊息的主體不是很容易,你最好在一個合適的文本編輯器中撰寫它,如果你還沒有一個編輯器,使用 Git 命令列來設定,參閱 Pro Git 的這一節,
在任何情形下,標題和主體之間的間隔都能在瀏覽日志時得到回報,這里是完整的日志:
$ git log
commit 42e769bdf4894310333942ffc5a15151222a87be
Author: Kevin Flynn <[email protected]>
Date: Fri Jan 01 00:00:00 1982 -0200
Derezz the master control program
MCP turned out to be evil and had become intent on world domination.
This commit throws Tron's disc into MCP (causing its deresolution)
and turns it back into a chess game.
現在執行 git log --oneline ,這會僅輸出標題行:
$ git log --oneline
42e769 Derezz the master control program
或者是執行 git shortlog ,這會按照用戶給提交分組,為了簡潔,同樣會僅輸出標題行:
$ git shortlog
Kevin Flynn (1):
Derezz the master control program
Alan Bradley (1):
Introduce security program "Tron"
Ed Dillinger (3):
Rename chess program to "MCP"
Modify chess program
Upgrade chess program
Walter Gibbs (1):
Introduce protoype chess program
也有一些 Git 中的其他背景關系,它們的標題行和主體的區別被打破了——但它們都無法在二者之間不空行時做得合適[12],
2. 標題控制在 50 個字符以內
50 個字符不是硬性限制,只是一個經驗法則,將標題行控制在這個長度能保證它們是可讀的,并且能強迫作者花一些時間思考如何最簡潔地解釋將要發生什么,
提示:如果你難以概括你提交的內容,或許是因為你一次提交了太多的更改,努力做到原子級提交[13] (另一篇投稿的話題) 吧,
GitHub 的用戶界面全面地意識到了這些慣例,例如,它會在你的標題行超過 50 個字符時警告你:

并且會截斷超過 72 個字符的標題行,后續用省略號代替:

因此,爭取限制在 50 個字符以內,并把 72 個字符當作是硬性限制,
3. 標題的首字母大寫
這就跟聽起來一樣簡單,所有的標題行的首字母都需要大寫,
例如,用:
- Accelerate to 88 miles per hour
來替代:
accelerate to 88 miles per hour
4. 標題的末尾不要寫句號
句末標點在標題中時無關緊要的,另外,當你想要控制 50 個字符時,空格是很珍貴的,
例如,用:
- Open the pod bay doors
來替代:
Open the pod bay doors.
5. 標題使用祈使語氣
祈使語氣意思就是“說出或寫出類似命令或指示的東西”,以下是一些例子:
- 清理你的房間
- 關上這扇門
- 拿走垃圾
你正在閱讀的七條規則的每一條都是用祈使語氣寫成的 (“主體每 72 個字符折行”以及其他的) ,
祈使語氣聽起來有一些失禮,這就是為什么我們不常使用,但是這對于 Git 提交的標題來說很完美,其中一個原因是 Git 自身以你的名義創建提交時都是使用祈使語氣,
例如,使用 git merge 后的默認提交訊息即是:
Merge branch 'myfeature'
還有使用 git revert 后:
Revert "Add the thing with the stuff"
This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.
或者是在 GitHub 的Pull Request 界面點擊 “Merge” 按鈕時:
Merge pull request #123 from someuser/somebranch
因此,當你用祈使語氣撰寫提交訊息時,你就是在遵守 Git 內置的慣例,例如:
- Refactor subsystem X for readability
- Update getting started documentation
- Remove deprecated methods
- Release version 1.0.0
起初這樣寫可能顯得有些蠢,我們更常用陳述語氣說話,這種語氣都是用于描述事實,這就是為什么提交訊息很多都像這樣結束了:
Fixed bug with YChanging behavior of X
并且有時提交訊息寫起來就像是它們的內容的描述:
More fixes for broken stuffSweet new API methods
為了去除任何困惑,這里是一條簡單的規則來讓你每次都能做對:
一個合適的 Git 提交的標題應當總是能完成如下的句子:
- 如果被應用了,這個提交將會這是你的標題
例如:
- If applied, this commit will refactor subsystem X for readability
- If applied, this commit will update getting started documentation
- If applied, this commit will remove deprecated methods
- If applied, this commit will release version 1.0.0
- If applied, this commit will merge pull request #123 from user/branch
注意到對于非祈使語氣,這是不能成功的:
- If applied, this commit will
fixed bug with Y - If applied, this commit will
changing behavior of X - If applied, this commit will
more fixes for broken stuff - If applied, this commit will
sweet new API methods
記住:祈使語氣的使用僅對標題很重要,在寫主體時,你可以放松這條規定,
6. 主體每 72 個字符折行
Git 從來不會自動折行,當你撰寫一個提交的主體時,你需要注意到它的右邊界,并手動折行,
折行的推薦值是 72 個字符,因此 Git 在大體上將對 80 個字符以內的內容保持原樣,有足夠的空間用于縮進排版,
一個好的文本編輯器能幫到你, Vim 的配置很容易,例如在撰寫 Git 提交訊息時在 72 個字符處折行,然而,在傳統意義上, IDE 對于智能地支持提交訊息的折行很糟糕 (盡管 IntelliJ IDEA 在最近的版本中終于做得比以前好了) ,
7. 用主體解釋做了什么和為什么,而不是如何做到
這里的一條來自 Bitcoin Core 的提交 是解釋更改了什么和為什么的最佳范例:
commit eb0b56b19017ab5c16c745e6da39c53126924ed6
Author: Pieter Wuille <[email protected]>
Date: Fri Aug 1 22:57:55 2014 +0200
Simplify serialize.h's exception handling
Remove the 'state' and 'exceptmask' from serialize.h's stream
implementations, as well as related methods.
As exceptmask always included 'failbit', and setstate was always
called with bits = failbit, all it did was immediately raise an
exception. Get rid of those variables, and replace the setstate
with direct exception throwing (which also removes some dead
code).
As a result, good() is never reached after a failure (there are
only 2 calls, one of which is in tests), and can just be replaced
by !eof().
fail(), clear(n) and exceptions() are just never called. Delete
them.
看一看它的完整的 diff 資訊,思考這位作者在此時此地花費時間提供代碼的背景關系節約了其他和未來的提交者多少時間,如果他沒有做到,這次提交將很可能會永遠被遺忘,
在大多數情況下,你可以省去這個更改如何被做出的細節,在這點上,代碼通常能自我解釋 (如果這段代碼如此復雜以至于它需要用枯燥的語言來解釋,那就是源代碼注釋應該做的事情了) ,只是專注于首先明確更改的原因——你在更改之前所做的事 (以及出現了什么錯誤) ,它們現在如何作業的,以及為什么你決定用你的方式解決這個問題[14],
未來感謝你的維護者可能就是你自己!
提示
學著熱愛命令列,把 IDE 拋在腦后,
處于諸多原因,比如說 Git 子命令的存在,擁抱命令列是一種明智之舉, Git 有著令人瘋狂的力量, IDE 亦然,但這兩者體現在不同的方面上,我每天都使用一款 IDE (IntelliJ IDEA) ,也廣泛地使用其他的 (Eclipse) ,但我還從沒有見過哪款 IDE 的 Git 集成能比得上簡單有力的命令列 (一旦你意識到了這點) ,
某些 Git 相關的 IDE 功能是無價的[^15],就像當你洗掉檔案時執行 git rm ,在你重命名檔案的時候用 git 執行正確的命令,而當你開始試著用 IDE 提交、合并、 rebase 或分析高深莫測的歷史記錄時,一切都會崩潰,
當支配 Git 的全部法力之日到來之時,一切都只剩下命令列,
記住不論你是使用 Bash 、 zsh 還是 PowerShell ,都有 Tab 自動補全腳本來減輕不少記憶子命令和開關的痛苦,
閱讀 Pro Git
Pro Git 在網上可以免費閱讀,并且它很美妙,好好利用它吧!
題圖提供者: xkcd
【完】
腳注
譯者注:”提交訊息“原文為 ”commit message“ ,下同, ??
譯者注: Spring 庫的插件名為 “Gem” , ??
譯者注:原文為 ”The contributors to these repositories know that a well-crafted Git commit message is the best way to communicate context about a change to fellow developers (and indeed to their future selves)“ , ??
譯者注:原文為 ”Re-establishing the context of a piece of code is wasteful. We can’t avoid it completely, so our efforts should go to reducing it [as much] as possible. Commit messages can do exactly that and as a result, a commit message shows whether a developer is a good collaborator“ , ??
譯者注:原文為 “Most programming languages have well-established conventions as to what constitutes idiomatic style, i.e. naming, formatting and so on” , ??
譯者注:原文為 “syntax” , ??
譯者注:原文為 “grammar” , ??
譯者注:原文為 “The end result will be a remarkably consistent log that’s not only a pleasure to read but that actually does get read on a regular basis” , ??
譯者注:原文為 “issue tracking IDs” , ??
譯者注:原文為 “ The text up to the first blank line in a commit message is treated as the commit title, and that title is used throughout Git” , ??
譯者注:原文為 “There are a number of other contexts in Git where the distinction between subject line and body kicks in—but none of them work properly without the blank line in between” , ??
譯者注:原文為 “atomic commits” , ??
譯者注:原文為 “Just focus on making clear the reasons why you made the change in the first place—the way things worked before the change (and what was wrong with that), the way they work now, and why you decided to solve it the way you did” , ??
譯者注:原文為 “Certain Git-related IDE functions are invaluable” , ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/295541.html
標籤:其他
