最近我在一個(gè)咨詢項(xiàng)目上,為了幫助客戶規(guī)范代碼提交信息,我們決定建議客戶使用 git hooks來實(shí)現(xiàn)。
為此,我們大概了解了一下 Git Hooks的相關(guān)知識(shí)。
一、初識(shí) Git Hooks
Git Hooks 是什么?
Git Hooks 是一系列腳本, Git 允許 Git 倉庫在觸發(fā)某些事件時(shí),去執(zhí)行這些腳本。 我們可以通過 Git Hooks 在開發(fā)生命周期的關(guān)鍵節(jié)點(diǎn),觸發(fā)自定義的操作。
這里的腳本可以是任何正確命名的可執(zhí)行腳本。由此可見,Git Hooks 的靈活性還是很強(qiáng)的。
在一個(gè) Git 倉庫中,我們可以在.git/hooks中發(fā)現(xiàn)一系列的文件:
applypatch-msg.sample pre-push.sample
commit-msg.sample pre-rebase.sample
fsmonitor-watchman.sample pre-receive.sample
post-update.sample prepare-commit-msg.sample
pre-applypatch.sample update.sample
pre-commit.sample
這些文件都是不同的 Git Hooks 腳本, 當(dāng)然,他們現(xiàn)在并不會(huì)執(zhí)行。
Git 能識(shí)別的文件名是不帶sample的,所以當(dāng)我們把上述文件的sample后綴去掉時(shí),這些腳本才是有效的。
另外,因?yàn)樯鲜鑫募际强蓤?zhí)行的腳本,所以在執(zhí)行時(shí),要注意給與其相應(yīng)的權(quán)限。
二、Git Hooks的版本化
我們?cè)谏厦婵吹?Git Hooks 的相關(guān)腳本存放在 .git/hooks 目錄下。但該目錄并不被版本化,提交到遠(yuǎn)端。
但是對(duì)于團(tuán)隊(duì)來說,保證其可版本化是非常重要的功能,否則配置新腳本,或者對(duì)已有的腳本進(jìn)行變化,都是一項(xiàng)浩大的工程。
經(jīng)過簡單的調(diào)研發(fā)現(xiàn),無論是Java相關(guān)的技術(shù)棧,還是JS相關(guān)的技術(shù)棧,都有比較成熟的方案。
Java 相關(guān)的技術(shù)棧,比如 Spring 項(xiàng)目,Android項(xiàng)目等,可以使用Gradle的相關(guān)配置功能,將配置腳本納入版本管理中,在gradle build時(shí),再拷貝到 .git\hooks 目錄中。 具體實(shí)現(xiàn)步驟,大家可以參見騰云的這個(gè)項(xiàng)目。
Js 相關(guān)的技術(shù)棧,可以直接在Npm中使用使用 husky 來完成對(duì)應(yīng)的配置。他依賴Npm的安裝和配置,具體的使用方式可以參見他的項(xiàng)目主頁。
以上兩種方式,都需要依靠特定的依賴管理工具才能完成。那依賴管理工具不支持,或者沒有依賴管理工具,還想完成版本化管理 Git hooks, 怎么辦呢?
Git hooks 的腳本文件都存放在.git\hooks目錄下,但是該目錄并不能被納入版本管理中。經(jīng)過查閱 git 的文檔可知, 在17年 Git 提供了一項(xiàng)配置: core.hooksPath,通過它,可以將指定某個(gè)目錄存放 git hook 的相關(guān)腳本。同時(shí),該目錄也可以納入版本管理。
執(zhí)行命令如下:
git config --add core.hookspath .mygithooks
執(zhí)行該命令后,在提交時(shí),會(huì)自動(dòng)觸發(fā).mygithooks 目錄下的腳本。
三、Git Hooks 的分類
Git Hooks 分兩種,客戶端鉤子 、服務(wù)端鉤子。 客戶端鉤子會(huì)被 commit,merge,rebase 等操作觸發(fā),而服務(wù)端會(huì)被push等操作觸發(fā)。
1. 客戶端鉤子
客戶端鉤子 分為三種,提交工作流,電子郵件流,和其他。我們這里只討論提交工作流和其他部分。
在 Git Hooks 中,若腳本以 exit 0退出應(yīng)用,則流程會(huì)繼續(xù)進(jìn)行。若腳本以非0值退出,則流程會(huì)中斷不會(huì)繼續(xù)進(jìn)行。
下圖是提交工作流鉤子的生命周期:

