一個維護版本日誌整潔的Git提交規範

arthinking_itzhai發表於2018-11-23

1 關於提交日誌規範

良好的Commit Message有利於程式碼審查,能更快速查詢變更記錄,並且可以直接生成Change log。

Commit Message的寫法規範:conventional-changelog 為了規範程式碼提交的說明,這裡我們使用angular的規範寫法:

<type>(<scope>): <subject>
<空行>
<body>
<空行>
<footer>
複製程式碼

其中 head((): )是必須的,body和footer是可選的。 如果只需要輸入header,可以直接使用:

git commit -m
複製程式碼

命令提交。

如果需要輸入body、footer這樣的多行日誌,需要輸入:

git commit
複製程式碼

跳出文字編輯器進行編寫。

1.1 Header

1.1.1 Type

commit的類別,包涵以下七種:

feat:新功能(feature)
fix:修補bug
docs:文件(documentation)
style: 格式(不影響程式碼執行的變動)
refactor:重構(即不是新增功能,也不是修改bug的程式碼變動)
test:增加測試
chore:構建過程或輔助工具的變動
複製程式碼

1.1.2 Scope

commit的影響範圍,比如會影響到哪個模組/效能/哪一層(業務層,持久層,快取,rpc),如果是特性程式碼,可以寫特性名稱

1.1.3 Subject

commit的簡短描述,不超過50個字元。

  • 使用現在式,祈使句
  • 第一個字母無需大寫
  • 結尾不用加句號

1.2 Body

跟subject一樣,使用現在式,祈使句。應該說明提交程式碼的動機,以及跟前一個版本的對比。

1.3 Footer

Foot包含可以包含以下資訊:

1.3.1 描述重大變更的資訊

以 BREAKING CHANGE 開頭,後面是變更的具體描述,如

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',
      myBind: 'bind',
      myExpression: 'expression',
      myEval: 'evaluate',
      myAccessor: 'accessor'
    }

    After:

    scope: {
      myAttr: '@',
      myBind: '@',
      myExpression: '&',
      // myEval - usually not useful, but in cases where the expression is assignable, you can use '='
      myAccessor: '=' // in directive's template change myAccessor() to myAccessor
    }

    The removed `inject` wasn't generaly useful for directives so there should be no code using it.
複製程式碼

1.3.2 關閉JIRA

如:

Closes DB-1001, DB1002
複製程式碼

1.4 一些 Commit Message 例子:

feat($browser): onUrlChange event (popstate/hashchange/polling)

Added new event to $browser:
- forward popstate event if available
- forward hashchange event if popstate not available
- do polling when neither popstate nor hashchange available

Breaks $browser.onHashChange, which was removed (use onUrlChange instead)

---------

fix($compile): couple of unit tests for IE9

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

---------

eat(directive): ng:disabled, ng:checked, ng:multiple, ng:readonly, ng:selected

New directives for proper binding these attributes in older browsers (IE).
Added coresponding description, live examples and e2e tests.

Closes #351

---------

feat($compile): simplify isolate scope bindings

Changed the isolate scope binding options to:
  - @attr - attribute binding (including interpolation)
  - =model - by-directional model binding
  - &expr - expression execution binding

This change simplifies the terminology as well as
number of choices available to the developer. It
also supports local name aliasing from the parent.

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',
  myBind: 'bind',
  myExpression: 'expression',
  myEval: 'evaluate',
  myAccessor: 'accessor'
}

After:

scope: {
  myAttr: '@',
  myBind: '@',
  myExpression: '&',
  // myEval - usually not useful, but in cases where the expression is assignable, you can use '='
  myAccessor: '=' // in directive's template change myAccessor() to myAccessor
}

The removed `inject` wasn't generaly useful for directives so there should be no code using it.

複製程式碼

如果是特性開發,則可以這樣:

feat(短視訊播放優化): 全屏播放動畫效果優化
複製程式碼

2 提交日誌自動校驗

在NodeJS專案中,我們可以通過使用 ghooks + validate-commit-msg進行提交日誌校驗,校驗不通過的將被拒絕提交。原理是通過NodeJs專案編譯,把NodeJS的校驗指令碼寫到.git/hooks/目錄下的勾子檔案中,這樣每次執行git commit命令都會執行這個校驗,不過這種方式依賴於Node環境,並且每個Git專案都需要引入對應的npm模組進行編譯,對於比較多微服務專案的情況來說使用不太方便。

為此,編寫了git-hook-maven-plugin外掛, 該外掛有如下特點:

  • 很方便的在專案中自定義團隊的工作流, 把自定義鉤子鉤子指令碼納入git管理類, 方便團隊共享;
  • 把鉤子指令碼的安裝整合到Maven的生命週期中的某個階段, 方便的通過專案編譯自動安裝或者升級指令碼;
  • 提供通用的內建指令碼, 方便鉤子的配置, 目前只提供了validate-commit-message鉤子指令碼, 用於提交日誌的規範, 遵循AngularJS Git Commit Message Conventions的格式。

具體安裝參考說明文件

配置完這個,當我們通過maven編譯完專案之後,下一次提交程式碼,如果提交日誌不符合規範,則會報錯:

➜  project-1 git:(master) ✗ git commit -m "test"

Commit log error: First commit message line (commit header) does not follow format: type(scope): subject
 - Refer commit guide: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
