ルールをフックに掛けたのに、一語で素通りされた
目標
リポジトリに入れて共有される、クライアントフックを3つ作り、それが何を止められて、何を止められないかを、自分で確認します。
なぜ重要なのか
フックは、「ルールを自動化する」という言葉で紹介されますが、クライアントフックが実際に行うのは、素早いフィードバックです。強制ではありません。この違いを知らずに、ルールをクライアントにだけ課しておくと、守られていると信じた状態が、長く続きます。このラボは、その思い込みを自分で壊してみます。ルールを3つ作り、1語ですべて通り抜けます。そして、pre-commitがワーキングツリーを読むと、なぜいけないのかを、インデックスにだけ機密情報がある状態を作って確認します。コミットされるのは、インデックスであって、ワーキングツリーではないという事実は、フックを使わなくても、知っておく価値があります。
ステップ
/root/gitx9/repoを作成し、core.hooksPathを.githooksに指定します。.githooks/commit-msgで、タイトルの形式を強制し、拒否された記録をnotes/reject.txtに残します。.githooks/pre-commitで、100000バイトを超えるファイルを止め、notes/bigfile.txtに残します。- そのフックがインデックスを見るように直して、
AKIAで始まる文字列を止め、ワーキングツリーだけを見ると、なぜ突破されるかをnotes/index.txtに残します。 --no-verifyでルールを破るコミットを1つ作り、notes/bypass.txtに残します。/root/gitx9/cloneをcloneして、何が付いてきて、何が付いてこないかを、notes/share.txtに残します。/root/gitx9/origin.gitを作成し、.githooks/pre-pushでmainへの直接pushを止めて、notes/prepush.txtに残します。notes/report.mdに、クライアントフックとサーバーフックの役割をまとめます。
参考
- フックのファイルには、必ず実行権限(
chmod +x)を与えてください。ないと、gitが静かにスキップします。 - このイメージには、python3がありません。フックは、
shとgit・grep・awkで書きます。 - ステップ4のテスト用の文字列は、
AKIAIOSFODNN7EXAMPLEです。パターンは、AKIAのあとに大文字・数字16文字で作ってください。 - よくある間違い: フックを作ってコミットしないことです。リポジトリに入っていてはじめて、共有されます。
- よくある間違い: ステップ7で、
mainを最後までpushしてしまうことです。止められるのを確認したら、機能ブランチだけをプッシュする必要があります。
フックをリポジトリの中に置くことにする
/root/gitx9/repoを作成し、core.hooksPathを.githooksに指定します。
.git/hooks/はcloneで付いてきません。git config core.hooksPath .githooksで、フックを探す場所を、リポジトリが追跡するディレクトリに移してください。ディレクトリも、先に作っておきます。リポジトリごとにuser.emailとuser.nameも指定しないと、コミットできません。
タイトルの形式を強制する
.githooks/commit-msgで、タイトルの形式を強制し、拒否された記録をnotes/reject.txtに残します。
commit-msgフックは、メッセージファイルのパスを$1で受け取ります。最初の行だけを見て、<타입>(<범위>): <설명>の形式かをgrep -qEで検査してください(プレースホルダーは、タイプ、範囲、説明です)。タイプは、feat・fix・docs・refactor・test・chore・build程度で構いません。外れていたら、標準エラー出力で何が間違っているかを伝え、0以外の値で終了する必要があります。chmod +xを忘れないでください。
大きなファイルをコミット前に止める
.githooks/pre-commitで、100000バイトを超えるファイルを止め、notes/bigfile.txtに残します。
pre-commitは、引数を受け取りません。何がコミットされようとしているかは、git diff --cached --name-only --diff-filter=ACMで、自分で調べる必要があります。サイズが100000を超えたら、どのファイルが何バイトかを伝えて、0以外の値で終了してください。止められるのを自分で確認したあとは、その大きなファイルをステージングから外しておいてください。
コミットされるのは、ワーキングツリーではなくインデックスである
そのフックがインデックスを見るように直して、AKIAで始まる文字列を止め、ワーキングツリーだけを見ると、なぜ突破されるかをnotes/index.txtに残します。
インデックスに載ったblobは、git ls-files -s -- <경로>の2列目で、内容はgit cat-file -p <blob>またはgit show :<경로>で読みます(プレースホルダーはパスです)。サイズも、git cat-file -s <blob>で測ってください。そのあと、ファイルにAKIAIOSFODNN7EXAMPLEを書いてgit addし、ワーキングツリー側のファイルだけをきれいに直してから、コミットしてみてください。ワーキングツリーを読むフックなら、そのまま通過していたコミットです。
1語ですべて通り抜ける
--no-verifyでルールを破るコミットを1つ作り、notes/bypass.txtに残します。
git commit --no-verifyは、pre-commitとcommit-msgをスキップします。タイトルのルールを破るコミットを1つ作って、履歴に残してください。そして、これは欠陥ではなく設計であること、そのため、強制力が必要なルールはどこに課すべきかも、一緒に書きます。
フックのファイルは付いてきて、設定は付いてこない
/root/gitx9/cloneをcloneして、何が付いてきて、何が付いてこないかを、notes/share.txtに残します。
git clone /root/gitx9/repo /root/gitx9/cloneを行い、その中で3つを見てください。.githooks/に何があるか、git config --local core.hooksPathが何を出すか、.git/hooks/に何があるか。確認したあと、cloneでもcore.hooksPathをオンにしてください。
保護ブランチをクライアントで止めてみる
/root/gitx9/origin.gitを作成し、.githooks/pre-pushでmainへの直接pushを止めて、notes/prepush.txtに残します。
pre-pushは、標準入力で行ごとに로컬참조 로컬해시 원격참조 원격해시を受け取ります(プレースホルダーは、ローカルの参照、ローカルのハッシュ、リモートの参照、リモートのハッシュです)。원격참조がrefs/heads/mainである行があれば、止めてください(プレースホルダーはリモートの参照です)。リモートはgit init --bare /root/gitx9/origin.gitで作り、git remote add originで接続します。mainのpushが止められるのを確認したら、機能ブランチだけをプッシュしてください。
どこが知らせ、どこが止めるのか
notes/report.mdに、クライアントフックとサーバーフックの役割をまとめます。
表1つとルール数行で構いません。クライアントフックが止められない3つを必ず書き、そのため同じルールを2か所に課す理由で締めくくってください。フックが遅いと、人々が回避を習慣として使うという実務上のアドバイスにも、価値があります。