章 ▾ 第2版

8.3 Gitのカスタマイズ - Gitフック

Gitフック

他の多くのバージョン管理システムと同様に、Gitには特定の重要なアクションが発生したときにカスタムスクリプトを実行する方法があります。これらのフックには、クライアントサイドとサーバーサイドの2つのグループがあります。クライアントサイドのフックはコミットやマージなどの操作によってトリガーされ、サーバーサイドのフックはプッシュされたコミットの受信などのネットワーク操作で実行されます。これらのフックは、さまざまな目的で使用できます。

フックのインストール

フックはすべてGitディレクトリのhooksサブディレクトリに保存されています。ほとんどのプロジェクトでは、それは.git/hooksです。新しいリポジトリをgit initで初期化すると、Gitはhooksディレクトリにたくさんのサンプルスクリプトを格納します。これらのスクリプトの多くはそれ自体で役立ちますが、それぞれのスクリプトの入力値も文書化されています。すべてのサンプルはシェルスクリプトとして書かれており、一部にはPerlも含まれていますが、適切な名前が付けられた実行可能なスクリプトであれば、RubyやPythonなど、使い慣れた言語で書かれていても問題なく動作します。付属のフックスクリプトを使用したい場合は、名前を変更する必要があります。それらのファイル名はすべて.sampleで終わっています。

フックスクリプトを有効にするには、.gitディレクトリのhooksサブディレクトリに、適切に命名され(拡張子なし)、実行可能であるファイルを配置します。それ以降、そのスクリプトが呼び出されるようになります。ここでは、主要なフックファイル名のほとんどを扱います。

クライアントサイドフック

クライアントサイドフックには多くの種類があります。このセクションでは、それらをコミットワークフローフック、メールワークフロースクリプト、およびその他のものに分類して説明します。

クライアントサイドフックはリポジトリをクローンしてもコピーされないことに注意することが重要です。これらのスクリプトでポリシーを強制しようとする場合は、おそらくサーバーサイドでそれを行うことになるでしょう。Gitによる強制ポリシーの例の例を参照してください。

コミットワークフローフック

最初の4つのフックはコミットプロセスに関連しています。

pre-commitフックは、コミットメッセージを入力する前に最初に実行されます。このフックは、コミットしようとしているスナップショットを検査し、何か忘れ物がないか、テストが実行されているかを確認したり、コード内で検査する必要があるものを調べたりするために使用されます。このフックがゼロ以外のステータスで終了すると、コミットは中止されますが、git commit --no-verifyでバイパスすることもできます。コードスタイル(lintなどの実行)、末尾の空白(デフォルトのフックがこれを行います)、新しいメソッドの適切なドキュメントの確認などを行うことができます。

prepare-commit-msgフックは、コミットメッセージエディタが起動される前で、デフォルトメッセージが作成された後に実行されます。これにより、コミット作成者がデフォルトメッセージを見る前に編集することができます。このフックはいくつかのパラメータを受け取ります:これまでのコミットメッセージを保持するファイルのパス、コミットのタイプ、そして修正コミットである場合はコミットのSHA-1です。このフックは通常、通常のコミットにはあまり有用ではありません。むしろ、テンプレート化されたコミットメッセージ、マージコミット、スカッシュされたコミット、修正コミットなど、デフォルトメッセージが自動生成されるコミットに有効です。コミットテンプレートと組み合わせて、プログラム的に情報を挿入するために使用することもできます。

commit-msgフックは1つのパラメータを受け取ります。これは、開発者によって書かれたコミットメッセージを含む一時ファイルのパスです。このスクリプトがゼロ以外のステータスで終了すると、Gitはコミットプロセスを中止するため、コミットが実行される前にプロジェクトの状態やコミットメッセージを検証するために使用できます。この章の最後のセクションでは、このフックを使用して、コミットメッセージが必要なパターンに準拠しているかを確認する方法を実演します。

コミットプロセス全体が完了した後、post-commitフックが実行されます。このフックはパラメータを取りませんが、git log -1 HEADを実行することで最後のコミットを簡単に取得できます。通常、このスクリプトは通知やそれに類する目的で使用されます。

メールワークフローフック

メールベースのワークフローには、3つのクライアントサイドフックを設定できます。これらはすべてgit amコマンドによって呼び出されるため、ワークフローでこのコマンドを使用しない場合は、次のセクションに安全にスキップできます。git format-patchで作成されたパッチをメールで受け取る場合は、これらのいくつかが役に立つかもしれません。

最初に実行されるフックはapplypatch-msgです。これは単一の引数、すなわち提案されたコミットメッセージを含む一時ファイルの名前を受け取ります。このスクリプトがゼロ以外のステータスで終了すると、Gitはパッチ適用を中止します。これを使用して、コミットメッセージが適切にフォーマットされていることを確認したり、スクリプトでメッセージをその場で編集して正規化したりすることができます。

git amを介してパッチを適用するときに次に実行されるフックはpre-applypatchです。少し紛らわしいですが、これはパッチが適用された、コミットが行われるに実行されるため、コミットを行う前にスナップショットを検査するために使用できます。このスクリプトでテストを実行したり、作業ツリーを検査したりすることができます。何かが不足している場合やテストがパスしない場合、ゼロ以外のステータスで終了すると、パッチをコミットせずにgit amスクリプトが中止されます。

git am操作中に最後に実行されるフックはpost-applypatchで、コミットが行われた後に実行されます。これを使用して、取り込んだパッチのグループまたは作者に完了を通知できます。このスクリプトでパッチ適用プロセスを停止することはできません。

その他のクライアントフック

pre-rebaseフックは、リベースを行う前に実行され、ゼロ以外のステータスで終了することでプロセスを停止できます。このフックを使用して、すでにプッシュされたコミットのリベースを禁止することができます。Gitがインストールするpre-rebaseの例はこれを行いますが、あなたのワークフローに合わない仮定をしている場合があります。

post-rewriteフックは、git commit --amendgit rebaseなど、コミットを置き換えるコマンドによって実行されます(ただしgit filter-branchでは実行されません)。単一の引数として書き換えをトリガーしたコマンドを受け取り、stdinを通じて書き換えのリストを受け取ります。このフックは、post-checkoutpost-mergeフックと多くの点で同じ用途があります。

git checkoutが成功した後、post-checkoutフックが実行されます。これを使用して、プロジェクト環境に合わせて作業ディレクトリを適切にセットアップできます。これは、バージョン管理したくない大きなバイナリファイルを移動したり、ドキュメントを自動生成したりするようなことを意味するかもしれません。

post-mergeフックは、mergeコマンドが成功した後に実行されます。これを使用して、パーミッションデータなど、Gitが追跡できない作業ツリー内のデータを復元できます。このフックはまた、作業ツリーが変更されたときにコピーしたいGit管理外のファイルの存在を検証することもできます。

pre-pushフックはgit pushの実行中に、リモート参照が更新された後、オブジェクトが転送される前に実行されます。リモートの名前と場所をパラメータとして受け取り、更新される参照のリストをstdinを通じて受け取ります。プッシュが行われる前に、一連の参照更新を検証するためにこれを使用できます(ゼロ以外の終了コードはプッシュを中止します)。

Gitは、通常の操作の一環として、git gc --autoを呼び出すことで、時々ガベージコレクションを行います。pre-auto-gcフックは、ガベージコレクションが行われる直前に呼び出され、これが実行されていることを通知したり、都合が悪い場合はコレクションを中止したりするために使用できます。

サーバーサイドフック

クライアントサイドフックに加えて、システム管理者としていくつかの重要なサーバーサイドフックを使用して、プロジェクトにほとんどあらゆる種類のポリシーを強制することができます。これらのスクリプトは、サーバーへのプッシュの前後に実行されます。プリフックはいつでもゼロ以外のステータスで終了することでプッシュを拒否し、クライアントにエラーメッセージを返すことができます。これにより、任意の複雑さのプッシュポリシーを設定できます。

pre-receive

クライアントからのプッシュを処理する際に最初に実行されるスクリプトはpre-receiveです。これは標準入力からプッシュされている参照のリストを受け取ります。もしゼロ以外のステータスで終了した場合、それらの参照はどれも受け入れられません。このフックを使用して、更新される参照が非ファストフォワードでないことを確認したり、プッシュで変更されるすべての参照やファイルへのアクセス制御を行ったりすることができます。

update

updateスクリプトはpre-receiveスクリプトと非常によく似ていますが、プッシュ者が更新しようとしている各ブランチに対して一度ずつ実行される点が異なります。プッシュ者が複数のブランチにプッシュしようとしている場合、pre-receiveは一度だけ実行されますが、updateはプッシュ先のブランチごとに一度ずつ実行されます。このスクリプトは標準入力から読み取る代わりに、3つの引数を受け取ります:参照(ブランチ)の名前、プッシュ前にその参照が指していたSHA-1、およびユーザーがプッシュしようとしているSHA-1です。もしupdateスクリプトがゼロ以外のステータスで終了した場合、その参照だけが拒否され、他の参照は引き続き更新できます。

post-receive

post-receiveフックは、プロセス全体が完了した後に実行され、他のサービスを更新したり、ユーザーに通知したりするために使用できます。これはpre-receiveフックと同じ標準入力データを受け取ります。例としては、リストへのメール送信、継続的インテグレーションサーバーへの通知、チケット追跡システムの更新などがあります。コミットメッセージを解析して、開く、変更する、閉じる必要があるチケットがあるかどうかを確認することもできます。このスクリプトはプッシュプロセスを停止することはできませんが、完了するまでクライアントは切断されないため、時間がかかる可能性のあることを試す場合は注意が必要です。

ヒント

他の人が読む必要があるスクリプトやフックを作成する場合は、コマンドラインフラグの長いバージョンを使用することを推奨します。半年後にはきっと私たちに感謝するでしょう。

scroll-to-top