Git Hooksとは?コミット時の自動チェックを設定する方法を初心者向けに解説
Git Hooksの基本的な使い方と実践的な活用方法を初心者向けに解説します。コミットやプッシュ時にリント、テスト、フォーマットを自動実行する設定方法を学び、コード品質を維持するワークフローを構築しましょう。
Git Hooksとは
Git Hooksは、Gitの特定のアクション(コミット、プッシュなど)の前後に自動的にスクリプトを実行する仕組みです。「フック」という名前の通り、Gitの処理に「引っ掛けて」任意の処理を実行できます。
たとえば、コミット前にコードのフォーマットをチェックしたり、プッシュ前にテストを実行したりすることで、問題のあるコードがリポジトリに混入するのを防げます。
Hooksの仕組み
Git Hooksは、リポジトリの.git/hooksディレクトリに配置されたスクリプトファイルです。ファイル名が特定の名前(例:pre-commit)になっていると、対応するGitのアクション時に自動実行されます。
Hooksは終了コードで成否を判断します。終了コード0なら成功、それ以外なら失敗としてGitの処理を中止します(一部のHooksを除く)。
主要なGit Hooks
Gitには多くのHooksがありますが、実務でよく使われるものを紹介します。
クライアントサイドHooks
開発者のローカル環境で実行されるHooksです。
| Hook名 | タイミング | 用途 |
|---|---|---|
pre-commit | コミット作成前 | リント、フォーマットチェック |
prepare-commit-msg | コミットメッセージ編集前 | メッセージのテンプレート挿入 |
commit-msg | コミットメッセージ入力後 | メッセージ形式のチェック |
pre-push | プッシュ前 | テスト実行、ブランチ名チェック |
post-commit | コミット作成後 | 通知、ログ記録 |
サーバーサイドHooks
リモートリポジトリ側で実行されるHooksです。
| Hook名 | タイミング | 用途 |
|---|---|---|
pre-receive | プッシュ受信前 | アクセス制御、ポリシーチェック |
post-receive | プッシュ受信後 | CI/CDトリガー、通知 |
Hooksの基本的な設定方法
Hookファイルの作成
.git/hooksディレクトリにスクリプトファイルを作成します。新しいリポジトリには、サンプルファイル(.sample拡張子付き)が用意されています。
# .git/hooksディレクトリの内容を確認
ls .git/hooks/
# applypatch-msg.sample pre-commit.sample pre-push.sample ...
Hookを有効にするには、.sample拡張子を削除し、実行権限を付与します。
# pre-commitフックを有効化
cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
簡単なpre-commit Hookの例
コミット前にESLintを実行するpre-commit Hookの例です。
#!/bin/sh
# .git/hooks/pre-commit
echo "Running ESLint..."
npm run lint
# ESLintが失敗したらコミットを中止
if [ $? -ne 0 ]; then
echo "ESLint failed. Please fix the errors before committing."
exit 1
fi
exit 0
このスクリプトは、npm run lintが失敗(終了コード0以外)した場合にコミットを中止します。
commit-msg Hookの例
コミットメッセージの形式をチェックするcommit-msg Hookの例です。
#!/bin/sh
# .git/hooks/commit-msg
commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")
# コミットメッセージが空でないかチェック
if [ -z "$commit_msg" ]; then
echo "Error: Commit message cannot be empty."
exit 1
fi
# メッセージが特定のプレフィックスで始まるかチェック
if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore):"; then
echo "Error: Commit message must start with: feat:, fix:, docs:, style:, refactor:, test:, or chore:"
echo "Example: feat: add login feature"
exit 1
fi
exit 0
huskyを使ったHooksの管理
.git/hooksディレクトリはGitで追跡されないため、チームで共有するにはhuskyなどのツールを使うのが一般的です。
huskyのセットアップ
# huskyをインストール
npm install --save-dev husky
# huskyを初期化
npx husky init
これにより、.huskyディレクトリが作成され、package.jsonにprepareスクリプトが追加されます。
pre-commit Hookの追加
# pre-commit hookを作成
echo "npm run lint" > .husky/pre-commit
.husky/pre-commitファイルの内容:
npm run lint
huskyで作成したHooksは.huskyディレクトリに保存され、Gitで追跡できるためチーム全員で共有できます。
lint-stagedとの組み合わせ
lint-stagedを使うと、ステージングされたファイルのみを対象にリントを実行できます。すべてのファイルをチェックするよりも高速です。
# lint-stagedをインストール
npm install --save-dev lint-staged
package.jsonに設定を追加します。
{
"lint-staged": {
"*.{js,ts,jsx,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{css,scss}": [
"prettier --write"
]
}
}
.husky/pre-commitを更新します。
npx lint-staged
この設定により、コミット時にステージングされたファイルのみが自動でリントとフォーマットされます。
実践的な活用シーン
ブランチ名のチェック
特定の命名規則に従っていないブランチからのプッシュを防ぐpre-push Hookの例です。
#!/bin/sh
# .husky/pre-push
branch=$(git rev-parse --abbrev-ref HEAD)
# mainへの直接プッシュを禁止
if [ "$branch" = "main" ]; then
echo "Error: Direct push to main is not allowed."
echo "Please create a feature branch and open a pull request."
exit 1
fi
# ブランチ名の形式をチェック
if ! echo "$branch" | grep -qE "^(feature|fix|hotfix|release)/"; then
echo "Error: Branch name must start with feature/, fix/, hotfix/, or release/"
echo "Current branch: $branch"
exit 1
fi
exit 0
テストの自動実行
プッシュ前にテストを実行して、失敗したらプッシュを中止します。
#!/bin/sh
# .husky/pre-push
echo "Running tests before push..."
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Push aborted."
exit 1
fi
exit 0
コミットメッセージにチケット番号を追加
ブランチ名からチケット番号を取得し、コミットメッセージに自動挿入する例です。
#!/bin/sh
# .husky/prepare-commit-msg
commit_msg_file=$1
branch=$(git rev-parse --abbrev-ref HEAD)
# ブランチ名からチケット番号を抽出(例: feature/TICKET-123-add-login)
ticket=$(echo "$branch" | grep -oE "[A-Z]+-[0-9]+")
if [ -n "$ticket" ]; then
# メッセージの先頭にチケット番号を追加
sed -i.bak "1s/^/[$ticket] /" "$commit_msg_file"
fi
初心者が混乱しやすいポイント
Hooksはデフォルトで共有されない
.git/hooksディレクトリはGitの追跡対象外です。そのため、チームで同じHooksを使いたい場合はhuskyのようなツールを使うか、スクリプトを別の場所に置いて手動でシンボリックリンクを作成する必要があります。
# .git/hooksを別の場所に変更する方法
git config core.hooksPath ./scripts/hooks
Hooksはスキップできる
--no-verifyオプションを使うと、pre-commitとcommit-msg Hooksをスキップできます。
git commit --no-verify -m "緊急修正"
これは緊急時には便利ですが、乱用するとHooksを設定した意味がなくなります。--no-verifyの使用はログに残らないため、CI/CDでの最終チェックも重要です。
Hookの失敗でGit操作が中止されるもの・されないもの
すべてのHooksがGit操作を中止できるわけではありません。
| Hook | 失敗時の動作 |
|---|---|
| pre-commit | コミット中止 |
| commit-msg | コミット中止 |
| pre-push | プッシュ中止 |
| post-commit | 処理は継続(通知目的のため) |
| post-merge | 処理は継続 |
post-*系のHooksは処理完了後に実行されるため、失敗しても元の操作は取り消されません。
Hooksのデバッグ方法
Hooksがうまく動作しない場合のデバッグ方法です。
手動でHookを実行
Hookファイルを直接実行して動作を確認できます。
# pre-commit hookを手動実行
.git/hooks/pre-commit
デバッグ出力を追加
スクリプトにset -xを追加すると、実行されるコマンドが表示されます。
#!/bin/sh
set -x # デバッグモード
npm run lint
Hookの実行パスを確認
Hookがどこから実行されているか確認するには、作業ディレクトリを出力します。
#!/bin/sh
echo "Current directory: $(pwd)"
echo "Hook script: $0"
まとめ
Git Hooksは、Gitの操作に連動して自動的にスクリプトを実行する仕組みです。
| Hook | 用途 |
|---|---|
| pre-commit | コミット前のリント・フォーマット |
| commit-msg | コミットメッセージの形式チェック |
| pre-push | プッシュ前のテスト実行 |
| prepare-commit-msg | メッセージテンプレートの挿入 |
チームでHooksを共有するにはhuskyを使い、lint-stagedと組み合わせることでステージングされたファイルのみを効率的にチェックできます。
Hooksを活用することで、問題のあるコードがリポジトリに混入するのを防ぎ、コード品質を維持するワークフローを構築できます。ただし、--no-verifyでスキップできるため、CI/CDでの最終チェックも忘れずに設定しましょう。
編集部