---
tags: git, ci
title: 探索藏在Git當中的Git Hook
description: 研究Git Hook在CI的應用
author: Frank
---
# 探索藏在Git當中的Git Hook
[](https://gitlab.com/s716134/githook-demo/-/commits/githook/)
如果希望在git生命週期中,在本機增加一些CI[^CI]任務的話,或許可以不必特地架一台CI Server[^CI_SERVER]去處理,只需要使用到本篇的主角「Git Hook」,就可以達到基本的CI任務了。
[TOC]
## 1. 什麼是Git Hook?
Git提供的一組客製化條件觸發腳本的規則,統稱這個規則為Git Hook。這些規則適用於Git的幾個重要操作環節,例如: 提交(commit)、合併(merge)等[^CUSTOMIZING_GIT]。
* 提交流程觸發(Committing-Workflow Hooks)
* 郵件流程觸發(Email Workflow Hooks)
* 其它客戶端觸發(Other Client Hooks)
* 伺服器端觸發(Server-Side Hooks)
:::warning
:warning: 為了聚焦在本文所探討的議題上,所以僅會介紹幾個基本的Committing的自定義Git Hook。
:::
## 2. 為什麼需要Git Hook?
在講求快速開發的時代,軟體提交的數量也隨之大量增加。若還是寄望所有的CI工作都交由CI Server來處理,那很快就會面臨到機器效能不足的嚴峻議題。另一方面,組織也希望減少在CI上的成本,同時又期許軟體產出的品質提升。事實上,我認為「**許多的CI工作,是可以分派給開發人員自己負責,而不是完全將其推卸給其它人或機器**」。Git Hook則是我認為目前在現代開發模式中,最適合拿來利用、推廣的一項工具。
## 3. 哪些人會需要使用到Git Hook?
* **開發人員**
正如先前所提到,使用Git Hook來達到基礎的CI任務,可以讓開發人員可以對自己交付的Source Code負責任。除此之外,只要定義好處發腳本的場景,開發人員並不需要額外再特別去學一套新的工具、語言。
* **測試人員**
測試人員如果可以定義好哪些Git場景(commit、push...)去觸發執行哪些測試工具、案例(JUnit、JMeter...),相信不僅方便留存測試紀錄,並且可以節省許多繁複的操作。
:::info
:bulb: Git Hook腳本可以使用任何程式語言去編寫,常見的例如: Shell、Ruby、Perl或Python等。
:::
## 4. 我該在哪裡使用Git Hook?
一般來說,Git在初始化專案的時候,會自動產生一組Git Hook範例腳本,並將其存放在`.git/hooks/`底下。~~幸運的話~~可以在`.git/hooks/`底下看到下面這些範例Git Hook腳本:
```bash=
applypatch-msg.sample pre-applypatch.sample pre-rebase.sample
commit-msg.sample pre-commit.sample pre-receive.sample
fsmonitor-watchman.sample pre-merge-commit.sample prepare-commit-msg.sample
post-update.sample pre-push.sample update.sample
```
對Git而言,僅會認hooks底下符合這些命名規範的檔案名稱,且不需要任何副檔名。所以這些腳本預設是關閉的,如果要啟用它們,**僅需要重新命名將`.sample`移除即可**。如果要固定使用同一組Git Hook腳本,可以透過`core.hooksPath`來指向執行Git Hook的目錄[^SHARE]。
:::info
:bulb: 只有最開始使用`git init`初始化的資料夾當中,會出現這些範例Git Hook腳本。這其實是因為`.git/`內的檔案,並不會被Git主動納入版本控管。
:::
## 5. 哪些時機可以觸發Git Hook?
### 5.1 使用`pre-commit`讓顯示訊息一致
:::danger
**案例描述 :**
因為會在不同的電腦、工具中,對相同專案使用Commit紀錄修改,有時候會忘記自己使用哪個名字、信箱,所以時常導致在同一個專案出現多名角色。

:::
:::success
**解決方法[^CASE1] :**
利用簡單的Shell Script判斷使用者資訊,沒有設定好就不給Commit。
```bash=
#!/bin/sh
username=$(git config user.name)
if [ "$username" != "LAI, CHUN JING" ]
then
cat <<\EOF
[ERROR] Are you LAI, CHUN JING?"
EOF
exit 1
fi
```
:::
### 5.2 使用`pre-push`觸發執行回歸測試
:::danger
**案例描述 :**
因為專案的急迫性,有時候開發完新功能或是修改完BUG後,就會急急忙忙地將程式推上版。但是,若是組織沒有一個完善的CI Server,且自己也忘記在推版前執行測試案例,去確認原有功能在修改後是否保持完整,那很可能就會不小心推了一版「改A壞B」的程式上去。

:::
:::success
**解決方法[^CASE2] :**
```bash=
#!/bin/sh
echo "Running mvn clean test for errors"
DIR=`pwd`
CWD="$DIR/app_example"
MAIN_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd $CWD
MVN_RESULT=$(mvn clean test 2>&1)
if [ $? -ne 0 ]; then
echo
echo "${MVN_RESULT}" | ((tee /dev/fd/5 | grep -A 10 -B 2 "Reactor Summary:" >/dev/fd/4) 5>&1 | sed -n -e '/^Failed tests:/,/Tests run:.*$/ p' ) 4>&1
echo
echo "Error while testing the code"
cd $CWD
exit 1
fi
```
:::
<!-- ### 5.2 使用`pre-push`或`post-merge`觸發Docker打包Image
:::danger
**案例描述 :**
:::
:::success
**解決方法[^CASE3] :**
```bash=
:::
-->
:::warning
:warning: 實際上觸發Git Hook不止這些時機[^TASKS][^TASKS2],這裡僅是把個人比較熟悉的場景列上來。
:::
## 6. 故障排除
### 6.1 hook was ignored because it's not set as executable
若是出現這個訊息,代表commiter並沒有執行`.git/hooks/`的權限,這時就可以使用`chmod ug+x .git/hooks/*`這行指令,給予使用者及群組擁有`執行(x)`的權限。
[^CI]: [What is Continuous Integration?](https://www.atlassian.com/continuous-delivery/continuous-integration)
[^CI_SERVER]: [為什麼我們需要CI Server?](https://ithelp.ithome.com.tw/articles/10102375)
[^CUSTOMIZING_GIT]: [Customizing Git - Git Hooks](https://git-scm.com/book/zh-tw/v2/Customizing-Git-Git-Hooks)
[^SHARE]: [How to Share Git Hooks with the Team](https://chamikakasun.medium.com/how-to-share-git-hooks-with-the-team-37424603dd91)
[^CASE1]: 不可考,突然找不到來源,待補上
[^CASE2]: [mallocator/git.hook.mvn_clean](https://gist.github.com/mallocator/34332f7a6a68d15a419c)
[^CASE3]: [Using a post-merge git hook to clean up old branches](https://www.liquidlight.co.uk/blog/using-a-post-merge-git-hook-to-clean-up-old-branches/)
[^TASKS]: [Understanding Git Hooks](https://codeburst.io/understanding-git-hooks-in-the-easiest-way-bad9afcbb1b3)
[^TASKS2]: [What hooks are supported by GitKraken?](https://support.gitkraken.com/working-with-repositories/githooks/)