プリコミットフックが壊れています
111日前原文(jyn.dev)
概要
- Rustプロジェクトの開始からpre-commitフックの作成までの流れ
- pre-commitフックが抱える根本的な問題点の指摘
- pre-pushフックの推奨理由とその運用ポイント
- 具体的なフックスクリプトの改良例
- フック導入時の注意点とベストプラクティス
Rustプロジェクトの初期セットアップとフォーマット管理
- 新規ディレクトリbest-fizzbuzz-ever作成、main.rsファイル作成
- Gitリポジトリの初期化とmain.rsのコミット
- 他言語FizzBuzz集への追加時、コードフォーマット統一の要求
- rustfmtによるフォーマットチェック用pre-commitフック導入
- 例:
#!/bin/sh set -eu for f in *.rs; do rustfmt --check "$f"; done
- 例:
- ステージングされていない変更をフックで検出できない問題発生
pre-commitフックの改良と限界
- インデックス上のファイルを一時ディレクトリに展開しチェックする方式へ改良
- 例:
tmpdir=$(mktemp -d --tmpdir "$(basename "$(realpath .)")-pre-commit.XXXX") trap 'rm -r "$tmpdir"' EXIT git checkout-index --all --prefix="$tmpdir/" for f in $tmpdir/*.rs; do rustfmt --check "$f"; done
- 例:
- 変更ファイルのみチェックするようさらに改良
- 例:
files=$(git diff --name-only --cached --no-ext-diff --diff-filter=d) ...
- 例:
- 既存コードが未整形だとコミットできない問題
- rebase時やRustファイルがないコミットでフックが失敗するケース
pre-commitフックの根本的な問題
- 他人のブランチや古いフックには適用できない
- rebaseやmerge時に意図せずフックが走る
- コミット単位での品質担保が困難
- CIでの検証と異なり、ローカルのみで完結してしまう
- 作業効率低下やワークフロー破壊のリスク
- 作業ツリー全体のチェックは大規模リポジトリで非現実的
pre-pushフック推奨理由と運用のポイント
- pre-pushフックなら、push前にまとめてチェック可能
- インデックス上のファイルを対象にすることで、一貫性確保
- 高速・信頼性重視のチェックのみ実装推奨
- ネットワークアクセスやビルド必須のチェックは非推奨
- 静かな出力でユーザー体験向上
- 自動セットアップ禁止、手動設定手順をドキュメント化
- コミット前フックは機密情報(例: 認証情報)流出防止用途に限定
pre-pushフック作成時の具体的アドバイス
- インデックス上のファイルでのみチェック実施
- 変更ファイル検出は信頼できないため、明示的なファイル指定が望ましい
- CONTRIBUTING.md等に手動導入手順を明記
- pre-commitフックは原則使用しない方針
まとめ
- pre-commitフックは多くの欠点があり、pre-pushフックの使用が推奨
- 品質担保はCIやpre-pushフックで実施
- 認証情報漏洩防止以外でpre-commitフックは避けるべき
- 運用ドキュメントの整備が重要