Git Hook 的種類

Git 在特定操作前後可以執行自定義腳本,位置在 .git/hooks/。最常用的三個:

pre-commitgit commit 執行前跑。適合:格式化(Prettier / Black)、lint(ESLint / flake8)、快速的 unit test。

commit-msg:commit message 寫好後驗證格式。適合:Conventional Commits 規範檢查。

pre-pushgit push 前跑。適合:跑較慢的測試、確保不 push 到受保護的 branch。


本機 Hook 的問題:.git/hooks/ 不進版控

.git/ 目錄不被 git 追蹤,所以每個人要手動安裝 hook。這就需要工具幫忙管理。


Husky(Node.js 生態)

Husky 讓 hook 存在 repo 裡(.husky/ 目錄),npm install 後自動安裝。

npm install --save-dev husky
npx husky init
# .husky/pre-commit
#!/bin/sh
npx lint-staged  # 只 lint 有改動的檔案
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": ["eslint --fix", "prettier --write"],
    "*.py": ["black", "flake8"]
  }
}

lint-staged 是關鍵配對工具:只對 staged 的檔案(你這次 commit 的檔案)跑 lint,而不是整個 codebase。這讓 pre-commit hook 的速度從幾分鐘降到幾秒。


lefthook(多語言,更快)

Lefthook 是 Husky 的替代,用 Go 寫成,速度快,配置在 YAML 裡:

# lefthook.yml
pre-commit:
  parallel: true
  commands:
    eslint:
      glob: "*.{ts,tsx}"
      run: npx eslint --fix {staged_files}
    prettier:
      glob: "*.{ts,tsx,css,json}"
      run: npx prettier --write {staged_files}
 
pre-push:
  commands:
    tests:
      run: npm test

parallel: true 讓多個 hook 並行跑,比 Husky 的串行快很多。支援 Node.js、Go、Ruby、Python 混合 monorepo。


commitlint:Conventional Commits 格式驗證

Conventional Commits 格式:type(scope): description

feat(auth): add OAuth2 login
fix(api): handle null response from payment service
docs: update deployment guide
npm install --save-dev @commitlint/cli @commitlint/config-conventional
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] }
# .husky/commit-msg
#!/bin/sh
npx --no-install commitlint --edit $1

現在提交不符合格式的 commit message 會被擋下來:

$ git commit -m "fix stuff"
⧗ input: fix stuff
✖ subject may not be empty [subject-empty]
✖ type may not be empty [type-empty]

✖ found 2 problems, 0 warnings

pre-commit.com(Python 生態)

pre-commit.com 是一個 hook 框架,有豐富的 plugin 生態(不只是 Python):

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/psf/black
    rev: 23.12.0
    hooks:
      - id: black
  - repo: https://github.com/PyCQA/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: check-json
      - id: detect-private-key  # 防止 secret 進 git
pip install pre-commit
pre-commit install

原則

Git hook 的目的是把規範從「道德要求」變成「系統強制」——不是要懲罰工程師,是讓壞的 commit 在本機就被擋住,而不是到 CI 才發現,節省所有人的時間。

Hook 的反應時間要快——超過 30 秒的 pre-commit hook 工程師就會用 --no-verify 繞過。把慢的工作(完整測試)放到 pre-push 或 CI,pre-commit 只做能在幾秒內完成的事。