項目開發時,一個好的 Commit Message 至關重要:
- 可以使自己或者其他開發人員能夠清晰地知道每個 commit 的變更內容,方便快速瀏覽變更歷史,比如可以直接略過文檔類型或者格式化類型的代碼變更。
- 可以基於這些 Commit Message 進行過濾查找,比如只查找某個版本新增的功能:
git log --oneline --grep "^feat|^fix|^perf"
。 - 可以基於規范化的 Commit Message 生成 Change Log。
- 可以依據某些類型的 Commit Message 觸發構建或者發布流程,比如當 type 類型為 feat、fix 時我們才觸發 CI 流程。
- 確定語義化版本的版本號。比如
fix
類型可以映射為 PATCH 版本,feat
類型可以映射為 MINOR 版本。帶有BREAKING CHANGE
的 commit,可以映射為 MAJOR 版本。
Commit Message 的規范
有以下幾種:
在這些規范中,Angular 規范在功能上能夠滿足開發者 commit 需求,在格式上清晰易讀,目前也是用得最多的。
Angular 規范其實是一種語義化的提交規范(Semantic Commit Messages),所謂語義化的提交規范包含以下內容:
- Commit Message 是語義化的:Commit Message 都會被歸為一個有意義的類型,用來說明本次 commit 的類型。
- Commit Message 是規范化的:Commit Message 遵循預先定義好的規范,比如 Commit Message 格式固定、都屬於某個類型,這些規范不僅可被開發者識別也可以被工具識別。
一個遵循 Angular 規范的 commit 歷史記錄:
一個完整的符合 Angular 規范的 Commit Message:
Angular 規范
在 Angular 規范中,Commit Message 包含三個部分,分別是 Header、Body 和 Footer,格式如下:
<type>[optional scope]: <description>
// 空行
[optional body]
// 空行
[optional footer(s)]
其中,Header是必需的,Body和Footer可以省略。在以上規范中,必須用括號 ()
括起來, <type>[<scope>]
后必須緊跟冒號 ,冒號后必須緊跟空格,2 個空行也是必需的。
在實際開發中,為了使 Commit Message 在 GitHub 或者其他 Git 工具上更加易讀,往往會限制每行 message 的長度。根據需要,可以限制為 50/72/100 個字符。
以下是一個符合 Angular 規范的 Commit Message:
fix($compile): couple of unit tests for IE9
# Please enter the Commit Message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# ...
Older IEs serialize html uppercased, but IE9 does not...
Would be better to expect case insensitive, unfortunately jasmine does
not allow to user regexps for throw expectations.
Closes #392
Breaks foo.bar api, foo.baz should be used instead
Angular 規范中 Commit Message 的三個部分:
Header
Header 部分只有一行,包括三個字段:type(必選)、scope(可選)和 subject(必選)。
type
分為兩個類別:
- Development:這類修改一般是項目管理類的變更,不會影響最終用戶和生產環境的代碼,比如 CI 流程、構建方式等的修改。遇到這類修改,通常也意味着可以免測發布。
- Production:這類修改會影響最終的用戶和生產環境的代碼。所以對於這種改動,一定要慎重,並在提交前做好充分的測試。
常見 type 和它們所屬的類別:
如何確定一個 commit 所屬的 type:
scope
用來說明 commit 的影響范圍。顯然,不同項目會有不同的 scope。在項目初期,可以設置一些粒度比較大的 scope,比如可以按組件名或者功能來設置 scope;后續,如果項目有變動或者有新功能,可以再用追加的方式添加新的 scope。
scope 不適合設置太具體的值。太具體的話,一方面會導致項目有太多的 scope,難以維護。另一方面,開發者也難以確定 commit 屬於哪個具體的 scope,導致錯放 scope,反而會使 scope 失去了分類的意義。
subject
subject 是 commit 的簡短描述,必須以動詞開頭、使用現在時。比如,可以用 change,卻不能用 changed 或 changes,而且這個動詞的第一個字母必須是小寫。通過這個動詞,可以明確地知道 commit 所執行的操作。此外還要注意,subject 的結尾不能加英文句號。
Body
Body 部分可以分成多行,而且格式也比較自由。不過,和 Header 里的一樣,它也要以動詞開頭,使用現在時。此外,它還必須要包括修改的動機,以及和跟上一版本相比的改動點。
Footer
Footer 部分不是必選的,可以根據需要來選擇,主要用來說明本次 commit 導致的后果。在實際應用中,Footer 通常用來說明不兼容的改動和關閉的 Issue 列表,格式如下:
BREAKING CHANGE: <breaking change summary>
// 空行
<breaking change description + migration instructions>
// 空行
// 空行
Fixes #<issue number>
-
不兼容的改動:如果當前代碼跟上一個版本不兼容,需要在 Footer 部分,以
BREAKING CHANG:
開頭,后面跟上不兼容改動的摘要。Footer 的其他部分需要說明變動的描述、變動的理由和遷移方法BREAKING CHANGE: isolate scope bindings definition has changed and the inject option for the directive controller injection was removed. To migrate the code follow the example below: Before: scope: { myAttr: 'attribute', } After: scope: { myAttr: '@', } The removed `inject` wasn't generaly useful for directives so there should be no code using it.
-
關閉的 Issue 列表:關閉的 Bug 需要在 Footer 部分新建一行,並以 Closes 開頭列出,例如:
Closes #123
。如果關閉了多個 Issue,可以這樣列出:Closes #123, #432, #886
。例如:Change pause version value to a constant for image Closes #1137
Revert Commit
如果當前 commit 還原了先前的 commit,則應以 revert:
開頭,后跟還原的 commit 的 Header。而且,在 Body 中必須寫成 This reverts commit <hash>
,其中 hash 是要還原的 commit 的 SHA 標識。例如:
revert: feat(iam-apiserver): add 'Host' option
This reverts commit 079360c7cfc830ea8a6e13f4c8b8114febc9b48a.
總結
為了更好地遵循 Angular 規范,建議在提交代碼時養成不用 git commit -m
,即不用-m 選項的習慣,而是直接用 git commit
或者 git commit -a
進入交互界面編輯 Commit Message。這樣可以更好地格式化 Commit Message。