Git
Chapters ▾ 2nd Edition

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

Git フック

他のバージョンコントロールシステムと同じように、Gitにも特定のアクションが発生した時にカスタムスクリプトを叩く方法があります。 このようなフックは、クライアントサイドとサーバーサイドの二つのグループに分けられます。 クライアントサイドフックはコミットやマージといったクライアントでの操作の際に、サーバーサイドフックはプッシュされたコミットの受け取りといったネットワーク操作の際に、それぞれ実行されます。 これらのフックは、さまざまなな目的に用いることができます。

フックをインストールする

フックは、Gitディレクトリの hooks サブディレクトリ(一般的なプロジェクトでは、.git/hooks )に格納されています。 git init で新しいリポジトリを初期化する時には、Gitに同梱されているスクリプトのサンプルがこの hooks ディレクトリに格納されます。サンプルの多くはそのままでも十分有用ですし、また、各スクリプトの入力値に関するドキュメントもついています。 サンプルは全てシェルスクリプトで書かれており、その中の一部では Perl も使われています。ですが、どんなスクリプトでも、実行可能かつ適切に命名されてさえいれば、問題なく動きます。Ruby や Python などで書くこともできます。 これら同梱のフックスクリプトを使用する場合は、ファイル名の末尾が .sample となっていますので適宜リネームしてください。

フックスクリプトを有効にするには、Gitディレクトリの hooks サブディレクトリに、実行可能なファイルを適切な名前(拡張子は使えません)で配置すれば、以降そのファイルが呼び出されます。 ここでは重要なフックファイル名をいくつか取り上げます。

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

クライアントサイドフックにはたくさんの種類があります。 ここではコミットワークフローフック、Eメールワークフロースクリプト、その他クライアントサイドフックに分類します。

Note

特筆すべき点として、クライアントサイドフックはリポジトリをクローンする際には コピーされません 。 スクリプトを使って何らかのポリシーを強制したいのなら、サーバサイドで行う方がよいでしょう。サンプルが Git ポリシーの実施例 にあります。

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

最初の4つのフックはコミットプロセスに関するものです。

pre-commit フックは、コミットメッセージが入力される前に実行されます。 これは、いまからコミットされるスナップショットを検査したり、何かし忘れた事がないか確認したり、テストが実行できるか確認したり、何かしらコードを検査する目的で使用されます。 このフックがゼロでない値を返すと、コミットが中断されます。また、この検査は git commit --no-verify で飛ばすこともできます。 ここではコーディングスタイルの検査(lintを実行するなど)や、行末の空白文字の検査(デフォルトのフックがまさにそうです)、新しく追加されたメソッドのドキュメントが正しいかどうかの検査といったことが可能です。

`prepare-commit-msg`フックは、コミットメッセージエディターが起動する直前、デフォルトメッセージが生成された直後に実行されます。 このフックでは、デフォルトメッセージを、コミットの作者の目に触れる前に編集できます。 このフックにはパラメータがあり、その時点でのコミットメッセージを保存したファイルへのパス、コミットのタイプ、さらにamendされたコミットの場合はコミットの SHA-1 をパラメータとして取ります。 このフックは普段のコミットにおいてはあまり有用ではありませんが、テンプレートが用意されているコミットメッセージ・mergeコミット・squashコミット・amendコミットのような、デフォルトメッセージが自動生成されるコミットにおいて効果を発揮します。 コミットメッセージのテンプレートと組み合わせれば、プログラムで情報を動的に挿入できます。

commit-msg フックは、開発者の書いたコミットメッセージを保存した一時ファイルへのパスをパラメータに取ります。 このスクリプトがゼロ以外の値を返した場合、Git はコミットプロセスを中断します。これを使えば、コミットを許可して処理を進める前に、プロジェクトの状態やコミットメッセージを検査できます。 この章の最後のセクションでは、このフックを使用してコミットメッセージが要求された様式に沿っているか検査するデモンストレーションを行います。

コミットプロセスが全て完了した後には、post-commit`フックが実行されます。 このフックはパラメータを取りませんが、 `git log -1 HEAD を実行することで直前のコミットを簡単に取り出すことができます。 一般的にこのスクリプトは何かしらの通知といった目的に使用されます。

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

Eメールを使ったワークフロー用として、三種類のクライアントサイドフックを設定できます。 これらはすべて 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 フックをゼロ以外の値で終了させると、プッシュを却下してエラーメッセージをクライアントに返すことができます。つまり、プッシュに関して、好きなだけ複雑なポリシーを設定できるということです。

pre-receive

クライアントからのプッシュを処理するときに最初に実行されるスクリプトが pre-receive です。 このスクリプトは、プッシュされた参照のリストを標準入力から受け取ります。ゼロ以外の値で終了させると、これらはすべて却下されます。 このフックを使うと、更新内容がすべてfast-forwardであることをチェックしたり、プッシュによって変更されるファイルや参照に対するアクセス制御を行ったりできます。

update

update スクリプトは pre-receive スクリプトと似ていますが、プッシュしてきた人が更新しようとしているブランチごとに実行されるという点が異なります。 複数のブランチへのプッシュがあったときに pre-receive が実行されるのは一度だけですが、update はブランチ単位でそれぞれ一度ずつ実行されます。 このスクリプトは、標準入力を読み込むのではなく三つの引数を受け取ります。参照 (ブランチ) の名前、プッシュ前を指す参照の SHA-1、そしてプッシュしようとしている参照の SHA-1 です。 update スクリプトをゼロ以外で終了させると、その参照のみが却下されます。それ以外の参照はそのまま更新を続行します。

post-receive

post-receive フックは処理が終了した後で実行されるもので、他のサービスの更新やユーザーへの通知などに使えます。 このフックは、 pre-receive フックと同じデータを標準入力から受け取ります。 サンプルのスクリプトには、リストをメールしたり、継続的インテグレーションサーバーへ通知したり、チケット追跡システムを更新したりといった処理が含まれています。コミットメッセージを解析して、チケットのオープン・修正・クローズなどの必要性を調べることもできます。 このスクリプトではプッシュの処理を中断させることはできませんが、クライアント側ではこのスクリプトが終了するまで接続を切断できません。このスクリプトで時間のかかる処理をさせるときには十分注意しましょう。