使用 Git Hooks 驗證 local commit 資料

  • 5168
  • 0
  • Git
  • 2020-04-07

如果團隊要求在 git commit message 必須要遵守一定的規範時,似乎只能透過默契或事後來達到這件事。透過 git 所提供的 hook,可以在開發者 local commit 的時候就達到驗證目地,不合規範的 commit 不會產生,更別說會推到遠端的分支去,是個相當不錯的功能

什麼是 Git Hooks

想像你的版控工具在做一些動作(比如是 commit 或是 merge)時,你希望當下讓 git 幫你執行客製化的 scripts,而 Git Hooks 又有分 client side (也就是 local 端的),以及 server side,目前這裡只會先介紹 client side 的部份

如何安裝 Git Hooks

當你在對資料夾下 git init 時,其實 Git Hooks 的 sample 都已經被安裝進你的版控裡了,只是尚未啟用而已,路徑就在 [$GIT_DIR]\.git\hooks 裡

有哪些事件可以使用

打開 hooks 資料夾,你就會發現 Git 提供了相當多的事件供你使用

比方 Pre-commit, prepare-commit-msg, Pre-Push, post-update ...這些

你可以選擇你想要的事件複製之後,把 .sample 拿掉,這樣就等於是啟用了,相當容易

我想要在 local side 時就檢查 commit message 是否符合規範,如果不符合就不能寫入 commit

1. 選擇事件
在這裡選用了 prepare-commit-msg 的事件,這個事件提供了幾個參數
a) commit message 的路徑
b) commit type
相關說明可以參考官方文件 https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks
這個事件在自動使用 template 來建立 commit message 上是非常有用的,但在這裡我們並不是這個用途

同時也建立了 pre-commit 來驗證順序

需要注意的是 exit status code 為 0 則告訴 git 執行成功,不為 0 則代表異常

以下是 pre-commit

#!/bin/sh
echo pre commit msg
exit 0

以下是 prepare-commit-msg

#!/bin/sh

echo prepare commit

COMMIT_MSG_FILE=$1

powershell -ExecutionPolicy ByPass -File C:\\github\\myGitUtility\\CheckCommitMsg.ps1 $COMMIT_MSG_FILE

2. 使用 powershell
在這邊我使用了 powershell 來幫我解析 commit 檔案內容並做簡單的處理與判斷是否有 http 的字樣
 

$commitPath = $Args[0]

$raw = Get-Content -Path $commitPath -Raw

if ($raw.Contains("http"))
{
	exit 0
}
else
{
	Write-Host "your content does not contain 'http', commit abort"
	exit 1
}

3. 進行測試

a) 準備一下環境

$ git init
Initialized empty Git repository in C:/github/test/.git/

$ touch hook.txt

$ git add .

$ git status

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   hook.txt

接著把 pre-commit 跟  prepare-commit-msg 複製到 .git\hook 裡

b) 測試 git commit 失敗

首先可以先確認 pre commit 的確是在 prepare commit message 事件之前

再來發現 local commit 會失敗,則會顯示出 powershell 中產生的文字

並下 git status 確認 file 並沒有真的 commit 進去


$ git commit -m 'hello world'
pre commit msg
prepare commit
your content does not contain 'http', commit abort

$ git status

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   hook.txt

$ git log --oneline
fatal: your current branch 'master' does not have any commits yet

c) 測試成功 commit

為了符合規範,我在 commit message 上加了 http 的字樣,果然就成功了

同樣下 git status, git log 確認檔案都有進 commit

$ git commit -m 'hello world http'
pre commit msg
prepare commit
[master (root-commit) c7bb7ea] hello world http
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 hook.txt

$ git status
On branch master
nothing to commit, working tree clean

$ git log --oneline
c7bb7ea (HEAD -> master) hello world http

d) 測試 pre-commit 失敗並不會往下執行 prepare commit message

把 exit status code 改成 1,強制失敗

#!/bin/sh
echo pre commit msg
echo custom force commit fail
exit 1
$ git commit -m 'hello world http'
pre commit msg
custom force commit fail

$ git status
On branch master

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   hook.txt

$ git log --oneline
fatal: your current branch 'master' does not have any commits yet

參考資料
https://git-scm.com/docs/githooks
https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks
https://myapollo.com.tw/zh-tw/git-hook-prepare-commit-msg-get-branch-name/
https://ithelp.ithome.com.tw/articles/10210660