Conventional Commits 的實踐指南:寫出乾淨的提交訊息
前言:
你是不是也常遇到這樣的情況:翻開 Git 的 Commit 記錄,滿滿的「Update」、「Fix bug」或乾脆直接用 emoji 混過去。在團隊協作中,這種隨意的 Commit Message 不但讓歷史紀錄難以理解,還可能拖累整個專案的自動化流程,甚至影響版本管理的準確性。這就是 Conventional Commit 派上用場的時候了!它是一套簡單又實用的規範,幫助你把 Commit Message 寫得清楚又有條理。不僅讓版本歷史一目了然,還能輕鬆實現自動生成變更日誌,甚至和語意化版本控制(Semantic Versioning)無縫接軌。
接下來,這篇文章會一步步帶你了解什麼是 Conventional Commit、它的格式怎麼寫、為什麼它能對專案有幫助,以及在日常開發中要怎麼用它來讓你的工作更高效,讓團隊合作更順暢!
什麼是 Conventional Commit?
定義與目的
簡單來說,Conventional Commit 就是一套用來規範 Git Commit Message 的寫法規則。它的目的是幫助我們在版本控制中維持一致性與可讀性,同時為自動化工具提供足夠的資訊,讓整個開發流程更有效率。
那為什麼要這麼麻煩規範 Commit Message 呢?其實,Conventional Commit 的好處不只是看起來整齊,它還能幫助你做到以下幾件事:
- 
提升版本控制的可讀性 
 清楚的 Commit Message 讓團隊中的每個人都能快速理解變更內容,甚至可以直接找到問題的根源。
- 
支援自動生成變更日誌(Change Log) 
 根據規範好的 Message,工具可以自動整理出完整的變更日誌,節省手動編寫的時間。
- 
輔助語意化版本控制(Semantic Versioning) 
 結合 Semantic Versioning,可以根據不同類型的 Commit 自動決定版本號的升級方式,讓版本管理更加可靠。
基本格式
Conventional Commit 的格式其實很簡單,看起來像這樣:
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
讓我們來分解一下每個部分的用途:
- 
type(Commit 類型): 描述這次 Commit 的性質,例如是新增功能(feat)、修復問題(fix),還是進行雜項更新(chore)。
- 
scope(影響範圍,可選): 用來標記這次變更影響的範圍,比如是哪個模組或功能。如果影響範圍明確,建議加上,能讓訊息更清楚。
- 
description(簡短描述): 一句話清楚說明這次 Commit 的內容。最好簡短有力,保持 在 50 字以內。
- 
body(詳細說明,可選): 如果需要,可以在這裡詳細描述 Commit 的背景、動機或實現方式。
- 
footer(註解或 Breaking Change,可選): 用來記錄像 Breaking Change 的訊息,或者相關 Issue 的 ID。例如:BREAKING CHANGE: 改變了 API 的回傳結構
 Resolves #123
與 Semantic Versioning 的關係
Conventional Commit 和 Semantic Versioning(語意化版本控制) 是一對好搭檔。後者使用版本號的格式 MAJOR.MINOR.PATCH,分別代表:
- MAJOR:重大改動(可能破壞向後相容性)
- MINOR:新增功能(不影響向後相容性)
- PATCH:修正錯誤(不影響向後相容性)
而 Conventional Commit 的規範則可以幫助我們自動決定版本號的升級規則:
- 使用 fix的 Commit,通常只影響 PATCH 版本,表示修正錯誤。
- 使用 feat的 Commit,會影響 MINOR 版本,表示新增功能。
- 如果有 BREAKING CHANGE,就會升級 MAJOR 版本,因為改動可能影響到向後相容性。
常用 Commit 類型與範例
常見的 Commit 類型
在使用 Conventional Commit 時,除了大家熟悉的 feat(新增功能)和 fix(修正 Bug),其實還有很多其他類型的 Commit 可以用來描述不同的變更。以下推薦一套我滿喜歡的類型定義集 @commitlint/config-conventional ,這套規範是基於 Angular 團隊的開發慣例 所制定的,目前在開源社群中被廣泛採用。
- feat: 表示新增了一個新的功能。通常會影響 MINOR 版本號。
- fix: 用來描述修復的問題。通常會影響 PATCH 版本號。
- docs: 任何文檔的更新,例如 README 或 API 文件的修改。不會影響程式碼執行。
- style: 只涉及程式碼的格式化,與程式邏輯無關的修改,比如縮排、逗號位置等。
- refactor: 改善程式結構或優化,未新增功能,也沒有修正 Bug。
- test: 專門針對測試檔案的修改。
- chore: 一些雜項工作,例如工具設定、依賴更新等。
- build: 涉及編譯過程的修改,例如修改 webpack、rollup 等配置。
- ci: 與 CI/CD 腳本或流程有關的更動。
- perf: 專注於提升執行效能的修改。
- revert: 如果需要回退某次提交,使用- revert。
範例
以下是幾個實際的使用場景,幫助你更好理解這些類型的應用:
- 
新增功能提交 feat(profile): add profile picture upload feature
- 
Bug 修正提交 fix(payment): resolve issue with invalid card numbers
- 
Breaking Change feat(api): migrate to new authentication flow
 BREAKING CHANGE: user API now requires OAuth token
