シェルスクリプトでGitフックを設計する方法|pre-commit・pre-pushへのチェック組み込みとチームへの配布パターン

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > シェルスクリプト > シェルスクリプトでGitフックを設計する方法|pre-commit・pre-pushへのチェック組み込みとチームへの配布パターン
「コミット前にshellcheckを実行し忘れた」という経験はないでしょうか。

Gitにはフック(hook)という仕組みがあり、コミットやプッシュの前後にシェルスクリプトを自動実行できます。設定すれば、うっかり構文エラーのあるスクリプトをコミットするミスを手作業なしに防止できます。

この記事では、Git 2.9以降を想定環境として、pre-commitフックとpre-pushフックをシェルスクリプトで実装する方法を解説します。変更ファイルへの自動チェック組み込み、git config core.hooksPathによるチームへの配布設計まで、順番に説明します。

この記事のポイント

・Gitフックはexit 0で処理続行・非0でブロックというルールで動作するシェルスクリプトである
・pre-commitでgit diff --staged --name-onlyを使い変更ファイルにshellcheckを実行できる
・core.hooksPathでバージョン管理対象のディレクトリへ向けるとチームへ配布できる
・shellcheckが見つからない環境ではexit 0でスキップする設計にするのが実務上の鉄則


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Gitフックとシェルスクリプトの関係

Gitフックは、コミット・プッシュ・マージといったGitのイベントの前後に自動実行されるシェルスクリプトです。

実体は.git/hooks/ディレクトリ配下のファイルで、ファイル名がフック名と一致し、実行権限(+x)が付いている場合に自動実行されます。

# .git/hooks/ の内容を確認する ls .git/hooks/ # 出力例(環境により異なります) applypatch-msg.sample commit-msg.sample fsmonitor-watchman.sample pre-applypatch.sample pre-commit.sample pre-merge-commit.sample pre-push.sample pre-rebase.sample prepare-commit-msg.sample update.sample

初期状態では.sampleという拡張子が付いており、実行されません。フック名から.sampleを外して実行権限を付与することで有効化できます。

Gitフックがシェルスクリプトとして動作する基本ルールは次のとおりです。

・exit 0: Gitが処理を続行する(コミットやプッシュが実行される)
・exit 1以上: Gitが処理をブロックする(コミットやプッシュが中断される)

この仕組みを利用すると、チェック処理で問題を発見したときだけexit 1を返してコミットを止められます。

pre-commitフックを実装する基本手順

1. スクリプトを作成して実行権限を付与する

.git/hooks/pre-commitというファイルを作成します。

# pre-commitフックのひな型を作成する cat > .git/hooks/pre-commit << 'HOOKEOF' #!/bin/bash set -euo pipefail echo "[pre-commit] フックが実行されました" exit 0 HOOKEOF # 実行権限を付与する chmod +x .git/hooks/pre-commit # 設定を確認する(出力例) ls -la .git/hooks/pre-commit -rwxr-xr-x 1 user user 72 Nov 7 17:30 .git/hooks/pre-commit

#!/bin/bashのshebangは/bin/shではなく/bin/bashを指定します。bash固有の構文([[ ]]によるパターンマッチングなど)を安全に使えるようになります。

動作確認はgit commitを実行して「[pre-commit] フックが実行されました」という出力が表示されることで確認できます。

2. 変更ファイル一覧を取得する

pre-commitフックでは、ステージングエリアの変更ファイルに対して処理します。git diff --staged --name-onlyでファイル一覧を取得します。

# ステージングされたファイルを取得する(追加・コピー・変更のみ) STAGED_FILES=$(git diff --staged --name-only --diff-filter=ACM 2>/dev/null) # 1ファイルずつ処理する while IFS= read -r file; do echo "対象: $file" done <<< "$STAGED_FILES"

