本文由風蕭蕭夢瀟投稿,轉載請註明出處。若有錯誤,請不吝指正。html
在工做中,咱們一般使用git來管理代碼,當咱們對代碼進行某項改動後,均可以經過git commit對代碼進行提交。git規定提交時必需要寫提交信息,做爲改動說明,保存在commit歷史中,方便回溯。但你仔細研究過git commit嗎?或者換句話說你注意過應該怎樣寫commit信息和優化commit信息嗎?本文將回答這些問題,但願對你們有所幫助。node
爲了對每次提交進行提交說明,方便以後回溯和團隊協做,Git 每次提交代碼,都要寫 Commit message(提交說明),不然就不容許提交。當參與團隊協做時,咱們共同維護一個maste或dev的代碼,若是其餘人改動了倉庫代碼,咱們確定想知道他到底改動了什麼,此次改動會對咱們有什麼影響。怎麼看呢,直接看代碼確定沒錯,但不是最好的方式。其實最方便的方式就是查看提交歷史了。若是提交信息寫的足夠好的話,不須要看代碼咱們也清楚改動了什麼,這樣就能夠提升協做效率和溝通成本了。git
試想誰不想看到下面這樣優雅清晰明瞭提交歷史呢,若是再能自動生成ChangeLog文檔天然是極好的。我想沒有哪一個程序員能抵擋得住這種誘惑吧。程序員
整體來講,規範 commit信息的意義以下:github
鑑於此,咱們須要寫出清晰規範的git commit。那麼什麼的規範的commit信息呢,請繼續看下去。shell
爲引導用戶爲開源社區作貢獻,angular較早提出了進行pr時的commit說明規範(詳見Git Commit Guidelines),後被不少開源項目所接受,造成了一套約定式提交規範。首先讓咱們從一條提交記錄出發看一下angular是如何寫提交信息的。 npm
如上所示,angular提交信息遵循以下格式:json
<type>(<scope>): <subject> # header信息頭必須
<BLANK LINE>
<body> # 信息體
<BLANK LINE>
<footer> # 結束部分
複製代碼
header 信息頭(必須)<type>(<scope>): <subject>
vim
信息頭必需要有type,它嚴格限定爲以下值:windows
feat: A new feature
fix: A bug fix
docs: Documentation only changes
style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
refactor: A code change that neither fixes a bug nor adds a feature
perf: A code change that improves performance
test: Adding missing or correcting existing tests
chore: Changes to the build process or auxiliary tools and libraries such as documentation generation
複製代碼
另外若是本次提交是回退以前的提交,應該以revert:開頭,跟着要回退提交的header信息,在body中寫:This reverts commit <hash>
其餘開源項目的type類型也相似,像electron的type類型以下所示,詳見Commit message guidelines:
fix: A bug fix
feat: A new feature
docs: Documentation changes
test: Adding missing tests or correcting existing tests
build: Changes that affect the build system
ci: Changes to our CI configuration files and scripts
perf: A code change that improves performance
refactor: A code change that neither fixes a bug nor adds a feature
style: Changes that do not affect the meaning of the code (linting)
vendor: Bumping a dependency like libchromiumcontent or node
複製代碼
接下來是scope。它指定了本次提交的影響範圍,好比數據層、控制層、視圖層等等,視項目不一樣而定。若是影響範圍較大,可使用通配符*。scope能夠忽略。
最後是subject,指出本次提交的主題,是對本次提交的簡短描述,不超過50個字符。注意它和前面的冒號之間要有空格,和type同樣,必需要有。subject內容有如下規定:
type和subject決定了本次提交信息的質量,是靈魂所在。
body 信息體(可忽略)
是對header中subject的補充,能夠寫一些本次提交改動了哪裏和有什麼影響等內容。注意時態一樣要使用通常如今時,好比改動要寫change,不能用changed或changes。
footer 信息尾部(可忽略)
尾部能夠包含一些相似作出了哪些重大改變這樣的內容,也能夠寫關閉了那些issue。另外Breaking Changes要以BREAKING CHANGE:
打頭,後面跟空格或兩個新行和具體內容。關閉了那些bug要使用Closes
打頭,哪怕只關閉了一個,例如Closes #234
angular關於commit message的詳細規範請參見Git Commit Message Conventions文檔。
看來要寫出符合git commit規範的信息也是不容易的,不過社區也爲咱們提供了一些輔助工具來幫助進行提交,下面根據使用目的來簡單介紹一下這些工具。
cz-cli 是一款能夠交互式創建提交信息的工具。它幫助咱們從type開始一步步創建提交信息,具體效果如圖所示,經過上下鍵控制指向你想要的type類型。
注意:
- windows下使用git bash 運行該工具不能上下選擇,可使用powershell代替。
- 此工具要配合adapter(相似模板)使用,來提供交互提交的樣式包括type類型和提交的結構,可自定義。
全局環境下安裝使用方法以下:
# 須要同時安裝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
// 若是不但願每次使用npm run commit寫提交信息的話,可使用git hook,把git commit命令替換成交互式提交,注意windows下不生效
"husky": {
"hooks": {
"prepare-commit-msg": "exec < /dev/tty && git cz --hook",
}
}
複製代碼
commitlint是一款提交信息校驗工具,原理是可用git hooks在真正進行git commit入庫操做前對信息進行驗證,不符合規則的commit信息將會被阻止提交入庫,以下所示。
使用方式也很簡單:
# 安裝 commitlint cli and conventional config
$ npm install --save-dev @commitlint/{config-conventional,cli}
# Windows下自帶命令行工具不會識別/{x,x},因此使用如下語句安裝
$ npm install --save-dev @commitlint/config-conventional @commitlint/cli
# 生成commitlint配置文件,這步是必須的,否則提交會報錯,達不到效果
$ echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
複製代碼
注意:使用commitlint時須要創建校驗文件commitlint.config.js,否則會校驗失敗
若是你們本身寫過git hooks,那麼能夠把commitlint添加到git commit前的校驗中,若是不是很熟的話能夠藉助一些工具,好比husky。
# 安裝husky
$ npm i -D husky
# package.json文件添加以下字段
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
複製代碼
固然若是認爲遵照規則很累,能夠在git commit時使用 --no-verify參數跳過驗證,直接提交。不過這樣使用該插件就沒意義了。因此儘可能保證本身寫的commit信息符合規範。
changelog是改動日誌,包含咱們發佈的版本信息,以及每次版本更新的東西。
conventional-changelog-cli是一款changelog生成工具,經過提取commit信息(默認提取類型爲feat、fix和包含breaking change的提交信息)爲咱們自動化生成changelog文檔。
全局使用方法以下:
# 安裝conventional-changelog-cli
$ npm install -g conventional-changelog-cli
$ cd my-project
# 保留原changelog文檔,生成本次改變
$ conventional-changelog -p angular -i CHANGELOG.md -s
# 徹底從新生成changelog文檔
$ conventional-changelog -p angular -i CHANGELOG.md -s -r 0
複製代碼
若是不想全局安裝的話,可使用npm run script方式運行,以下所示
$ npm i -D conventional-changelog-cli
// package.json script字段中添加version命令
"script": {
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md"
}
// use
$ npm run version
複製代碼
conventional-changelog-cli生成changelog是根據到目前爲止的全部提交信息同最新一次tag做比較獲得的全部feature、bug、breaking change信息進行文檔的生成,因此要在每次版本更新並生成changelog文檔後對分支打tag,再push到遠程分支。
這裏摘取conventional-changelog-cli推薦的總體流程:
社區一樣提供了一些高級工具來自動化進行上述的某些流程,能夠說提供了一條龍服務。具體工具以下:
standard-version: 更新版本、生成changelog、打tag
semantic-release: 更新版本、生成changelog、打tag、推送代碼發佈
具體使用方法請自行查看文檔,這裏再也不贅述。
前面講了這麼多git commit規範,可能你們要問了,我也準備從如今開始寫符合規範的提交信息,那以前項目中的不符合規範的信息怎麼辦呢?其實以前的提交信息也是有辦法修改的,關鍵就是使用git rebase
命令。此外還有一個問題就是我寫了不少條提交信息,但其實都是乾的同一件事(這很常見,好比說要修復某個bug,改好以後發現這個bug在其餘設備上並無改好,因而又改好以後提交了一次,但都是改的同一個問題),有辦法將他們合併成一條嗎?其實有的,關鍵也在於git rebase
。是否是瞬間感受這個命令很強大,想要火燒眉毛的試一下呢。
好吧,不賣關子了,直接來到正題,如何使用git rebase
進行變基操做。先說一下若是直接使用git rebase [target-branch]
,雖然能夠幫咱們在本分支基於目標分支進行變基操做,但它只會讓兩個分支的全部提交連成一條線,不能修改具體提交信息,因此須要使用git rebase -i
進行交互式操做。首先了解一下git rebase -i
的幾個操做。
另外還要知道git rebase --continue
命令用來告訴git繼續進行合併,通常在解決衝突後使用(通常來說若是全部的提交沒有衝突的話,能夠一直走下去,直到出現成功的提示。但若是兩次提交合並涉及到衝突,就須要解決衝突並在git add後使用該命令繼續走下去);git rebase --abort
命令用來終止變基操做,通常在不想進行rebase時使用(大部分時間是由於玩脫了,rebase到了一種不可預知的地步,不知道幹啥了,就只能終止了)。直到出現相似「Successfully rebased and updated refs/heads/dev.」的消息就說明本次變基成功了。下面具體講一下如何修改和美化提交信息。
修改提交信息能夠分爲如下幾個步驟,固然具體步驟還要具體狀況具體分析,有些能夠忽略。
暫存工做狀態:使用git stash
將當前工做狀態進行暫存,若是沒有須要暫存的工做狀態請忽略
轉到須要修改的提交上:使用git rebase -i [commit-id]^
將 HEAD 移動到須要修改的 commit-id 上,由於該[commit-id]也在修改之列,因此要基於它的上一個提交變基,或者直接使用[commit-id]^
將pick修改成想要進行的操做命令:進行編輯時通常要把全部須要修改的commit的pick改成r或e,提示:若是多的話可使用vim的全局替換
從新編輯commit信息
附加修改:若是須要在該分支上修改其餘內容,能夠在改動文件後直接使用git add
將其添加到暫存區,並使用 git commit –amend
追加改動到提交。若是不須要請忽略
繼續進行變基操做:使用git rebase –continue
繼續,通常若是提交沒有衝突或者附加修改的話,不須要手動輸入,git會自動進行下一次操做。
變基完成:重複以上步驟,直到出現變基成功文字。若是暫存過工做狀態,須要使用git stash pop
恢復以前的工做狀態,結束。
咱們大多數時候只會使用git merge
來將開發代碼合併到主分支。不過這樣會致使主分支包含咱們合併分支的全部提交信息。久而久之,會讓commit history愈來愈繁雜,但其實合併的代碼徹底能夠用一條提交信息好比feat: 添加某項新特性
來代替。固然還有其餘緣由會致使commit信息重複且沒必要要。好比當咱們在拿不許bug到底有沒有修復成果時可能會屢次改動代碼進行測試,若是每次改動測試都進行了提交,就會產生屢次commit信息,若是不對這些commit作合併的話,在咱們將代碼合併到開發主分支時就會產生不少繁雜的commit信息,不過因爲這些commit信息都是爲解決一樣的事情服務的,徹底能夠合到一塊兒。因此若是你嫌棄commit信息太囉嗦冗餘,那麼就須要對其進行美化了。美化主要有兩種方式:git rebase
和 git merge --squash
,這裏將介紹它們的使用,並進行對比。
rebase merge
注意:若是要合併的commit歷史過多而且對git rebase命令使用不熟練,請慎用,否則可能會被各類衝突逼瘋的!
若是咱們想將dev的提交信息進行美化併合併到master分支,能夠進行以下操做:
git checkout dev
git rebase -i master
,其中具體的操做請參見上一小節,不一樣的不過是把一些提交的pick改成s而已,即提交信息合併到上一次提交。git checkout master
git merge dev
squash merge
一樣以dev分支合併到master爲例:
git checkout master
git merge --squash dev
兩種方式都能實現提交信息的合併,保持 master 分支幹淨整潔。不過它們也有很大不一樣。
squash merge會把分支的全部提交一股腦應用在目標分支上,而後你要修改衝突後再提交,生成一次commit,而此次commit包含dev的全部改動。產生的此次新的commit的做者天然也就是作了本次將dev分支合併到master分支操做的人,他可能並非原dev分支的做者,也就掩蓋了真實的做者。此外不管改動有多少,只會產生一次提交記錄。
rebase merge擁有更高的可操做性,它容許咱們能夠選擇保留幾條有意義的提交,而不像squash merge只有一條。固然有更高的操做自由度也意味着步驟比較繁瑣,不像squash merge簡單方便。固然變基操做完成後在主分支上使用git merge
進行合併,原做者信息會獲得保留。
總之,若是你想要更快進行提交合並,能容忍原做者信息丟失的話,能夠選擇squash merge;若是你想要保留多條有意義的提交信息和須要保留原做者信息的話,rebase merge可能要更適合你。
話說回來,其實git rebase
命令很是強大,不只能作本分支commit信息的修改、合併,還能使用git rebase [startpoint] [endpoint] --onto [branchName]
將本分支的某一段commit集合合併到另外一分支。嗯,git非常博大精深呢,要想達到精通,我還有很長一段路要走!