6
4

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 3 years have passed since last update.

Git Hooksでgit cloneだけをフックする

Last updated at Posted at 2019-11-27

やりたいこと

言わば、post-cloneフックです。git clone実行後だけリポジトリディレクトリに対して特定の処理がしたい。その際、clone先ディレクトリに応じて処理を分岐させたい。つまり、git clone --templateではうまくいかないケース。

結論

workaround的なもので、他のフックとは違ってスクリプトファイル1つでお終いというものではないのです。また、Bashの例ですが、他のシェルでも考え方は流用できると思います。

  • trap DEBUGgit cloneを叩いたことを検知して、コマンド実行前にフラグを立てる。
  • post-checkoutスクリプト内でフラグが立っているときだけ処理を行い、処理後フラグを倒す。
  • 今回の記事の本筋ではないが、注意点として、GitHooksは標準入力を利用しないので、処理の中で入力を待ちたい場合はターミナルからリダイレクションする必要あり。

背景

前の記事で、gitアカウントを自動で切り替えるためにgit clone前に処理をする必要があったので、cdgitのラッパー関数を作り、エイリアスで元のcdgitを上書きして、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の記事がわかりやすいです。

参考

6
4
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
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?