什麼是 Git Hooks?為什麼它這麼萬能?

前幾天在工作上看到別的組用了一個叫做 git hooks 的東西,在這之前我完全沒有聽過這個工具。

所以我就花一點時間查了一下 git hooks 可以做什麼,才發現原來他的功能這麼強大!

那今天我們就來看看這工具到底是什麼吧!

什麼是 Git Hooks

第一件事當然就是要來了解什麼是 git hooks 啦!

簡單來說,它就是一個自動化腳本,每當特定的事件發生時,git 就會執行這些腳本。我們則可以藉由編輯這些腳本做到幾乎各種事情!

比如說,我們可以用這些腳本執行重複的任務、提升程式碼品質、自動測試等等。

我們可以在 .git/hooks 這個資料夾中找到這些腳本。

基本上,git hooks 分為兩種:

  1. local 本地端
  2. server 私服器端
local 本地端

本地端的 hooks 著重於和使用者互動,基本上在做 git commit 和 git merge 時會進行。

server 私服器端

私服器端的 hooks 則著重於網路操作上的互動,基本上是在收到使用者推送的 git push 時會進行。

使用 Git Hooks

那麼我們該如何開始使用 git hooks 呢?

前面說到,這些 hooks 儲存在 .git/hooks 的資料夾底下,這個資料夾是每個 git repository 都會有的。

在一開始做 git init 時,git 就會自動幫我們創建好這個資料夾,並連到創建一系列 hooks 作為範例。

我們可以在這個資料夾底下看到類似這樣的文件:

pre-push.sample
pre-rebase.sample
commit-msg.sample
post-update.sample
prepare-commit-msg.sample
pre-commit.sample

這些 .sample 結尾的文件,都是一個 git hook,並會在特定事件發生時才會執行。

比如說,pre-commit 的 hook 就會在使用者執行 git commit 之前被執行。

想要讓這些 hook 發揮功能,我們就必須先移掉 .sample,由 pre-commit.sample 變成 pre-commit,這樣這個 hook 才會發揮功能。

接下來,我們來講一些常被使用到的 git hooks!

常見的 git hooks

這邊我們主要來講本地端的 hooks,因為這和作為開發者的我們最為相關。

我們會來介紹三個常見的 hooks,分別是:

  • pre-commit
  • commit-msg
  • pre-push

pre-commit

這是一個非常常用的 hook,因為這類的 hook 可以在使用者在 commit 時做一些檢查。

這些檢查包括自動格式化程式碼、自動測試等等。

接下來我們來看一個超級簡單的例子,這個例子讓我們在 commit 的時候輸出一則訊息。

編寫 pre-commit

首先,我們要來編寫 pre-commit 這個檔案

cd .git/hooks
vim pre-commit

接著,我們複製貼上以下程式碼:

#!/bin/bash

echo "This is a pre-commit hook!"
轉為執行檔

讓這個 pre-commit hook 成為可以被執行的執行檔

chmod +x pre-commit

這樣就完成啦!我們接著可以自己試著做出一個 commit,並看看是否有訊息印出。

當然,我們可以看到類似這樣的訊息。

This is a pre-commit hook!
On branch main
nothing to commit, working tree clean

commit-msg

這個 hook 常常被用來統一 commit message 的格式。

如果公司或是團隊對於每個人提交的 commit 訊息有特定的要求,比如說字數、大小寫開頭等等,那就可以使用這個 hook。

編寫 commit-msg

一樣,我們需要編寫一個叫 commit-msg 的 hook。

cd .git/hooks
vim commit-msg

接著,我們來做一個 hook,這個 hook 會要求 commit message 都是大寫開頭,並且至少有 10 個字母。

#!/bin/bash

commit_message=$(cat "$1")

# Check if commit message starts with a capital letter and is at least 10 characters long
if ! [[ $commit_message =~ ^[A-Z] ]] || [[ ${#commit_message} -lt 10 ]]; then
    echo "Error: Commit message must start with a capital letter and be at least 10 characters long."
    exit 1
fi
轉為執行檔

讓這個 commit-msg hook 成為可以被執行的執行檔

chmod +x commit-msg

而當我們給予的 commit message 不符合規範時,比如說:

git commit -m "test"

這樣這個 commit 就不會通過,而會出現以下的錯誤訊息:

Error: Commit message must start with a capital letter and be at least 10 characters long.

pre-push

第三個也是一個常用的 hook,這個 hook 是在我們做 push 時被執行的。

常用的情況比如說,在 push 前進行某些測試,如果測試通過就推送到遠端私服器,否則就取消執行。

編寫 pre-push

這邊我們就直接給上範例的檔案啦!

#!/bin/bash

# Run the test suite
echo "Running tests..."
if ! ./run-tests.sh; then
    echo "Tests failed. Push aborted."
    exit 1
fi

echo "Tests passed. Proceeding with push."

這裡我們看到,它會執行一個叫做 run-tests.sh 的執行檔。

在這個檔案中,我們可以定義測試,或是可以再另外呼叫其他檔案。

唯有這個測試通過了,我們的 push 才會被執行。

git hooks 的作用域

理解了 git hooks 的功能後,我們還要來了解一個重要的觀念。

前面講到的那些 git hooks 的作用域啊,都只是在本地端而已!

一般來說,儲存在 .git/ 資料夾下的檔案,git 都不會推送到遠端的私服器,而因為 git hooks 存放在 .git/ 資料夾,因此你所做的 hooks 只會在你的電腦上運行。

那麼問題來了,如果我們想要在團隊中,強制每個人在做 git commit 之前,都遵守某個特定規則的話,該怎麼辦呢?

這裡有兩個方法:

  1. 統一 git hook
  2. 分享 git hook
統一 git hook

我們可以要求每個人的電腦上的 git hook 文件都長的一樣,這樣一來就可以讓每個人都遵守一樣的規則了。

但這樣的缺點也顯而易見,當某天某個 git hook 需要更改時,每個人的電腦都要進行手動更改。這相當耗時而且容易出錯。

那有沒有更好的方法呢?

分享 git hook

我們可以建立一個 hooks/ 的檔案夾,並讓所有人共用!具體步驟如下:

1. 建立 hooks/ 檔案夾,並將位於 .git/hooks 中的 hooks 拖移到其中。

mkdir hooks

mv .git/hooks/pre-commit hooks/
mv .git/hooks/commit-msg hooks/
mv .git/hooks/post-commit hooks/

2. 設定 git 查看 hooks 的路徑

git config core.hooksPath hooks

如此一來,當其他人在使用這個專案時,就會自動使用位於 hooks/ 檔案夾中的 hooks 執行檔!

是不是比第一種方法既有效率又保險的多!

結尾

這樣我們就介紹完基本的 git hooks 啦!

簡單來說,它就是一個自動化腳本,每當特定的事件發生時,git 就會執行這些腳本。

我們則可以藉由編輯這些腳本做到幾乎各種事情!

但其實,這篇文章指介紹了一半的 hooks,還有另一半作用於 server 端的 hooks 我們沒有介紹。

如果有興趣的人可以去看這篇更詳細的 Atlassian Git hooks 教學文章!

那這篇文章就到這裡啦~

以後我們還會再繼續介紹更多和 git 有關的知識和技術!

有學到東西的話歡迎給五星評價喔!