pre commit 不帶任何參數(shù),可以通過git diff 的形式獲取變更的代碼。一般用來檢查即將提交的代碼是否有效,比如是否能通過編譯,是否能通過測(cè)試等。
prepare commit msg 帶三個(gè)參數(shù), 當(dāng)前提交信息的文件路徑,當(dāng)前的提交類型,以及提交的SHA-1校驗(yàn)。 一般用來做提交信息的格式化,合并,整理等。
commit msg 帶一個(gè)參數(shù),即當(dāng)前提交信息的文件路徑。 一般用來檢查提交信息格式等。
post commit 不帶任何參數(shù)。 一般用于提交后發(fā)送通知等。
其他工作流包括pre-rebase, post-rewrite, post-merge, pre-push 這些在我們?nèi)粘i_發(fā)過程中用的比較少, 這里就不做介紹了。
使用git commit --no-verify 可以越過和commit相關(guān)的鉤子,直接提交。
2. 服務(wù)端鉤子
服務(wù)端的鉤子在持續(xù)集成中,應(yīng)用是很廣泛的。
pre-receive 會(huì)處理來自客戶端的推送。 常見的就是提交后觸發(fā)CI,由CI返回結(jié)果來判斷能否合入。
update 也會(huì)處理來自客戶端的推送,區(qū)別是可以按照分支進(jìn)行處理。 如果客戶端同時(shí)推送了多個(gè)分支,僅有失敗的分支會(huì)被拒絕。成功的分支仍會(huì)被更新。update 帶三個(gè)參數(shù),分別是引用分支的名字,推送前的SHA-1值, 需要更新的SHA-1值。
post-receive 在整個(gè)過程完結(jié)以后運(yùn)行。 大家最熟悉的就是提交后觸發(fā)測(cè)試,打包,部署等流程。
四、 Git Hooks的使用場(chǎng)景
Git Hooks 的本質(zhì)是觸發(fā)器,可以在 Git 的生命周期的某些階段觸發(fā)開發(fā)人員預(yù)先編寫好的腳本。 所以其使用場(chǎng)景并不受限。
下面僅介紹一些我們?cè)陂_發(fā)過程中可能會(huì)用到的場(chǎng)景。
-
客戶端鉤子
因?yàn)榭蛻舳算^子僅在本地起作用,想要做出強(qiáng)制性的約束,還是只能依賴服務(wù)端的流程。
所以客戶端的鉤子一般多用來做提示性,或觸發(fā)一些重復(fù)手動(dòng)工作。
所以我們可以在客戶端鉤子中檢查:
暫存區(qū)是否還有未提交的代碼。
檢查或者直接格式化提交信息。
代碼是否能夠編譯,是否能通過lint,是否能通過單元測(cè)試,是否達(dá)到單元測(cè)試覆蓋率等等指標(biāo)。
在Push 時(shí)自動(dòng)升級(jí)版本。
在將多個(gè)提交合并成一個(gè)提交時(shí),按照格式整理提交信息等。
-
服務(wù)端鉤子
服務(wù)端的鉤子主要的作用有兩個(gè):一個(gè)是決定是否接受當(dāng)前推送(
pre-receive,update),另外一個(gè)是在接受推送后(post-receive), 觸發(fā)其他操作等。服務(wù)端的鉤子的使用場(chǎng)景,大家就很熟悉了,通過各種自動(dòng)化的方式來對(duì)代碼質(zhì)量進(jìn)行審查,來決定代碼是否能夠合入云端。如果不能合入,給出相應(yīng)的提示。以及合入后,觸發(fā)各種各種自動(dòng)化的流程進(jìn)行CICD流程。
推薦閱讀: