やりたいこと
言わば、post-cloneフックです。git clone実行後だけリポジトリディレクトリに対して特定の処理がしたい。その際、clone先ディレクトリに応じて処理を分岐させたい。つまり、git clone --templateではうまくいかないケース。
結論
workaround的なもので、他のフックとは違ってスクリプトファイル1つでお終いというものではないのです。また、Bashの例ですが、他のシェルでも考え方は流用できると思います。
-
trap DEBUGでgit cloneを叩いたことを検知して、コマンド実行前にフラグを立てる。 - post-checkoutスクリプト内でフラグが立っているときだけ処理を行い、処理後フラグを倒す。
- 今回の記事の本筋ではないが、注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要あり。
背景
前の記事で、gitアカウントを自動で切り替えるためにgit clone前に処理をする必要があったので、cdとgitのラッパー関数を作り、エイリアスで元のcdとgitを上書きして、cdをフックにgitコマンドが使用する変数を切り替えるようにしました。しかし、何らかのプラグインを利用してcd以外でディレクトリ移動するようなケースを考えるとやはり汎用性に欠け、取り急ぎとはいえ筋が良くなかったので、潔くgit cloneを直接フックすることにしました。
意外にもgit cloneだけをフックする方法に関する記事を見かけなかったので書きました。記事がないということは全く必要とされていないのかも。
ちなみに、GitHooksとは、というお話は以下を参照。
git hook はじめの一歩
git hookでできること
問題
Git Hooksでgit cloneをフックしたいとき、post-checkoutを使う必要がありますが、これだとgit checkoutのときにも実行されます。
また、clone先ディレクトリに応じて処理を分岐させるので、共通テンプレートを設定するだけでは実現できません。
解決策
環境
macOS
sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.5
BuildVersion: 18F132
Git
git --version
git version 2.23.0
Bash
bash --version
GNU bash, version 5.0.11(1)-release (x86_64-apple-darwin18.6.0)
git cloneの実行を検知する
Gist: .bash_hooksとして置いておきました。使い方はGist冒頭に書いてあります。このGistには、ディレクトリ移動の検知や、コマンド実行前だけでなくコマンド実行後に実行される処理もまとめて書いているのですが、今回の記事に関わるポイントは以下の3点です。
- 各コマンドの実行前に
__hooks__before_each_commandが実行されるようにする
trap '__hooks__before_each_command' DEBUG
-
__hooks__before_each_command内でBASH_COMMANDで直前のコマンド名(=叩かれたコマンド名)を取得 - フックがアクティベートされていたら(この上記Gistの最後でアクティベートしてます)
__hooks__set_git_clone_flagに直前のコマンド名を渡して実行
function __hooks__before_each_command() {
local last_command=( $BASH_COMMAND )
if [ -z "$__HOOKS__ACTIVATED" ]; then
return
fi
# before each command
__hooks__set_git_clone_flag "${last_command[@]}";
if [ -z "$__HOOKS__EXECUTING_LINE" ]; then
return
fi
unset __HOOKS__EXECUTING_LINE
# before each first command of a comamnd line
}
- 直前に叩かれたコマンド名が
git cloneだった場合、__hooks__set_git_clone_flag内で__HOOKS__ON_GIT_CLONEフラグを立てる
function __hooks__set_git_clone_flag() {
if [ 'git' = "$1" ] && [ 'clone' = "$2" ]; then
echo "Setting __HOOKS__ON_GIT_CLONE=true in $filename"
export __HOOKS__ON_GIT_CLONE=true
fi
}
post-checkout内でフラグをチェックして処理する
Gist: post-checkoutとして置いておきました。.gitvariablesというファイルを置いてgitアカウントの情報をロードするようにして、そのファイル自体は~/.gitignoreに登録してトラックしないようにしてあります。
- ここでフラグをチェックして、フラグが立ってなかったら
exitしてます。
if [ -z "$__HOOKS__ON_GIT_CLONE" ]; then
exit
fi
- 処理の最後にフラグを倒します。
unset __HOOKS__ON_GIT_CLONE
GitHooks中でユーザの入力を待ちたいときの注意点
オフトピックですが注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要があります。ちょっとハマりました。
read -rp 'Type anything you like:' < /dev/tty
まとめ
コードが散在することと、暗示的にフックすることでハマったりしないように、多用には注意が必要ではありますが、cdとgit上書きより汎用的かつ拡張しやすくなった感はあります。
ちなみに、Bashのコマンドの実行前後をフックする方法の解説はChuan Ji: DEBUG trap and PROMPT_COMMAND in Bashの記事がわかりやすいです。