Help us understand the problem. What is going on with this article?

git 2.9 の core.hooksPath でフックスクリプトの一元管理をやってみた

More than 3 years have passed since last update.

git 2.9 の新機能で core.hooksPath という設定が出来るようになったようだ。

これは何かというと、今まで git のフックスクリプト置き場は リポジトリルート/.git/hooks というパスに固定だったのが、core.hooksPath を設定することでその場所を変えられるようになったというもののようだ。

指定可能なのは以下の3種類のどれか。

  • 絶対パス (e.g. /home/kawaz/.config/git/hooks)
  • ~ から辿れるパス (e.g. ~/.config/git/hooks)
  • 相対パス(リポジトリルートから) (e.g. myhooks が指定してあったら リポジトリルート/myhooks の意味)

僕の事情:dotfiles のパスが不定なん…

大抵の環境では上述した3種類のパス記述で十分かもしれないが、僕は自分の設定を kawaz/dotfiles で管理していて、これを各所で clone して使っている。そういった運用の都合上、僕の dotfiles はポータビリティを重視して最近は XDG Base Directories (参考[1],[2]) になるだけ準拠した感じに育てています。

で、実際の環境でどうなるかというと clone した先が ~/.dotfiles~/.kawaz/dotfiles だったり、ケースバイケースで置き場が変わってくるので、hooks ディレクトリの場所が絶対パスでも~からのパスでも相対パスでも書けなくなってしまいます。

それだと困るので苦肉の策として以下のようにしています。

  • まず $XDG_CONFIG_HOME/bash/rc/applications/git.sh (bashrcからsourceされる) が、$XDG_CACHE_HOME/gitconfig.local を無ければ作ります。
  • $XDG_CACHE_HOME/gitconfig.local はスクリプトから作られるので $XDG_CONFIG_HOME/git/hooks のパスを解決済みのフルパス状態で core.hooksPath にセットする感じのファイルになります。
  • 仕上げに $XDG_CONFIG_HOME/git/config から ../../cache/gitconfig.local を include して繋がります。
    • ちなみに僕の環境では XDG_CONFIG_HOMEXDG_CACHE_HOME の値はそれぞれ /path/to/dotfiles/{config,cache} になるようにしているので、config/git/config から cache/gitconfig.local は決め打ちの相対パスで書けるわけです。

これで無事自前の dotfiles リポジトリ内にポータビリティを保った形で共通フックスクリプトを管理できるようになりました。
めでたしめでたし。

練習用フックスクリプト:log.sh

今まで git のフックスクリプトはポータビリティが微妙で使いこなすモチベが無かったのでそっちの資産は全然ない。だけど今回の機能追加でスクリプトが外出し管理できるようになったので今後は充実させてみようかな、と。

とりあえずどんなフックがあってどんな値が渡されてくるのかの調査用に環境変数とコマンド引数をログに取る為の log.sh というスクリプトを作ってみました。

config/git/hooks/log.sh
#!/bin/bash
quoted=()
for t in "$0" "$@"; do
  quoted+=("$(printf %q "$t")")
done

log="${TMPDIR:-/tmp}/git-hooks-log.$(date +%w)"
find "$log" -maxdepth 0 -mtime +1 -delete 2>/dev/null
{
  ts=$(date +%Y-%m-%dT%H:%M:%S.%3N%z)
  printf "%s cd %q && " "$ts" "$PWD"
  for e in $(compgen -e GIT_); do
    printf '%q=%q ' "$e" "${!e}"
  done
  printf "%s\n" "${quoted[*]}"
} >> "$log"

# 作業のじゃまをしないようキッチリ0で終了する
exit 0

こいつは何をするかというと $TMPDIR/git-hooks-log.[0-6] というファイル名にフックスクリプトがどう呼ばれたかのログを取ります。(ファイル名の数字部分は曜日です、追記前に1日以上古いログだったら削除してるので1周間で雑にローテされます)

で、とりあえずこいつを全てのフックスクリプトから symlink してみます。フックスクリプト名は githooks ドキュメント からクライアント側フック名を全部コピペで列挙してきました。

全てのフックをlog.shで受けてみる
cd "$XDG_CONFIG_HOME/git/hooks"
for f in applypatch-msg pre-applypatch post-applypatch pre-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-push; do
  ln -sfn log.sh $f
done

で、おもむろに何かコミットしてみたりプッシュしてみたりします。すると↓こんなログが出ます。

$TMPDIR/git-hooks-log.4
2016-06-16T00:15:04.972+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1465999617\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_INDEX_FILE=.git/index GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/pre-commit
2016-06-16T00:15:04.993+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1465999617\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_INDEX_FILE=.git/index GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/prepare-commit-msg .git/COMMIT_EDITMSG commit HEAD
2016-06-16T00:22:25.959+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1465999617\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_INDEX_FILE=.git/index GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/commit-msg .git/COMMIT_EDITMSG
2016-06-16T00:22:25.987+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1465999617\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_INDEX_FILE=.git/index GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/post-commit
2016-06-16T00:22:26.012+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1465999617\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/post-rewrite amend
2016-06-16T00:23:14.834+0900 cd /Users/kawaz/.dotfiles && GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/pre-push origin git@github.com:kawaz/dotfiles.git
2016-06-16T01:04:17.115+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1466006657\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_EDITOR=: GIT_INDEX_FILE=/Users/kawaz/.dotfiles/.git/next-index-72309.lock GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/pre-commit
2016-06-16T01:04:17.132+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1466006657\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_EDITOR=: GIT_INDEX_FILE=/Users/kawaz/.dotfiles/.git/next-index-72309.lock GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/prepare-commit-msg .git/COMMIT_EDITMSG message
2016-06-16T01:04:17.147+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1466006657\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_EDITOR=: GIT_INDEX_FILE=/Users/kawaz/.dotfiles/.git/next-index-72309.lock GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/commit-msg .git/COMMIT_EDITMSG
2016-06-16T01:04:17.164+0900 cd /Users/kawaz/.dotfiles && GIT_AUTHOR_DATE=@1466006657\ +0900 GIT_AUTHOR_EMAIL=kawazzz@gmail.com GIT_AUTHOR_NAME=Yoshiaki\ Kawazu GIT_DIR=.git GIT_EDITOR=: GIT_INDEX_FILE=.git/index GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/post-commit
2016-06-16T01:04:21.806+0900 cd /Users/kawaz/.dotfiles && GIT_PREFIX='' /Users/kawaz/.dotfiles/config/git/hooks/pre-push origin git@github.com:kawaz/dotfiles.git

ローカルのリポジトリパス、GIT_* な環境変数、呼ばれたフックスクリプト名、とその引数、がいい感じでログに出るようになりました。諸々エスケープも抜かり無い筈なのてコピペで再実行も簡単にできます。
今後はこれの情報を参考に便利そうなフックスクリプトを作っていきたいなと思ってます。

フックスクリプトの呼び出しログの閲覧コマンドも作っとこう

折角フックスクリプトの呼び出しログが綺麗に出せるようになったのに、出力ファイル名がローテ考慮してるせいで cat しにくいのがいまいち。そこでそれも簡単に見られるよう hooks-log というエイリアスを定義してみました。

[alias]
  # see config/git/hooks/log.sh
  hooks-log = !"ls -rt \"${TMPDIR:-/tmp}\"/git-hooks-log.[0-6]|while read -r f;do [[ -f $f ]] && cat \"$f\";done"

これで git hooks-log を実行すればローテファイルを古い順に cat してくれるので最新ファイルを探す必要もなくログがみれるようになりました。便利。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away