かゆいところに手が届くgitフックスクリプト(クライアントサイド編)

2015/09/29 admin
Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0Share on Tumblr0

こんにちは、ユニトラストの稲邑です。
前回はGitBucketについて紹介しました。
今回はgitフックの便利な機能について紹介したいと思います。

経緯

みなさんはgit管理によるプロジェクト開発を行っていて、
以下のようなことに困ったことは無いでしょうか。

①誤ってmasterブランチへ直接pushしてしまった。
②コミットログを見たときに、過去のコミットコメントが空で、何を対応したかプログラムを見ないとわからない。
③権限のある担当者以外はコミットしてはいけないファイルを、権限の無い開発者がローカル環境で書き換えていたために誤ってコミットしてしまった。

私が10人程のチームでgit管理によるシステム開発を行っていた際、
上記のような問題が発生し、調査や復旧対応にかなり時間を割いたことがありました。

gitに限ったことではないですが、チーム開発時にバージョン管理のルールを決めても、
誰かが誤ってルールを破ってしまうと、被害が大きくなることがあります。
そこで今回は上記①〜③のようなことが起きないように物理的にブロックしてくれる、
gitのフックスクリプトを紹介します。

git フックとは

他のバージョン管理システムと同じように、gitにも特定のアクションが発生した時にスクリプトを叩く機能があります。
フックはクライアントサイドとサーバーサイドの二つのグループに分かれており、
クライアントサイドフックはコミットやマージといったクライアントでの操作用に、
サーバーサイドフックはプッシュされたコミットを受け取るといったサーバーでの操作用に利用されます。
※今回はクライアントサイドのgit フックについて紹介します。

インストール

フックはgitディレクトリのhooksサブディレクトリに格納されており、デフォルトでは.git/hooksディレクトリになります。
gitはデフォルトでこのディレクトリにサンプルとなるスクリプトが用意されていて、
以下のようにフックファイルの末尾は.sampleとなっています。
フックスクリプトを有効にするには、ファイルの名をリネームして”.sample”を外せばOKです。

$ ls .git/hooks
applypatch-msg.sample           post-update.sample              pre-commit.sample               pre-rebase.sample               update.sample
commit-msg.sample               pre-applypatch.sample           pre-push.sample                 prepare-commit-msg.sample

 

代表的なフックファイルの紹介

以下にcommit時、push時の代表的なフックファイル名を挙げます。

commitコマンドのフック

  • pre-commit コミットメッセージが入力される前に実行される。
  • prepare-commit-msg コミットメッセージエディターが起動する前に実行される。
  • commit-msg コミットメッセージエディター起動時に実行される。

pushコマンドのフック

  • pre-push pushを実行する前にクライアントで実行される。
  • pre-receive pushを受信したらサーバーサイドで実行される。
  • post-receive pushが完了したらサーバーサイドで一度だけ実行される。

では、具体的に①~③に対するgitフックの設定を以下に紹介していきます。

動作環境

動作確認環境は以下です。
Mac OSX
git version 1.9.5

①「特定のブランチへ間違ってpushしてしまった」への対策

→クライアント側で特定のブランチへpushできないように設定します。
※本来はサーバー側で特定のユーザー以外pushできないように設定するのが理想なのですが、
プロジェクトで利用しているソフトウェア(GitBucket)上で実現する方法がわからなかったため、
クライアント側のpush不可にする設定を紹介します。
対応方法がわかり次第また記事にしたいと思います。

.git/hooks/pre-pushで”master”というブランチへpushできないように設定します。

まず、pre-pushファイルを作成します。
※シェルで記載されているため、実行権限付与を忘れると動かないので注意してください!(筆者はこれで1時間ほどハマりました。。)

vi .git/hooks/pre-push
chmod +x .git/hooks/pre-push

pre-pushファイルへ以下のコードを記述します。

#!/bin/sh

remote="$1"
url="$2"

while read local_ref local_sha remote_ref remote_sha
do
if [[ "$remote_ref" =~ ^.*/(master)$ ]]; then
echo "$remote_ref には直接pushできません。"
exit 1
fi
done

exit 0

これで、”master”ブランチに直接pushができなくなりました。
pushしようとすると、以下のようなメッセージが表示され、pushがエラーで中止されます。

