やりたいこと
デバッグ中に一時的に入れたコード等を間違えてコミットしてしまわないようにファイル中に NEVER_COMMIT
という文字が入っていたら Git コミットできないようにする。
動作を確認した Git クライアントのバージョン → git version 2.20.1 (Apple Git-117)
。
個別の Git プロジェクトディレクトリのみで設定
.git/hooks/pre_commit
を作成。内容は以下のような感じ。
#!/bin/sh
NG_WORD="NEVER_COMMIT"
git diff --cached -U0 | grep ${NG_WORD}
if [ $? -eq 0 ]; then
echo "[ERROR] Can't commit because ${NG_WORD} is detected"
git grep --cached $NG_WORD
exit 1
else
exit 0
fi
実行権限が必要なので付与しておく。
$ chmod +x .git/hooks/pre-commit
ローカルでグローバル設定化(テンプレート化)
ローカルでグローバルって新鮮な古米みたいな響きだけど
$ mkdir -p ~/.git-templates/hooks
$ git config --global init.templatedir '~/.git-templates'
$ vim ~/.git-templates/hooks/pre-commit
(スクリプト貼り付ける)
$ chmod +x ~/.git-templates/hooks/pre-commit
これで今後ローカル(PC内)で git init
や git clone
すると作成される Git のプロジェクトディレクトリに自動的にスクリプトがコピーされる。
一元管理
お察しの通り、前項の方法だと既に git init
git clone
済みのプロジェクトに適用されない。さらにスクリプトを書き換えてグローバルに(全プロジェクトに)適用したいときにコピー済みの全ファイル全部書き換え直す必要があって手間かかること山の如しなので
$ git config --global init.templatedir '~/.git-templates'
じゃなくて
$ git config --global core.hooksPath ~/.git-templates/hooks
ってしておいたほうがおすすめ(最初からそう書けばいいのにね)。
この場合 .git-templates/hooks
という名前は微妙なので .git-hooks
とかそれっぽいものにすると良い。
シェルスクリプト(というより主に Git コマンド)の説明
git diff
は何もオプションを指定しないと git add
されていないワーキングコピーの差分のある行とその前後を表示する。 --cached
を指定すると git add
されているファイル(staged files)のみを対象とする。差分表示時に差分の前後行の diff は必要ないので -U0 で差分行のみを抽出している。表示された結果にNGワードが含まれていればその行が取得される。
$?
は直前に実行したコマンドの結果が取得できる。diff があるときは正常終了 = 0 となっているはずで、もしNGワードがコミット対象に含まれていれば 0 と比較して等しくなる。
どのファイルにNGワードが含まれているのかわかりやすいように git grep
で該当ファイルと該当行を表示している。ここもやはり git add
済みのファイルだけを対象としたいので --cached
オプションを付与している。
exit 0
なら正常終了なのでコミットは続行され、 exit 1
なら異常終了なのでコミットは実行されない。
ちなみに git add
せず git commit -a
などとしたときもシェルスクリプト内の --cached
オプションは対象ファイルを git add
している場合と同様に動く。
それはともかく
git add
するときに -p
付けるとか、 git commit
する前に git diff --cached
見て意図していないコードが含まれていないか見ろよって話ですよ。