複製程式碼

3 特性開發提交壓縮合並

對於獨自完成的特性,可能在開發過程中會產生多個提交,為了讓提交整潔,需要對這個特性的所有提交進行壓縮合並。我們先看看壓縮合並之前的程式碼:

* be6e32d (HEAD->master)feat(測試提交): 修改第二個檔案
* 4a7615e feat(測試提交): 修改第一個檔案
* 721064e feat(測試提交): 提交第四個檔案
* e20968e feat(測試提交): 提交第三個檔案
* b7160b3 feat(測試提交): 提交第二個檔案
* 0c90fcl feat(測試提交): 提交第一個檔案
* e618321 fix(頁面展示): 展示錯誤修復
複製程式碼

如圖,可以發現壓縮合並測試特性有多個提交,可以進行合併,現在準備把e618321前面的提交都進行合併,執行git rebase -i命令進行壓縮合並:

git rebase -i e618321

其中命令後面的hash值是壓縮合並開始的那個提交的hash值:

pick 0c90fcl feat (測試提交): 提交第一個檔案
pick b7160b3 feat(測試提交): 提交第二個檔案
pick e20968e feat(測試提交): 提交第三個檔案
pick 721064e feat(測試提交): 提交第四個檔案
pick 4a7615e feat(測試提交): 修改第一個檔案
pick be6e32d feat(測試提交): 修改第二個檔案

# Rebase e618321..be6e32d onto e618321 (6 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
複製程式碼

注意:這個介面裡面的提交是按照提交時間順序往下排的,最新提交的在最後面,跟git lg的相反,平時檢視日誌是最新提交的排在最前面。

我們把低2行到6行改為 s(squash),第一個行改為 r(reword),把第2到6行合併到第一個提交裡面,調整下提交資訊:

r 0c90fcl feat (測試提交): 提交第一個檔案
s b7160b3 feat(測試提交): 提交第二個檔案
s e20968e feat(測試提交): 提交第三個檔案
s 721064e feat(測試提交): 提交第四個檔案
s 4a7615e feat(測試提交): 修改第一個檔案
s be6e32d feat(測試提交): 修改第二個檔案
複製程式碼

然後輸 :wq 儲存並退出編輯模式繼續處理。

如果遇到有衝突,需要解決完衝突之後,修改衝突的提交日誌。

最後是一個壓縮合並之後的提交日誌確認介面,看看第一行的日誌是否符合我們的需求,沒有問題則輸入 :wq 儲存並退出編輯模式。

# This is acombination of 7commits.
# The first commit'smessage is:
feat(測試提交): 測試程式碼

# This is the 2nd commit message:

feat(測試提交): 提交第二個檔案

# This is the 3rd commit message:

feat(測試提交): 提交第三個檔案
複製程式碼

這樣我們就合併完成了,再次檢視提交日誌,發現已經合併程了一個提交:

* eb0121a (HEAD->master) feat(測試提交): 測試程式碼
* e618321 fix(頁面展示): 展示錯誤修復
複製程式碼

注:git lg中 lg = log --graph --oneline --decorate,特別注意,本次演示程式碼直接在master上面進行,實際上需要在 feature-xxx 分支上面進行。

提交程式碼和壓縮合並的原則:

開發過程中的提交程式碼原則: 儘量減少commit的次數; 在push之前,需要把本次特性所有的程式碼都壓縮成一個提交; 在提交程式碼審查之前,最好把本次特產壓縮成一個提交

4 合併master程式碼使用 rebase

假設我們開發一個新的特性,並且已經把程式碼進行了壓縮合並,提交日誌如下:

* 69b4ffb (HEAD -> feature-test) feat(測試特性): 測試特性程式碼
* eb0121a (HEAD->master) feat(測試提交): 測試程式碼
* e618321 fix(頁面展示): 展示錯誤修復
複製程式碼

而此時,master程式碼也有了新的修復程式碼提交:

* 69b4ffb (HEAD -> master) fix(快取): 修復產品介面快取bug
* eb0121a (HEAD->master) feat(測試提交): 測試程式碼
* e618321 fix(頁面展示): 展示錯誤修復
複製程式碼

為了保持合併之後,我們當前特性點仍然在提交日誌的最新位置,達成效果如下:

我們使用rebase進行合併程式碼:

git rebase master

合併之後效果如下,我們當前特性分支的提交仍然在最前面:

* 69b4ffb (HEAD -> feature-test) feat(測試特性): 測試特性程式碼
* 69b4ffb (HEAD -> master) fix(快取): 修復產品介面快取bug
* eb0121a (HEAD->master) feat(測試提交): 測試程式碼
* e618321 fix(頁面展示): 展示錯誤修復
複製程式碼

rebase程式碼原則:

特性分支合併master程式碼使用 rebase,總讓當前特性處於最近提交; 如果一個特性有多個同事開發,rebase之前記得讓其他同事提交所有程式碼,rebase之後記得讓其他同事強制更新原生程式碼;

References

Git 提交的正確姿勢:Commit message 編寫指南 你可能會忽略的 Git 提交規範 Git鉤子:自定義你的工作流 用 Node.js 寫前端自己的 Git-hooks gist.github.com/jasonrobert…

Bash in Linux v.s Mac OS

GitUntrackedFilesMojo

首次釋出於:一個維護版本日誌整潔的Git提交規範

相關文章