10 上級 約 35 分

Git hooks と自動化

Git hooks を使ってコミット前の自動チェックやメッセージ整形を自動化する方法を演習問題で学びます。

このチュートリアルでは、Git の特定の操作をトリガーにスクリプトを自動実行する「Git hooks」の仕組みを演習形式で学びます。コミット前の自動リント、コミットメッセージの形式チェックなど、チーム開発の品質を支える自動化の基礎を扱います。

前提: シェルスクリプトの基本的な読み書きができることを前提とします。


演習 1:.git/hooks/ ディレクトリの仕組みを理解する

問題: .git/hooks/ ディレクトリの中身を確認し、どのようなファイルが最初から存在するか調べてください。

答えを見る
ls .git/hooks/
applypatch-msg.sample
commit-msg.sample
pre-commit.sample
pre-push.sample
pre-rebase.sample
...

git init した時点で、Git は .sample 拡張子の付いたサンプルスクリプトを自動生成します。これらは実行されません。フックを有効にするには、.sample を取り除いたファイル名(例: pre-commit)でスクリプトを配置し、実行権限を付与する必要があります。

フック名実行タイミング
pre-commitgit commit 実行時、コミットメッセージ入力前
commit-msgコミットメッセージが確定した直後
pre-pushgit push 実行前
post-mergegit mergegit pull 含む)完了後

演習 2:pre-commit hook でコミット前にリントを実行する

コミットする前に、自動でコードのリントチェックを走らせ、エラーがあればコミットを止めたいとします。

問題: .git/hooks/pre-commit として、npm run lint を実行し、失敗した場合はコミットを中止するスクリプトを作成してください。

答えを見る
#!/bin/sh
npm run lint
# 実行権限を付与する(これを忘れるとフックが動かない)
chmod +x .git/hooks/pre-commit

hook スクリプトは、終了コードが 0 以外の場合に Git がその後の処理(この場合はコミット)を中止します。npm run lint がエラーで終了すれば、自動的にコミットがブロックされます。

git commit -m "何か変更"
# > npm run lint
# ✖ 3 problems (3 errors, 0 warnings)
# (コミットは実行されない)

演習 3:commit-msg hook でコミットメッセージのフォーマットを強制する

チームで feat: , fix: , docs: のようなプレフィックスをコミットメッセージに必須にしたいとします。

問題: .git/hooks/commit-msg として、コミットメッセージが feat: fix: docs: chore: のいずれかで始まっていない場合にコミットを拒否するスクリプトを作成してください。

答えを見る
#!/bin/sh
MESSAGE_FILE="$1"
PATTERN="^(feat|fix|docs|chore): "

if ! grep -qE "$PATTERN" "$MESSAGE_FILE"; then
  echo "エラー: コミットメッセージは feat:/fix:/docs:/chore: のいずれかで始めてください"
  exit 1
fi
chmod +x .git/hooks/commit-msg

commit-msg フックには、コミットメッセージが書かれた一時ファイルのパスが第1引数($1)として渡されます。パターンに一致しなければ exit 1 でコミット自体を中止します。

git commit -m "ログイン画面を修正"
# エラー: コミットメッセージは feat:/fix:/docs:/chore: のいずれかで始めてください

git commit -m "fix: ログイン画面のバリデーションを修正"
# (コミット成功)

演習 4:post-merge hook でマージ後に自動で依存関係を更新する

package.json が更新されるマージ(git pull 含む)の後、自動的に npm install を実行して依存関係のずれを防ぎたいとします。

問題: .git/hooks/post-merge として、マージによって package-lock.json が変更されていた場合にのみ npm install を実行するスクリプトを作成してください。

答えを見る
#!/bin/sh
CHANGED_FILES="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

if echo "$CHANGED_FILES" | grep --quiet "package-lock.json"; then
  echo "package-lock.json が更新されました。依存関係を再インストールします..."
  npm install
fi
chmod +x .git/hooks/post-merge

ORIG_HEAD はマージ直前の HEAD を指す特殊な参照で、マージによって変更されたファイル一覧を git diff-tree で取得できます。package-lock.json の変更を検知したときだけ npm install を走らせることで、無駄な再インストールを避けられます。


演習 5:hook スクリプトをチームで共有する方法(.githooks/ ディレクトリ)

.git/hooks/.git ディレクトリの中にあるため、通常の git add / git commit の対象外であり、リポジトリをクローンしても引き継がれません。チームで同じ hook を共有する方法を扱います。

問題: リポジトリ内に .githooks/ ディレクトリを作り、そこに配置したスクリプトを Git 標準の hook として使うよう設定するコマンドを書いてください。

答えを見る
# 1. リポジトリ内に共有用ディレクトリを作成し、スクリプトを配置
mkdir -p .githooks
mv .git/hooks/pre-commit .githooks/pre-commit
chmod +x .githooks/pre-commit

# 2. Git に hook の参照先ディレクトリを変更するよう設定
git config core.hooksPath .githooks

# 3. 通常のファイルとしてコミットし、チームに共有
git add .githooks/pre-commit
git commit -m "chore: pre-commit hook をチームで共有"
git push

core.hooksPath を設定することで、Git は .git/hooks/ の代わりに .githooks/ を hook の実行元として使うようになります。.githooks/ は通常のファイルなのでリポジトリに含めてコミットでき、clone した全員が同じ hook を利用できます。

ただしこの設定自体(core.hooksPath)は各メンバーがローカルで実行する必要があるため、README などに手順を明記しておくか、husky のような、npm install 時に自動でこの設定を行うツールを使うのが実務では一般的です。


hook ファイルの実行権限について

⚠️ hook スクリプトは実行権限(chmod +x)が必要です

.git/hooks/pre-commit.githooks/pre-commit にスクリプトを配置しても、実行権限が付与されていなければ Git はそのファイルを無視します。作成・編集後は必ず以下を確認してください。

chmod +x .githooks/pre-commit
ls -l .githooks/pre-commit
# -rwxr-xr-x ... pre-commit   ← "x" が付いていることを確認

Windows 環境から .githooks/ をコミットした場合、実行権限のビットが正しく記録されないことがあるため、macOS/Linux のメンバーが clone 後に手動で chmod +x する必要が生じることがあります。


まとめ

コミット前にチェックしたい           → pre-commit
コミットメッセージの形式を強制したい  → commit-msg
push 前に確認したい                  → pre-push
マージ後に自動処理したい             → post-merge
hook をチームで共有したい            → .githooks/ + git config core.hooksPath

Git hooks はローカルでの自動化に強力な仕組みですが、強制力を持つルール(CIでのリント必須化など)は、hook だけに頼らず CI 側でも二重にチェックするのが安全です。hook はあくまで「早い段階で気づくための仕組み」と位置づけましょう。