一、git hook
和其它版本控制系統一樣,Git 能在特定的重要動作發生時觸發自定義腳本,有兩組這樣的鉤子:客戶端鉤子和服務器鉤子,客戶端鉤子由諸如提交和合并這樣的操作所呼叫,而服務器端鉤子作用于諸如接收被推送的提交這樣的聯網操作,
鉤子都被存盤在 Git 目錄下的 hooks 子目錄中, 也即絕大部分專案中的 .git/hooks,默認存在的都是示例,其名字都是以 .sample 結尾,如果你想啟用它們,得先移除這個后綴,把一個正確命名且可執行的檔案放入 Git 目錄下的 hooks 子目錄中,即可激活該鉤子腳本,這樣一來,它就能被 Git 呼叫,
關于各種詳細的 hook 型別可以參考官方檔案 《自定義 Git - Git 鉤子》,
了解了這些 hook 鉤子,你就可以真的為所欲為了,你可以用來檢查訊息、檢查代碼,可以用來觸發任意流程,譬如自動規范檢查等等,只能說想象空間巨大無比,
二、commit msg 格式自動檢查
雖然有很多現成的 hook 可用,但是這里還是給出一個簡單的例子演示下,這里實作一個提交 message 格式的簡單檢查,要求提交訊息單行且分兩部分,且有一定的字數限制,
2.1 撰寫 commit-msg 腳本
#!/usr/bin/env python
# coding=utf-8
#
# commit msg check
import sys
import re
import io
if hasattr(sys.stdout, 'buffer'):
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
TIPS_INFO = '''
不符合commit規范,提交失敗(當前狀態等于沒做剛剛的commit操作)!
commit規范:
型別 詳細訊息
規范樣例:
git commit -m "xxxxx xxxxxxxxxxxxx"
!!!!提交失敗!!!!
'''
def check_commit_line1_format(msg):
regOther = r'\S{5,} (.){10,100}'
matchObj = re.match(regOther, msg)
return matchObj
if __name__=="__main__":
with open(sys.argv[1], 'r') as f:
for line in f:
if (check_commit_line1_format(line)):
sys.exit(0)
else:
print(TIPS_INFO)
sys.exit(1)
既然撰寫好了 commit hook 腳本,那新問題就來了,本地 hook 在專案的 .git 目錄下,.git 目錄又不受版本控制,所以在團隊推廣時,你不可能讓大家主動去放這個檔案,一方面可能會放錯,另一方面可能有些人壓根就不放,愛理不理,故而需要將這件事做成自動的,所以選擇在編譯專案時為拷貝切入點(因為你拽下來的專案一定會編譯的),對于 android 專案來說,我們可以使用 gradle 撰寫一個小任務來做這件事,具體如下:
/**
* git-hook-copy.gradle 檔案
*
* 本地專案 git hook 自動拷貝腳本
* 用法:
* apply from: 'git-hook-copy.gradle'
*/
/**
* 緊急開關
*/
def forbid = false
project.afterEvaluate {
if (forbid) {
preBuild.dependsOn 'resetGitHookConfig'
} else {
preBuild.dependsOn 'prepareGitHookConfig'
}
}
task prepareGitHookConfig(type: Copy) {
from getConfigFile()
into getGitHookDir()
}
task resetGitHookConfig {
doFirst {
File file = getGitHookFile('commit-msg')
if (file != null) {
file.delete()
}
}
}
def getGitHookFile(fileName) {
def dirPath = getGitHookDir()
if (dirPath != null && dirPath.length() > 0) {
def file = new File(dirPath, fileName)
if (file.exists()) {
return file
}
}
return null
}
def getConfigFile() {
File configFile = new File(project.rootDir, "git-hook/commit-msg")
if (configFile.exists()) {
return configFile.absolutePath
}
return null
}
def getGitHookDir() {
File gitHookDir = new File(project.rootDir, ".git/hooks/")
if (!gitHookDir.exists()) {
println("Your project can't find .git directory in the ${project.rootDir.absolutePath}," +
" please ensure it have been tracked by git VCS!")
return null
}
return gitHookDir.absolutePath
}
上面腳本有兩個任務,一個 reset,一個 config,reset 會將 .git hook 目錄下的規則刪掉,等于沒有規則;config 是把專案根目錄下 git-hook 目錄下的 commit-msg hook 腳本復制到 .git hook 目錄下,這里不用判斷是否已經存在檔案,直接覆寫即可,因為 gradle task 天生支持 UPDATE 機制,而且我們需要在修改 commit-msg 檔案后自動覆寫,所以不建議判斷 .git hook 下是否已經存在,
至此,我們可以進行一波操作了,譬如在命令列提交代碼,你會看到如下提示:

修復 log 格式后再進行 commit 即可,
三、總結
上面簡單介紹和實戰了一個小的 git hook 操作,感興趣你可以無限想象,和你的 checkstyle 什么的,各種檢查什么的結合起來都可以,反正師傅領進門,修行靠自己,需求靠團隊,
參考鏈接:
- git 的 hook 操作強大到難以置信,效率為王!
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/2433.html
標籤:其他
