3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Git Hook pre-commit プログラミングの仕組みと解説

Last updated at Posted at 2020-04-26

Git Hook pre-commit プログラミングの仕組みと解説

About Hook

Hookとは、ある機能を実行する前後に、ユーザーにプログラムを実行する機会を与える機能です。

git hook とは、commit や push などの命令をする際、命令が実行される前や後に、シェル・スクリプトを実行できる機能です。

pre-commit や pre-push のポピュラーな機能として、クラウドサーバーの秘密キーの公開を抑止するというのがあります。

pre とは、 previous の略で、〇〇する前という意味です。

About git hook

リポジトリーをクローンしたディレクトリーには、.git という隠しディレクトリがあります。そのディレクトリーの中に、 hooks というディレクトリーがあり、ここに git hook で実行したいシェル・スクリプトを作成・設置します。

gitの特定のコマンド( commit や push )を実行すると、.git/hooks ディレクトリーに保存されているシェル・スクリプトが実行されます。これが git hook です。

保存したファイル名で hook されますので、任意のファイル名は使えません。

よく使われる git hook には以下のようなものがあります。

  • master に push したら、自動でサーバーにデプロイする
  • master を更新したら、自動でユニットテストが実行される
  • push する前に、特定の文字列やフォーマットをチェックする

できることの一覧は、公式サイトgit hook はじめの一歩 をご覧ください。
サンプルファイルは、 .git/hooks ディレクトリに入っています。
何ができるか、どのように使うか、サンプルファイルをざっと眺めるのが一番勉強になります。個人的には git diff のオプションについて、とても参考になりました。

Point

git hook用の命令や変数などはありません。 (多分😅)
コミットやプッシュのタイミングで、シェル・スクリプトが実行されるだけです。
スクリプトでは、既存のgitのコマンドを使って、更新されたファイルの一覧を取得したり、差分をチェックしたりします。

Important - 強制コミット

例えば、var_dump という文字列を発見したら、commit や push を禁止するというスクリプトを書いたとします。しかし、var_dumpが必要で、どうしてもコミットしたいという場合があります。

そのような場合は git hook をスキップする(実行しない)というオプションを付与します。具体的なオプションは、-n--no-verify の略) ですが、git commit の場合は、-mの前に付与する必要があります。

git commit -nm "Commit message"

↑nは、mの前に付ける。逆だとエラーになります。

Script

今回は、 var_dumpprint_rexit が記述されていたら push できないというスクリプトを書きました。

スクリプトが、1を返すと命令は中断され、pre-commit の場合はコミットが実行されません。

vi .git/hooks/pre-commit

pre-commit
#!/usr/bin/env bash

# Case sensitive
shopt -s nocasematch

# Change delimiteir to only LF
IFS=$'\n'

# Get target files
files=`git diff --cached --name-only`;

# Loop for each file name
for file in $files
do
  # Get diff lines
  LINES=`git diff --cached "$file"`

  # Loop for each lines
  for LINE in $LINES
  do
    # Skip delete line
    if [[ $LINE =~ ^- ]]; then
      continue
    fi

    # Search deny word
    if [[ $LINE =~ var_dump|print_r|exit ]]; then
      # Error message
      echo
      echo ".git/hooks/pre-commit : $file --> ${BASH_REMATCH[0]}"
      echo
      echo "$LINES"
      exit 1
    fi

  done # end each lines
done   # end each files

# No problem
exit 0

余談

シェル・プログラミングは普段おこなわないので、PHPで書きたいと思ったのですが、シェバンを #!/usr/bin/env php として実行したところ、上手くいかずに断念しました。

また、シェルの正規表現でハマりました。

そもそも正規表現のやり方が分からない。

if [[ $LINE =~ ^.+$ ]] という記法で正規表現が使えます

for in ループで、スペース毎で区切られる → 行毎で取得できない

git diff の差分を for ループしようとしたら、差分が 『行』 毎ではなく、『単語』 毎に変数へ代入された。

シェルの区切り文字のデフォルトは、改行・タブ・スペースだったので、改行(LF)だけに変更した。

# Change delimiteir to only LF
IFS=$'\n'

大文字・小文字の区別(ケースセンシティブ)をしない

# Case sensitive
shopt -s nocasematch

こちらの記事が参考になりました。

https://qiita.com/myblackcat7112/items/5e8205f1b5161a1f8590
https://qiita.com/kazu56/items/83340a2e284298b9e237

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?