--diff-filter=ACMはAdded(追加)・Copied(コピー)・Modified(変更)のファイルだけを対象にするオプションです。削除済みファイルへの処理は不要なため、このフィルタを付けておくのが実務での設計です。

3. shellcheckで変更ファイルを自動検査する

ステージングされたファイルのうち、シェルスクリプトに対してshellcheckを実行する設計です。

#!/bin/bash set -euo pipefail # shellcheckが使えるか確認する(未インストール環境ではスキップする) if ! command -v shellcheck &>/dev/null; then echo "[pre-commit] shellcheckが見つかりません。チェックをスキップします。" exit 0 fi # ステージングされたファイルを取得する STAGED_FILES=$(git diff --staged --name-only --diff-filter=ACM 2>/dev/null) FAILED=0 while IFS= read -r file; do [[ -z "$file" ]] && continue [[ ! -f "$file" ]] && continue # .shファイル、またはshebangにshが含まれるファイルを対象にする if [[ "$file" == *.sh ]] || head -1 "$file" 2>/dev/null | grep -qE '^#!.*(ba)?sh'; then echo "[pre-commit] shellcheck: $file" if ! shellcheck "$file"; then FAILED=1 fi fi done <<< "$STAGED_FILES" if [[ $FAILED -ne 0 ]]; then echo "[pre-commit] shellcheckでエラーが見つかりました。修正してからコミットしてください。" exit 1 fi exit 0

command -v shellcheck &>/dev/nullでshellcheckの存在を確認してから処理に進みます。ツール未インストールの環境でexit 1を返すと、開発者がコミットできなくなるため、スキップする設計にしておくことが重要です。

pre-pushフックでテストを強制実行する設計

1. pre-pushフックが受け取る情報

pre-pushフックは、プッシュ先のリモート名とURLを引数で受け取り、プッシュ対象のブランチ情報を標準入力で受け取ります。

#!/bin/bash # 引数: $1=リモート名(例: origin)、$2=リモートURL REMOTE="$1" URL="$2" # 標準入力からプッシュ対象を受け取る # フォーマット: <ローカルref> <ローカルSHA> <リモートref> <リモートSHA> while read -r local_ref local_sha remote_ref remote_sha; do echo "プッシュ対象: $local_ref -> $remote_ref" done

2. mainブランチへのプッシュ時だけテストを実行する設計

フィーチャーブランチの開発速度を落とさないよう、mainブランチへのプッシュ時だけテストを実行する設計です。

#!/bin/bash set -euo pipefail REMOTE="$1" URL="$2" # プッシュ先のブランチを確認する while read -r local_ref local_sha remote_ref remote_sha; do if [[ "$remote_ref" == refs/heads/main ]]; then echo "[pre-push] mainブランチへのプッシュ前にテストを実行します..." if [[ -f "./run-tests.sh" ]]; then if ! bash ./run-tests.sh; then echo "[pre-push] テストが失敗しました。プッシュを中断します。" exit 1 fi echo "[pre-push] テストが通過しました。" fi fi done exit 0

run-tests.shが存在しない場合は何もしないでexit 0します。初期セットアップ前の環境でプッシュを妨げないための設計です。

core.hooksPathでチームへ配布する設計

.git/hooks/はバージョン管理の対象外です。git cloneしたときにフックは各開発者の環境に配布されません。チーム全体でフックを共有するには、core.hooksPathを使って管理対象ディレクトリへ向ける設計が有効です。

1. .githooksディレクトリにフックを移す

# リポジトリのルートに.githooksディレクトリを作成する mkdir -p .githooks # .git/hooks/のフックを.githooksへ移動する mv .git/hooks/pre-commit .githooks/pre-commit mv .git/hooks/pre-push .githooks/pre-push # 実行権限が付いているか確認する(出力例) ls -la .githooks/ -rwxr-xr-x 1 user user 1234 Nov 7 17:30 pre-commit -rwxr-xr-x 1 user user 567 Nov 7 17:30 pre-push # gitの管理対象に追加する git add .githooks/ git commit -m "chore: Gitフックを.githooksへ移動"

