TT Lab
はじめる
学ぶ 学習パス コース

Git実戦

ルールをフックに掛けたのに、一語で素通りされた

TT Labで続きを見る

目標

リポジトリに入れて共有される、クライアントフックを3つ作り、それが何を止められて、何を止められないかを、自分で確認します。

なぜ重要なのか

フックは、「ルールを自動化する」という言葉で紹介されますが、クライアントフックが実際に行うのは、素早いフィードバックです。強制ではありません。この違いを知らずに、ルールをクライアントにだけ課しておくと、守られていると信じた状態が、長く続きます。このラボは、その思い込みを自分で壊してみます。ルールを3つ作り、1語ですべて通り抜けます。そして、pre-commitがワーキングツリーを読むと、なぜいけないのかを、インデックスにだけ機密情報がある状態を作って確認します。コミットされるのは、インデックスであって、ワーキングツリーではないという事実は、フックを使わなくても、知っておく価値があります。

ステップ

  1. /root/gitx9/repoを作成し、core.hooksPathを.githooksに指定します。
  2. .githooks/commit-msgで、タイトルの形式を強制し、拒否された記録をnotes/reject.txtに残します。
  3. .githooks/pre-commitで、100000バイトを超えるファイルを止め、notes/bigfile.txtに残します。
  4. そのフックがインデックスを見るように直して、AKIAで始まる文字列を止め、ワーキングツリーだけを見ると、なぜ突破されるかをnotes/index.txtに残します。
  5. --no-verifyでルールを破るコミットを1つ作り、notes/bypass.txtに残します。
  6. /root/gitx9/cloneをcloneして、何が付いてきて、何が付いてこないかを、notes/share.txtに残します。
  7. /root/gitx9/origin.gitを作成し、.githooks/pre-pushでmainへの直接pushを止めて、notes/prepush.txtに残します。
  8. notes/report.mdに、クライアントフックとサーバーフックの役割をまとめます。

参考

フックをリポジトリの中に置くことにする

/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か所に課す理由で締めくくってください。フックが遅いと、人々が回避を習慣として使うという実務上のアドバイスにも、価値があります。