$ git push origin master
refs/heads/master には直接pushできません。
error: failed to push some refs to 'http://xxx.xxx.xxx.xxxx:8080/git/hogehoge'

②「コミットコメントが空で何の対応をしたかわからない。」への対策

→コミットコメントに空をNG、かつRedmineのチケット番号(#番号)が必須という設定をします。
これならRedmineのチケット番号と紐付けができればどのような対応をしたかが判断できるからです。
また、RedmineとGitの連携により、コミットコメントによりRedmineのチケットのステータス等
を更新することもできるので、コミットコメントにチケット番号必須としておくと便利です。

commit-msgファイルを生成(&実行権限付与)

vi .git/hooks/commit-msg
chmod +x .git/hooks/commit-msg

commit-msgファイルへ以下のコードを記述します。

#!/bin/sh

if grep "\(refs\|fixes\) #[0-9]\+" $1 > /dev/null; then
  echo "commit ok"
  exit 0
else
  echo "コミットメッセージにチケット番号を入れてください!!"
  exit 1
fi

コミットメッセージにチケット番号(#番号)を入れずにコミットをすると、
以下のようなエラーメッセージが表示され、コミットが失敗します。

$ git commit -a -m "とりあえずコミット"
コミットメッセージにチケット番号を入れてください!!

 

③「変更してはいけないファイルを誤ってコミットしてしまった。」への対策

→特定のファイルを変更したらコミットできないように設定します。
DB接続定義ファイル等、プロジェクト全体に影響を及ぼす重要なファイルについては、
特定の管理担当者以外は変更権限を与えないようにしたいので、
変更権限の無い開発者はクライアントのコミット時点でNGという設定をします。

例えばhoge.xmlファイルの変更があればコミットを禁止するには以下の様なpre-commitフックを作成します。

pre-commitファイルを生成(&実行権限付与)

vi .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

pre-commitファイルへ以下のコードを記述します。

#!/bin/sh

unchangeable_files=(
  ^hoge.xml$
)

containsElement () {
  local e
  for e in "${@:2}"; do [[ "$1" =~ $e ]] && return 0; done
  return 1
}

for FILE in `git diff --cached --name-status $against -- | cut -c3-`; do
  if containsElement $FILE "${unchangeable_files[@]}"; then
    echo "$FILE"
    CHANGE_DETECTED=1
  fi
done

if [ "$CHANGE_DETECTED" ]; then
  echo "変更禁止ファイルが変更されているため、コミットできません。"
  exit 1
fi

hoge.xmlファイルを変更してコミットしようとすると、
以下のようなエラーメッセージが表示され、コミットが失敗します。

$ git commit -a
hoge.xml
変更禁止ファイルが変更されているため、コミットできません。

以上です。
実際のプロジェクトで開発メンバーで上記のクライアントのフックスクリプトを配布する際は、
以下のようなsetupシェルを用意しておき、
チェックアウト後に実行するようにすると開発メンバーが増えた際に便利です。

#!/bin/sh

# プロジェクトのディレクトリへ移動
cd ~/project_xxx/

#/setup/hooks/に配置したhookスクリプトをgit/hooks/へコピー
cp ./setup/hooks/pre-push .git/hooks/
cp ./setup/hooks/pre-commit .git/hooks/
cp ./setup/hooks/commit-msg .git/hooks/
# 権限付与
chmod +x .git/hooks/pre-push
chmod +x .git/hooks/pre-commit
chmod +x .git/hooks/commit-msg

今回はクライアントサイドでの特定のファイルやブランチへのコミット等の防止を紹介しましたが、
このフック機能を使えば他にも、
「特定の文言(例えばTODOやFIXME等)が入っていたらコミットNGとする」、
「push時にローカルの単体テストコードが実行されるようにする」、
「push時にメールが飛ぶように設定する」等の設定も可能です。
今回はクライアントサイドのフックスクリプトの紹介をしたので、
次回はサーバーサイド編を記事にしたいと思います。
みなさんもgitのフックスクリプト機能を使ってみてはいかがでしょうか。

Share on Facebook0Tweet about this on TwitterShare on Google+0Share on LinkedIn0Share on Tumblr0