你可能已經(jīng)忽略的git commit規(guī)范
引言
在日常的開發(fā)工作中,我們通常使用 git 來管理代碼,當(dāng)我們對代碼進(jìn)行某項改動后,都可以通過 git commit 來對代碼進(jìn)行提交。
git 規(guī)定提交時必須要寫提交信息,作為改動說明,保存在 commit 歷史中,方便回溯。規(guī)范的 log 不僅有助于他人 review, 還可以有效的輸出 CHANGELOG,甚至對于項目的研發(fā)質(zhì)量都有很大的提升。
但是在日常工作中,大多數(shù)同學(xué)對于 log 信息都是簡單寫寫,沒有很好的重視,這對于項目的管理和維護(hù)來說,無疑是不友好的。本篇文章主要是結(jié)合我自己的使用經(jīng)驗來和大家分享一下 git commit 的一些規(guī)范,讓你的 log 不僅“好看”還“實用”。
為什么要規(guī)范 git commit
一直在說要規(guī)范 commit 格式,那為什么要這樣做呢?
讓我們先來看一個不太規(guī)范的 commit 記錄:
看完什么感覺,寫的是啥啊(內(nèi)心 OS),這種 commit 信息對于想要從中獲取有效信息的人來說無疑是一種致命的打擊。
那我們來看一個社區(qū)里面比較流行的Angular規(guī)范的 commit 記錄:
看完是不是一目了然呢?
上圖中這種規(guī)范的 commit 信息首先提供了更多的歷史信息,方便快速瀏覽。其次,可以過濾某些 commit(比如文檔改動),便于快速查找信息。
既然說到了 Angular 團(tuán)隊的規(guī)范是目前社區(qū)比較流行的 commit 規(guī)范,那它具體是什么呢?下面讓我們來具體深入了解下吧。
Angular 團(tuán)隊的 commit 規(guī)范
它的 message 格式如下:
- <type>(<scope>): <subject>
- // 空一行
- <body>
- // 空一行
- <footer>
分別對應(yīng) Commit message 的三個部分:Header,Body 和 Footer。
Header
Header 部分只有一行,包括三個字段:type(必需)、scope(可選)和subject(必需)。
- type: 用于說明 commit 的類型。一般有以下幾種:
- feat: 新增feature
- fix: 修復(fù)bug
- docs: 僅僅修改了文檔,如readme.md
- style: 僅僅是對格式進(jìn)行修改,如逗號、縮進(jìn)、空格等。不改變代碼邏輯。
- refactor: 代碼重構(gòu),沒有新增功能或修復(fù)bug
- perf: 優(yōu)化相關(guān),如提升性能、用戶體驗等。
- test: 測試用例,包括單元測試、集成測試。
- chore: 改變構(gòu)建流程、或者增加依賴庫、工具等。
- revert: 版本回滾
- scope: 用于說明 commit 影響的范圍,比如: views, component, utils, test...
- subject: commit 目的的簡短描述
Body
對本次 commit 修改內(nèi)容的具體描述, 可以分為多行。如下圖:
- # body: 72-character wrapped. This should answer:
- # * Why was this change necessary?
- # * How does it address the problem?
- # * Are there any side effects?
- # initial commit
Footer
一些備注, 通常是 BREAKING CHANGE(當(dāng)前代碼與上一個版本不兼容) 或修復(fù)的 bug(關(guān)閉 Issue) 的鏈接。
簡單介紹完上面的規(guī)范,我們下面來說一下commit.template,也就是 git 提交信息模板。
git 提交信息模板
如果你的團(tuán)隊對提交信息有格式要求,可以在系統(tǒng)上創(chuàng)建一個文件,并配置 git 把它作為默認(rèn)的模板,這樣可以更加容易地使提交信息遵循格式。
通過以下命令來配置提交信息模板:
- git config commit.template [模板文件名] //這個命令只能設(shè)置當(dāng)前分支的提交模板
- git config — —global commit.template [模板文件名] //這個命令能設(shè)置全局的提交模板,注意global前面是兩杠
新建 .gitmessage.txt(模板文件) 內(nèi)容可以如下:
- # headr: <type>(<scope>): <subject>
- # - type: feat, fix, docs, style, refactor, test, chore
- # - scope: can be empty
- # - subject: start with verb (such as 'change'), 50-character line
- #
- # body: 72-character wrapped. This should answer:
- # * Why was this change necessary?
- # * How does it address the problem?
- # * Are there any side effects?
- #
- # footer:
- # - Include a link to the issue.
- # - BREAKING CHANGE
- #
看完上面這些,你會不會像我一樣感覺配置下來挺麻煩的,配置一個適合自己和團(tuán)隊使用的近乎完美的 commit 規(guī)范看來也不是一件容易的事情。不過社區(qū)也為我們提供了一些輔助工具來幫助進(jìn)行提交,下面來簡單介紹一下這些工具。
commitizen(cz-cli)
commitizen是一款可以交互式建立提交信息的工具。它幫助我們從 type 開始一步步建立提交信息,具體效果如圖所示:
- 首先通過上下鍵控制指向你想要的 type 類型,分別對應(yīng)有上面提到的feat、fix、docs、perf等:
- 然后會讓你選擇本次提交影響到的文件:
- 后面會讓你分別寫一個簡短的和詳細(xì)的提交描述:
- 最后會讓你去判斷本次提交是否是BREAKING CHANGE或者有關(guān)聯(lián)已開啟的issue:
看完上面的 commitizen 的整個流程,下面讓我們來看下如何來安裝。
- 全局環(huán)境下安裝:
commitizen 根據(jù)不同的adapter配置 commit message。例如,要使用 Angular 的 commit message 格式,可以安裝cz-conventional-changelog。
- # 需要同時安裝commitizen和cz-conventional-changelog,后者是adapter
- $ npm install -g commitizen cz-conventional-changelog
- # 配置安裝的adapter
- $ echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
- # 使用
- $ git cz
- 本地項目安裝:
- # 安裝commitizen
- $ npm install --save-dev commitizen
- # 接下來安裝適配器
- # for npm >= 5.2
- $ npx commitizen init cz-conventional-changelog --save-dev --save-exact
- # for npm < 5.2
- $ ./node_modules/.bin/commitizen init cz-conventional-changelog --save-dev --save-exact
- // package.json script字段中添加commit命令
- "scripts": {
- "commit": "git-cz"
- }
- // use
- $ npm run commit
commitlint
commitlint是一個提交驗證工具。原理是可以在實際的 git commit 提交到遠(yuǎn)程倉庫之前使用 git 鉤子來驗證信息。提交不符合規(guī)則的信息將會被阻止提交到遠(yuǎn)程倉庫。
先來看一下演示:
對于 Conventional Commits 規(guī)范,社區(qū)已經(jīng)整理好了 @commitlint/config-conventional 包,我們只需要安裝并啟用它就可以了。
首先安裝 commitlint 以及 conventional 規(guī)范:
- npm install --save-dev @commitlint/cli @commitlint/config-conventional
接著在 package.json 中配置 commitlint 腳本:
- "commitlint": {
- "extends": [
- "@commitlint/config-conventional"
- ]
- },
當(dāng)然如果你想單獨對 commitlint 進(jìn)行配置的話,需要建立校驗文件 commitlint.config.js,不然會校驗失敗
為了可以在每次 commit 時執(zhí)行 commitlint 來 檢查我們輸入的 message,我們還需要用到一個工具 —— husky。
husky 是一個增強(qiáng)的 git hook 工具??梢栽?git hook 的各個階段執(zhí)行我們在 package.json 中配置好的 npm script。
首先安裝 husky:
- npm install --save-dev husky
接著在 package.json 中配置 commitmsg 腳本:
- "husky": {
- "hooks": {
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
- }
- },
到這里,commitlint就配置完成了~
gitmoji-cli
平時與朋友聊天時,我們一定會用到表情包,比如。表情包的出現(xiàn)讓我們與朋友之間的溝通變得更加有趣。如果能在 git 提交 commit 時用到表情包(),豈不是使每次的 commit 能夠更加直觀,維護(hù)起來也更加方便。
gitmoji就是可以實現(xiàn)這種功能的插件,先讓我們來感受一下
有沒有感覺很 cool~~
其實gitmoji的使用是很簡單的:
- # 安裝
- npm i -g gitmoji-cli
- # 使用
- git commit -m ':bug: 問題fix'
我們來看一下官方的示例吧:
是不是躍躍欲試了呢?
gitmoji項目地址: https://github.com/carloscuesta/gitmoji/
gitmoji使用示例: https://gitmoji.carloscuesta.me/
看完本文,是不是感覺對于git commit message又有了新的認(rèn)識呢?去在你的項目中運用這些吧,讓你的commit更加規(guī)范的同時,也不要忘了給你的log加上emoji哦!
最后附上一個之前項目針對git commit配置的package.json,作為參考:
- {
- "name": "ts-axios",
- "version": "0.0.0",
- "description": "",
- "keywords": [],
- "main": "dist/ts-axios.umd.js",
- "module": "dist/ts-axios.es5.js",
- "typings": "dist/types/ts-axios.d.ts",
- "files": [
- "dist"
- ],
- "author": "fengshuan <1263215592@qq.com>",
- "repository": {
- "type": "git",
- "url": ""
- },
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- },
- "scripts": {
- "dev": "node examples/server.js",
- "lint": "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
- "prebuild": "rimraf dist",
- "build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src",
- "start": "rollup -c rollup.config.ts -w",
- "test": "jest --coverage",
- "test:watch": "jest --coverage --watch",
- "test:prod": "npm run lint && npm run test -- --no-cache",
- "deploy-docs": "ts-node tools/gh-pages-publish",
- "report-coverage": "cat ./coverage/lcov.info | coveralls",
- "commit": "git-cz",
- "semantic-release": "semantic-release",
- "semantic-release-prepare": "ts-node tools/semantic-release-prepare",
- "precommit": "lint-staged",
- "travis-deploy-once": "travis-deploy-once"
- },
- "husky": {
- "hooks": {
- "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
- }
- },
- "lint-staged": {
- "{src,test}/**/*.ts": [
- "prettier --write",
- "git add"
- ]
- },
- "config": {
- "commitizen": {
- "path": "node_modules/cz-conventional-changelog"
- }
- },
- "jest": {
- "transform": {
- ".(ts|tsx)": "ts-jest"
- },
- "testEnvironment": "node",
- "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
- "moduleFileExtensions": [
- "ts",
- "tsx",
- "js"
- ],
- "coveragePathIgnorePatterns": [
- "/node_modules/",
- "/test/"
- ],
- "coverageThreshold": {
- "global": {
- "branches": 90,
- "functions": 95,
- "lines": 95,
- "statements": 95
- }
- },
- "collectCoverageFrom": [
- "src/*.{js,ts}"
- ]
- },
- "prettier": {
- "semi": false,
- "singleQuote": true
- },
- "commitlint": {
- "extends": [
- "@commitlint/config-conventional"
- ]
- },
- "devDependencies": {
- "@commitlint/cli": "^7.1.2",
- "@commitlint/config-conventional": "^7.1.2",
- "@types/jest": "^23.3.2",
- "@types/node": "^10.11.0",
- "body-parser": "^1.19.0",
- "colors": "^1.3.2",
- "commitizen": "^3.0.0",
- "coveralls": "^3.0.2",
- "cross-env": "^5.2.0",
- "cz-conventional-changelog": "^2.1.0",
- "express": "^4.17.1",
- "husky": "^1.0.1",
- "jest": "^23.6.0",
- "jest-config": "^23.6.0",
- "lint-staged": "^8.0.0",
- "lodash.camelcase": "^4.3.0",
- "prettier": "^1.14.3",
- "prompt": "^1.0.0",
- "replace-in-file": "^3.4.2",
- "rimraf": "^2.6.2",
- "rollup": "^0.67.0",
- "rollup-plugin-commonjs": "^9.1.8",
- "rollup-plugin-json": "^3.1.0",
- "rollup-plugin-node-resolve": "^3.4.0",
- "rollup-plugin-sourcemaps": "^0.4.2",
- "rollup-plugin-typescript2": "^0.18.0",
- "semantic-release": "^15.9.16",
- "shelljs": "^0.8.3",
- "travis-deploy-once": "^5.0.9",
- "ts-jest": "^23.10.2",
- "ts-loader": "^6.1.1",
- "ts-node": "^7.0.1",
- "tslint": "^5.11.0",
- "tslint-config-prettier": "^1.15.0",
- "tslint-config-standard": "^8.0.1",
- "tslint-loader": "^3.5.4",
- "typedoc": "^0.12.0",
- "typescript": "^3.0.3",
- "webpack": "^4.40.2",
- "webpack-dev-middleware": "^3.7.1",
- "webpack-hot-middleware": "^2.25.0"
- }
- }