- 
文件變更提交 docs(api): add endpoint usage examples
- 
回退某次提交 revert: revert "feat(auth): add login with Google"
常見問題與注意事項
常見問題
- 
Q:當提交符合多種提交類型時,該怎麼辦? 當一次提交同時符合多種類型,建議選擇最能代表此次變更的主要類型。例如,如果新增功能時也修正了 Bug,應選擇 feat,並在正文中補充說明修正的內容。
- 
Q: 什麼時候該寫 Scope? scope是可選的,但建議在影響範圍清晰時加以描述,例如指定某個功能模組、檔案或子系統。這能幫助開發者快速理解變更的影響範圍,如果範圍不明確或無特定模組影響,可以省略。
- 
Q: Commit Message 一定要用英文嗎? Commit Message 的語言應以團隊習慣為準。英文是國際化的標準選擇,但如果團隊成員習慣用中文或其他語言,也可以使用母語,只要訊息內容清楚、一致即可。 
- 
Q: 如果不小心用錯提交類型,該怎麼辦? 可以使用 git commit --amend修正最近一次提交訊息,或者使用git rebase -i <base commit>來編輯提交歷史。
- 
Q: 如何處理回退提交 (revert commit)? 在實際開發中,回退提交(revert commit)有時是不可避免的,但這可能比想像中複雜得多。你需要考慮以下幾個問題: - 是回退一個提交還是多個提交?
 如果是多個提交,需特別小心可能產生的衝突,並確保每個被回退的改動都處理妥當。
- 回退的是功能還是修復?
 如果回退的內容是某個功能,那麼回退後的變更應該算作 修正檔(PATCH) 嗎?這需要根據專案的語意化版本策略來決定。
 Conventional Commit 並沒有對回退行為做出強制規範,這留給工具作者或團隊自行定義。一個推薦的方式是使用 revert類型,並在頁腳中標註被回退的 Commit 的 SHA 雜湊。這樣能清楚表達回退的意圖,同時為工具處理提供必要的資訊。範例: revert: let us never again speak of the noodle incident
 Refs: 676104e, a215868
- 是回退一個提交還是多個提交?
注意事項
- 
避免過長的描述 description部分應保持簡潔,建議 50 個 字以內即可清楚表達變更的重點。詳細的內容可以放在body區塊中,避免讓標題過於冗長。
- 
合理的提交粒度 每次提交應專注於單一變更,保持適當的粒度。過大的提交會讓變更難以追溯,而過於零碎的提交則可能讓記錄過於冗長,增加維護成本。 
- 
大小寫規範 Commit Message 的 type和scope建議使用小寫字母,以保持格式的一致性。唯有BREAKING CHANGE必須大寫,且後需接分號與空格。
- 
BREAKING CHANGE的規範當變更中包含破壞性更新(例如影響向後相容性),必須在頁腳中明確標註 BREAKING CHANGE,並描述破壞性改動的原因及影響。例如:BREAKING CHANGE: user authentication now requires OAuth tokens
- 
頁腳格式的書寫規範