Conventional Commits
根據 Conventional Commits v1.0.0 規範編寫提交訊息,以實現結構化的提交歷史、自動化變更日誌生成和語意化版本控制。
何時使用此技能
在以下情況使用此技能:
- 為結構化專案建立 Git 提交訊息
- 驗證提交訊息格式是否符合 Conventional Commits 規範
- 使用 commitlint 設定提交訊息驗證
- 從提交歷史配置變更日誌生成
- 實作語意化版本控制自動化
- 審查提交訊息以確保符合規範
- 對團隊進行提交訊息標準訓練
- 與 semantic-release、commitizen 或 git-cliff 等工具整合
訊息結構
<類型>[可選範圍]: <描述>
[可選正文]
[可選腳註]
組成元件
| 元件 | 必要 | 描述 | 範例 |
|---|---|---|---|
| type(類型) | 是 | 提交類別 | feat, fix, docs |
| scope(範圍) | 否 | 受影響的程式碼庫部分 | (auth), (parser) |
| description(描述) | 是 | 變更的簡短摘要 | add user authentication |
| body(正文) | 否 | 詳細解釋 | 多段落,提供上下文 |
| footer(腳註) | 否 | 重大變更、問題參照 | BREAKING CHANGE:, Refs: #123 |
提交類型
必要類型(SemVer 影響)
這些類型是 Conventional Commits 規範所要求的,並直接影響語意化版本控制:
flowchart LR
Fix[fix: 錯誤修正<br/>for users] --> PatchVer[PATCH 修補<br/>0.0.X]
Feat[feat: 新功能<br/>for users] --> MinorVer[MINOR 次要<br/>0.X.0]
Breaking[任何包含<br/>BREAKING CHANGE 或 ! 的類型] --> MajorVer[MAJOR 主要<br/>X.0.0]
style PatchVer fill:#e1f5ff
style MinorVer fill:#fff4e1
style MajorVer fill:#ffe1e1
| 類型 | 描述 | SemVer | 範例 |
|---|---|---|---|
feat | 為使用者新增功能 | MINOR (0.X.0) | feat: add user authentication |
fix | 為使用者修正錯誤 | PATCH (0.0.X) | fix: prevent crash on empty input |
建議類型(Angular 慣例)
這些類型來自 @commitlint/config-conventional,基於 Angular 提交準則。除非包含 BREAKING CHANGE,否則它們對 SemVer 沒有隱含影響。
| 類型 | 描述 | 範例 |
|---|---|---|
build | 建置系統或外部依賴項的變更 | build: update webpack to v5 |
ci | CI 配置變更 | ci: add Node 18 to test matrix |
docs | 僅文件變更 | docs: update API reference |
perf | 效能改進 | perf: reduce bundle size by 20% |
refactor | 不修正錯誤也不新增功能的程式碼變更 | refactor: extract validation logic |
style | 程式碼樣式變更(空白、格式) | style: fix indentation |
test | 新增或修正測試 | test: add unit tests for parser |
其他常見類型
| 類型 | 描述 | 範例 |
|---|---|---|
chore | 不修改 src 或測試檔案的變更 | chore: update .gitignore |
revert | 還原先前的提交 | revert: feat: add user auth |
注意:團隊可以定義除 feat 和 fix 之外的自訂類型,但 Angular 慣用類型廣泛受到工具支援。
規範規則
以下規則構成官方的 Conventional Commits 1.0.0 規範,符合 RFC 2119。
類型和範圍
-
提交 必須 以類型(名詞,如
feat、fix等)為前綴,後接 可選 範圍、可選!,以及 必要 的結尾冒號和空格。 -
當提交新增功能時,必須 使用類型
feat。 -
當提交是錯誤修正時,必須 使用類型
fix。 -
可以在類型之後提供範圍。範圍 必須 由括號內的描述程式碼庫部分的名詞組成,例如
fix(parser): -
描述 必須 緊跟在類型/範圍前綴後的冒號和空格之後。描述是程式碼變更的簡短摘要,例如
fix: array parsing issue when multiple spaces were contained in string
正文
-
可以在簡短描述之後提供較長的提交正文,以提供額外的上下文資訊。正文 必須 在描述之後空一行開始。
-
提交正文是自由格式的,可以 由任意數量的換行分隔段落組成。
腳註
-
可以在正文之後空一行提供一個或多個腳註。每個腳註 必須 由一個單詞標記組成,後接
:<space>或<space>#分隔符,再接一個字串值(靈感來自 git trailer 慣例)。 -
腳註的標記 必須 使用
-代替空白字元,例如Acked-by(這有助於將腳註部分與多段落正文區分開)。例外:BREAKING CHANGE可以 也用作標記。 -
腳註的值 可以 包含空格和換行。解析 必須 在觀察到下一個有效的腳註標記/分隔符對時終止。
重大變更
-
重大變更 必須 在提交的類型/範圍前綴中標示,或作為腳註中的條目標示。
-
如果作為腳註包含,重大變更 必須 由大寫文字
BREAKING CHANGE組成,後接冒號、空格和描述,例如BREAKING CHANGE: environment variables now take precedence over config files -
如果包含在類型/範圍前綴中,重大變更 必須 透過
!緊接在:之前來標示。如果使用!,則 可以 從腳註中省略BREAKING CHANGE:,並且 應該 使用提交描述來說明重大變更。
其他規則
-
可以在提交訊息中使用
feat和fix以外的類型,例如docs: update ref docs -
構成 Conventional Commits 的資訊單元 不得 被實作者視為區分大小寫,但
BREAKING CHANGE除外,它 必須 為大寫。 -
BREAKING-CHANGE必須 與BREAKING CHANGE同義,當用作腳註中的標記時。
描述最佳實踐
基於 Angular 提交訊息準則:
摘要/描述準則
- 使用祈使句、現在式:"change" 而不是 "changed" 或 "changes"
- 不要將第一個字母大寫
- 結尾不要有句號 (.)
- 保持整個標題(類型 + 範圍 + 描述)在 72 個字元以內
良好範例
feat: add validation for email input
fix: handle null pointer in user service
docs: update installation instructions
refactor: simplify authentication flow
不良範例(應避免)
feat: Added validation for email input # 過去式
fix: Handles null pointer in user service # 第三人稱
docs: Update installation instructions. # 結尾有句點,首字大寫
FEAT: add validation # 大寫類型(不一致)
正文準則
- 使用祈使句、現在式(與摘要相同)
- 解釋變更的動機
- 在有用時包含先前與新行為的比較
- 正文 必須 在描述之後以空行開始
- 建議最小值:當有正文時,至少 20 個字元
重大變更
標示重大變更的方法
三種等價的方法:
-
腳註標記:
feat: allow provided config object to extend other configs BREAKING CHANGE: `extends` key in config file is now used for extending other config files -
類型後綴
!:feat!: remove support for Node 6 -
類型+範圍後綴
!:feat(api)!: send an email to the customer when a product is shipped
規則
BREAKING CHANGE必須 為大寫- 當用作腳註標記時,
BREAKING-CHANGE是同義詞 - 重大變更 必須 對應到 SemVer 中的 MAJOR 版本提升
語意化版本控制關聯
flowchart TD
Start([分析提交]) --> CheckBreaking{任何提交包含<br/>BREAKING CHANGE<br/>或 !?}
CheckBreaking -->|是| Major[MAJOR 提升<br/>X.0.0]
CheckBreaking -->|否| CheckFeat{任何提交<br/>是 feat?}
CheckFeat -->|是| Minor[MINOR 提升<br/>0.X.0]
CheckFeat -->|否| CheckFix{任何提交<br/>是 fix?}
CheckFix -->|是| Patch[PATCH 提升<br/>0.0.X]
CheckFix -->|否| NoChange[無版本變更]
Major --> End([發布])
Minor --> End
Patch --> End
NoChange --> End
提交類型到版本對應
| 提交類型 | 版本提升 |
|---|---|
fix | PATCH (0.0.X) |
feat | MINOR (0.X.0) |
BREAKING CHANGE or ! | MAJOR (X.0.0) |
原因:啟用基於提交歷史的自動化語意化版本控制。
驗證模式
標題驗證正則表達式
^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?:\s.+$
Python 驗證範例
import re
CONVENTIONAL_COMMIT_PATTERN = re.compile(
r"^(?P<type>feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)"
r"(?:\((?P<scope>[^)]+)\))?"
r"(?P<breaking>!)?"
r":\s"
r"(?P<description>.+)$"
)
def validate_header(header: str) -> bool:
"""驗證提交標題是否遵循 Conventional Commits。"""
return bool(CONVENTIONAL_COMMIT_PATTERN.match(header))
def parse_header(header: str) -> dict | None:
"""將提交標題解析為組成部分。"""
match = CONVENTIONAL_COMMIT_PATTERN.match(header)
if not match:
return None
return {
"type": match.group("type"),
"scope": match.group("scope"),
"breaking": bool(match.group("breaking")),
"description": match.group("description"),
}
完整範例
簡單提交
feat: add user authentication
fix: prevent crash on empty input
docs: correct spelling of CHANGELOG
帶範圍的提交
feat(lang): add Polish language
feat(parser): add ability to parse arrays
fix(auth): handle token expiration correctly
重大變更
feat: allow provided config object to extend other configs
BREAKING CHANGE: `extends` key in config file is now used for extending other config files
feat!: send an email to the customer when a product is shipped
feat(api)!: send an email to the customer when a product is shipped
chore!: drop support for Node 6
BREAKING CHANGE: use JavaScript features not available in Node 6.
多段落正文加上腳註
fix: prevent racing of requests
Introduce a request id and a reference to latest request. Dismiss
incoming responses other than from latest request.
Remove timeouts which were used to mitigate the racing issue but are
obsolete now.
Reviewed-by: Z
Refs: #123
還原提交
revert: let us never again speak of the noodle incident
Refs: 676104e, a215868
效能改進
perf: reduce memory allocation in parser by 40%
Replace string concatenation with StringBuilder pattern.
Benchmark results show 40% reduction in heap allocations.
Refs: #456
重構
refactor: extract authentication logic into separate module
Move auth-related functions from utils.py to auth.py.
No functional changes.
建置/CI 變更
ci: add Node 18 to test matrix
build: upgrade webpack from v4 to v5
- Update webpack.config.js for v5 compatibility
- Replace deprecated plugins
- Update all loader dependencies
Refs: #789
測試和樣式變更
test: add unit tests for user validation
Cover edge cases for email validation and password strength.
style: format code according to prettier config
chore: update .gitignore to exclude IDE files
與驗證工具整合
commitlint 配置
使用 Angular 配置,透過 commitlint 驗證提交:
# 安裝 commitlint
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# 建立配置
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# 測試提交訊息
echo 'feat(api): add new endpoint' | npx commitlint
如需全面的 commitlint 配置指導,請使用本文件中的指引。如果您的工作區安裝了 commitlint 外掛,可以委託其處理專案特定的規則和範例。
Pre-commit Hooks
使用 pre-commit hooks 強制執行提交訊息格式。如果您的工作區安裝了 pre-commit 外掛,可以使用它來設定 hook,否則直接配置 hooks。
變更日誌生成
使用 semantic-release 從 Conventional Commits 生成變更日誌:
// release.config.js
module.exports = {
branches: ['main'],
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
'@semantic-release/github',
],
};