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での最終チェックも忘れずに設定しましょう。

編集部

編集部