Git フックの基本的な使い方
TL;DR
# テンプレートディレクトリを作成
$ mkdir -p ~/.git_template/hooks
# テンプレートを読み込む
$ git config --global init.templatedir '~/.git_template'
# pre-commit スクリプトを作成 (中身は下に記載)
$ touch ~/.git_template/hooks/pre-commit
# commit-msg スクリプトを作成 (中身は下に記載)
$ touch ~/.git_template/hooks/commit-msg
# 実行権限を付与
$ chmod a+x ~/.git_template/hooks/pre-commit
$ chmod a+x ~/.git_template/hooks/commit-msg
# 適当なディレクトリを作成
$ mkdir git-hooks-test2
$ cd git-hooks-test2
$ git init
# 適当なファイルを作成 (中身は適当な文字で OK)
$ touch test.txt
$ git add test.txt
$ git commit -m "Add test.txt"
# ユーザ名がセットされていないとエラーになることを確認
fatal: user.name is not set locally
$ git config user.name "test"
$ git commit -m "Add test.txt"
# メールアドレスがセットされていないとエラーになることを確認
fatal: user.email is not set locally
$ git config user.email "test@example.com"
$ git commit -m "Add test.txt"
# コミット時に以下のようなメッセージが表示されることを確認
Committed as:
(use "git reset --soft HEAD^" to undo)
test <test@exmaple.com>
Check if they are correct
#!/bin/sh
if [ -z "`git config --local user.name`" ]; then
echo "fatal: user.name is not set locally"
exit 1
fi
if [ -z "`git config --local user.email`" ]; then
echo "fatal: user.email is not set locally"
exit 1
fi
#!/bin/bash
echo -e "Committed as:"
echo -e " (use \"git reset --soft HEAD^\" to undo)\n"
echo -e " \033[1;93m`git config --local user.name` <`git config --local user.email`>\033[00m\n"
echo -e "\033[1;95mCheck if they are correct\033[00m\n"
- 新しく作成したフックスクリプトを、すでに Git で管理されているディレクトリに追加する場合は
git init
- すでに読み込んでいるフックスクリプトを編集した場合は
.git/hooks
内の該当スクリプトを削除してからgit init
- その他、注意事項は 注意点まとめ を参照すること
Git フックとは
Git フックは、Git でコマンドを実行する直前もしくは実行後に特定のスクリプトを実行するための仕組みです。
たとえば、pre-commit
という Git フックを使用すると、git commit
を実行する直前に任意のスクリプトを実行することができます。これを利用することで、コミットメッセージにイシューやチケットの番号が含まれていなかったらコミットを中止する、などの処理を行うことができるようになります。
サンプルフックスクリプトの確認 (任意)
試しにサンプルのフックスクリプトを見てみましょう。この操作はただの確認なので、必要でなければ読み飛ばしても問題ありません。
適当なディレクトリを作成し、git init
を実行します。
$ mkdir git-hooks-test
$ cd git-hooks-test
$ git init
git init
を実行すると、.git
という隠しディレクトリが作成されます。.git
ディレクトリ内に hooks
というディレクトリが入っているので、hooks
ディレクトリの中身を見てみましょう。
$ ls .git/hooks
applypatch-msg.sample pre-applypatch.sample pre-rebase.sample update.sample
commit-msg.sample pre-commit.sample pre-receive.sample
post-update.sample pre-push.sample prepare-commit-msg.sample
ファイルがたくさん入っていることがわかります。ファイル名の .sample
を外せばフックスクリプトとして動作します。たとえば pre-commit
ならコミットする直前に実行されますし、commit-msg
ならコミットされたときに実行されます。
ここではサンプルスクリプトの中身の詳細については触れませんが、興味があれば中身を見てみたり、実際にスクリプトを実行してみたりすると良いと思います。
サンプルフックスクリプトが生成されない場合
git init
しても .git/hooks
内にサンプルフックスクリプトが生成されないことがあります。その理由は、すでに自作のフックスクリプトを読み込んでいるからです。サンプルフックスクリプトをもう一度生成するようにしたい場合は、一時的に自作のフックスクリプトを読み込まないように設定を変更してください。
$ git config --global --unset init.templatedir
その後もう一度、新しくディレクトリを作成し git init
すればサンプルフックスクリプトが生成されます。
自作のスクリプトを読み込む設定を追加
Git フックの仕組みがわかったところで自作のフックスクリプトを読み込む設定を追加してみましょう。ホームディレクトリに .git_template
というディレクトリを作成し、その中に hooks
ディレクトリを作成します。
$ mkdir -p ~/.git_template/hooks
.git_template
ディレクトリに Git のテンプレートが入っていることを Git で認識できるように設定を追加します。
$ git config --global init.templatedir '~/.git_template'
フックスクリプトの作成
ではいよいよフックスクリプトを作成していきましょう。サンプルフックスクリプトで見たように、フックスクリプトの種類はたくさんありますが、ここでは例としてコミットする直前に実行される pre-commit
とコミット時に実行される commit-msg
のスクリプトを作成してみましょう。
pre-commit スクリプトの作成
先ほど作成した ~/.git_template/hooks
ディレクトリ内にスクリプトファイルを作ります。
$ touch ~/.git_template/hooks/pre-commit
そして、pre-commit
に以下のようなスクリプトを書いてみます。
#!/bin/sh
if [ -z "`git config --local user.name`" ]; then
echo "fatal: user.name is not set locally"
exit 1
fi
if [ -z "`git config --local user.email`" ]; then
echo "fatal: user.email is not set locally"
exit 1
fi
参考: リポジトリごとに user.name や user.email の設定を強制する
上記のスクリプトは、コミットする前に user.name
と user.email
がそのリポジトリ内でローカルに設定されているかどうかを確認し、もしどちらかが設定されていなければ、設定されいないというメッセージを出力し、コミットを中断するフックスクリプトです。
pre-commit
などの、Git コマンドの実行直前に実行されるフックスクリプト (pre
という接頭辞がついているフックスクリプト) では、非ゼロの値で exit
することによって、そのコマンドを中断することができます。上記のフックスクリプトでは、ユーザ名、メールアドレスが設定されていないときに exit 1
とすることで、git commit
の実行を中断しています。
Git 2.8 以降では user.useConfigOnly
という設定が新たに用意され、これを使用することで、コミットする際にユーザ名やメールアドレスが設定されていないとエラーにすることができるようになりました。そのため、Git 2.8 以降をお使いの環境ではあまり有用ではないかもしれませんが、Git 2.8 以前を使用している環境や、グローバルにはセットしているけどローカルにはセットしていない場合にもコミットを中断する設定にしたい場合には便利なスクリプトです。
commit-msg スクリプトの作成
もう一つ例を見てみましょう。pre-commit
の例で十分理解した方はこちらは無視しても構いません。
pre-commit
と同様に、hooks
ディレクトリ内に commit-msg
というスクリプトファイルを作成します。
$ touch ~/.git_template/hooks/commit-msg
commit-msg
内に以下のようなスクリプトを書いてみます。
#!/bin/bash
echo -e "Committed as:"
echo -e " (use \"git reset --soft HEAD^\" to undo)\n"
echo -e " \033[1;93m`git config --local user.name` <`git config --local user.email`>\033[00m\n"
echo -e "\033[1;95mCheck if they are correct\033[00m\n"
このフックスクリプトを用意すると、コミットした後に以下のようなメッセージが表示されます。
<img width="408" alt="commit-msg.png" src="https://qiita-image-store.s3.amazonaws.com/0/113895/9c26db3b-c6d3-e7a9-a4e7-3e1ef377b131.png">
git commit
したときのユーザ名とメールアドレスが表示されるようになります。
使用している PC で一つのリモートリポジトリのアカウントしか管理していない場合は特に有用性はありませんが、個人用と会社用で GitHub のアカウントを分けている場合や、別のユーザ名、メールアドレスで GitLab や Bitbucket などのアカウントと併用している場合では、うっかり違うユーザ名とメールアドレスでコミットしてしまう事故が発生することがあります。そのような事故を防ぐために、コミットした後にどのユーザ名、メールアドレスでコミットされたのかが上記のように表示されると便利です。
TIPS: 他のスクリプト言語も実行可能
今回の例ではシェルスクリプトを使用しましたが、Ruby や Perl などの別のスクリプト言語を実行させることも可能です。shebang を、実行したいスクリプト言語に変更すれば OK です。
実行権限を付与
実はこのままではこのフックスクリプトは実行されません。シェルスクリプトをよく書く人ならお気づきかもしれませんが、ファイルに実行権限を与えないとスクリプトとして実行することができません。そのため、上記で作成したフックスクリプトファイルに実行権限を与えます。
$ chmod a+x ~/.git_template/hooks/pre-commit
$ chmod a+x ~/.git_template/hooks/commit-msg
これでフックスクリプトが実行されるようになりました。この作業は新しいフックスクリプトを作成したときなどに忘れがちなので注意してください。また、もし実行権限が与えられていない場合、Git は何も警告を出してくれません。フックスクリプトを設定したのになぜか実行されない場合は実行権限が与えられているかどうかを確認してみてください。
フックスクリプトの実行確認
ではフックスクリプトが動作するかどうか確認してみましょう。適当なディレクトリを作成し、git init
を実行して、適当なファイルを作成してコミットしてみます。
# 適当なディレクトリを作成
$ mkdir git-hooks-test2
$ cd git-hooks-test2
$ git init
# 適当なファイルを作成
$ touch test.txt # エディタを開き test.txt に適当な文字を入力
$ git add test.txt
$ git commit -m "Add test.txt"
# ユーザ名がセットされていないとエラーになることを確認
fatal: user.name is not set locally
$ git config user.name "test"
$ git commit -m "Add test.txt"
# メールアドレスがセットされていないとエラーになることを確認
fatal: user.email is not set locally
$ git config user.email "test@example.com"
$ git commit -m "Add test.txt"
# コミット時に以下のようなメッセージが表示されることを確認
Committed as:
(use "git reset --soft HEAD^" to undo)
test <test@exmaple.com>
Check if they are correct
ユーザ名とメールアドレスを設定していない状態でコミットしようとしたときにエラーメッセージが表示されコミットされていないことがわかります。また、ユーザ名とメールアドレスを設定してコミットした後に test <test@exmaple.com>
としてコミットしたことを表すメッセージが表示されているのが確認できます。
フックスクリプト生成の注意点
フックスクリプトは、git init
したときや git clone
したときに、そのディレクトリ内の .git/hooks
内に生成されます。フックスクリプトを作成したあとに git clone
する場合や、新しくディレクトリを作成して git init
する場合は問題ありませんが、すでに Git で管理されているディレクトリにフックスクリプトを追加する場合は注意が必要です。
新しく作成したフックスクリプトを追加する場合
すでに Git で管理されているディレクトリに、新たに追加作成したフックスクリプトを読み込ませるには、単に git init
をもう一度実行すれば OK です。
$ git init
Reinitialized existing Git repository in /path/to/repo/.git/
すでに一度 git init
を実行している場合でも、もう一度実行すれば新しいフックスクリプトを読み込んでくれるようになります。
すでに読み込んでいるフックスクリプトを編集した場合
たとえば pre-commit
がすでにそのディレクトリ内で動作していて、~/.git_template/hooks/pre-commit
の中身を変更した場合、変更後の pre-commit
を反映させるためには、一旦そのディレクトリ内にある古い pre-commit
を手動で削除する必要があります。
$ rm path/to/repo/.git/hooks/pre-commit
$ git init
Reinitialized existing Git repository in /path/to/repo/.git/
すでに pre-commit
がある状態で git init
しただけだと、変更した ~/.git_template/hooks/pre-commit
の内容は反映されないので注意してください。
その他
プロジェクトディレクトリごとにフックスクリプトを変更したい場合
今回の例では ~/.git_template/hooks
にフックスクリプトを置いて、そのフックスクリプトを各々のプロジェクトディレクトリで読み込む方法について紹介しました。要するに、すべての Git 管理下のディレクトリで共通のフックスクリプトが実行されるようになっています。
通常はこれで問題ないかと思いますが、特定のディレクトリだけ別のフックスクリプトを実行したい場合があるかもしれません。
その場合は、単にそのディレクトリ内の .git/hooks
に用意されたテンプレートのフックスクリプト (~/.git_template/hooks
から読み込まれたフックスクリプト) を削除し、新たに別のフックスクリプトを追加すれば良いです。先ほども説明したように、すでに存在するフックスクリプトに関しては、git init
しても上書きはされないので問題ありません。ただし、フックスクリプトの管理が煩雑になるので、あまりおすすめはしません。
注意点まとめ
- すでに自作のフックスクリプトが読み込まれている場合は、
git init
を実行してもサンプルフックスクリプトは生成されない - フックスクリプトを新たに作成した場合は実行権限の付与を忘れずに
- すでに Git で管理されているディレクトリに、新たに作成したフックスクリプトを追加する場合は
git init
する必要がある - すでに読み込んでいるフックスクリプトを編集した場合は、一度
.git/hooks
内の該当スクリプトを削除してからgit init
しないと反映されない
Discussion