やりたいこと
言わば、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の記事がわかりやすいです。