.githooks/はリポジトリのルートに作成します。名前は慣例で.githooksが使われますが、任意の名前で構いません。

2. core.hooksPathを設定するセットアップスクリプトを用意する

リポジトリにセットアップスクリプトを置いて、各開発者がcore.hooksPathを設定できるようにします。

#!/bin/bash # setup.sh(リポジトリルートに置く) set -euo pipefail HOOKS_DIR=".githooks" if [[ ! -d "$HOOKS_DIR" ]]; then echo "エラー: $HOOKS_DIR が見つかりません" exit 1 fi # core.hooksPathをローカルgit設定に書き込む git config --local core.hooksPath "$HOOKS_DIR" echo "Gitフックのセットアップが完了しました。" echo "フックディレクトリ: $(git config core.hooksPath)"

プロジェクトに参加したメンバーがbash setup.shを1回実行するだけで設定が完了します。

3. 設定が有効になっていることを確認する

# core.hooksPathの設定を確認する(出力例) git config --local core.hooksPath .githooks # フックの実行権限を確認する(出力例) ls -la .githooks/ -rwxr-xr-x 1 user user 1234 pre-commit -rwxr-xr-x 1 user user 567 pre-push

.githooks/内のファイルに実行権限(先頭3文字がrwx)が付いていることを確認してから、チームへ案内します。

シェルスクリプトの書き方をより体系的に学びたい方は、シェルスクリプト実践入門もあわせてご覧ください。

トラブルシュート・よくある問題

「フックが実行されない」場合の確認手順

# 1. 実行権限を確認する ls -la .git/hooks/pre-commit # -rwxr-xr-x なら権限OK。-rw-r--r-- なら chmod +x で付与する # 2. core.hooksPathの設定を確認する git config --local core.hooksPath # .githooksが返れば設定済み。空なら setup.sh を再実行する # 3. shebangを確認する head -1 .githooks/pre-commit # #!/bin/bash のような正しいshebangが必要

--no-verifyオプションについて

--no-verifyオプションを使うと、フックをスキップしてコミットできます。

緊急対応時の手段として存在しますが、チームで乱用しないよう運用ルールを決めておくことが重要です。

# フックをスキップしてコミットする(緊急時のみ使用する) git commit --no-verify -m "hotfix: 緊急対応"

Windowsで作成したスクリプトの改行コードに注意する

Windowsで作成したスクリプトをそのまま使うと、改行コードが\r\n(CRLF)になってshebangが正しく解釈されないことがあります。dos2unixコマンドで変換できます。

# 改行コードをLFに変換する dos2unix .githooks/pre-commit # 改行コードを確認する(出力例) file .githooks/pre-commit .githooks/pre-commit: Bourne-Again shell script, ASCII text executable

本記事のまとめ

やりたいこと 設計
コミット前に自動チェックを実行したい chmod +x .git/hooks/pre-commitでフックを有効化する
変更ファイルにshellcheckを適用したい git diff --staged --name-only --diff-filter=ACMで一覧取得してループする
プッシュ前にテストを強制実行したい .git/hooks/pre-pushで標準入力を受け取りテストを実行する
チーム全体でフックを共有したい git config --local core.hooksPath .githooksで管理対象ディレクトリへ向ける
フックを一時的にスキップしたい git commit --no-verify(緊急時のみ使用する)
Gitフックをシェルスクリプトで実装すると、コミット・プッシュのたびに品質チェックを自動実行できます。

特にcore.hooksPathによるチーム配布の設計は、フックを「個人の設定」から「プロジェクトの標準」に変える重要なポイントです。セットアップスクリプトと組み合わせて、チーム全員が同じチェックを実行できる環境を整えてください。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、シェルスクリプトの実践的な設計スキルを体系的に習得できるシェルスクリプト実践入門をご覧ください。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。