先日、globalなgit-hooksを設定して、すべてのリポジトリで共有のhooksを使うという記事で、git-hooksをグローバルに展開する方法を書きました。
今回は、それとは逆にRepositoryごとに個別にスクリプトを展開することが目的です。
グローバルなgit-hooksを用意したくないケース
基本的にはグローバルなgit-hooksの設定を用意すればいいですが、Repositoryごとに行うかどうかを変えたいということもあります。
例えば、会社のgitリポジトリにコミットする際に、git.emailが個人のものになっていないかチェックするスクリプトは、会社のRepositoryには適用したいですが、個人のRepositoryには適用したくないといった形です。
これを実現するために以下のスクリプトを書きました。
#!/bin/bash -eu
function echo_usage() {
echo "usage: ${0} target_git-hooks_name target_git_dir_root_path"
exit 1
}
if [ $# -ne 2 ]; then
echo_usage
fi
target_script=$1
target_dir=$2
GIT_ROOT=$(git rev-parse --show-superproject-working-tree --show-toplevel | head -1)
SCRIPT_DIR="${GIT_ROOT}/git-hooks"
if [ ! -e "${SCRIPT_DIR}/${target_script}" ]; then
echo "${SCRIPT_DIR}/${target_script} not found."
echo ""
echo "ls ${SCRIPT_DIR}/*"
ls ${SCRIPT_DIR}/*
echo ""
echo_usage
fi
if [ ! -d "${target_dir}/.git" ]; then
echo "${target_dir} is not git root dir."
echo ""
echo_usage
fi
script_name=$(echo ${target_script} | cut -d "." -f 1)
script_type=$(echo ${target_script} | cut -d "." -f 2)
# まだ対象のhooksファイルがない場合、自動生成
if [ ! -e "${target_dir}/.git/hooks/${script_type}" ]; then
echo '#!/bin/bash' >>"${target_dir}/.git/hooks/${script_type}"
echo "" >>"${target_dir}/.git/hooks/${script_type}"
chmod 700 "${target_dir}/.git/hooks/${script_type}"
fi
ln -s "${SCRIPT_DIR}/${target_script}" "${target_dir}/.git/hooks/__${script_name}"
echo "source \$(dirname \${BASH_SOURCE})/__${script_name}" >>"${target_dir}/.git/hooks/${script_type}"
使い方
以下のようなディレクトリ構成でスクリプトを用意します。
.
├── README.md
├── git-hooks
│ └── personal_address_not_confirm.pre-commit
└── script
└── copy-git-hooks.sh
ここでgit-hooksディレクトリには、スクリプトの名前.git-hookのアクション名
という命名規則で、スクリプトのテンプレートを用意しておきます。
たとえば、先程例にあげた「会社のgitリポジトリにコミットする際に、git.emailが個人のものになっていないかチェックするスクリプト」であれば、以下のようになります。
#!/bin/bash
# personal_address_not_confirm
## git configをチェックして、user.emailが個人アドレスになっていないか確認
COMPANY_DOMAIN="example.com"
git config user.email | grep -i ${COMPANY_DOMAIN} > /dev/null
if [ $? -ne 0 ]; then
echo "git user.email is $(git config user.email)."
echo "It is not a company email address."
exit 1
fi
これをスクリプトを使って、gitリポジトリに展開するときは、以下のコマンドを実行します。
./script/copy-git-hooks.sh personal_address_not_confirm.pre-commit /path/to/target-git-repository
これを実行することで、ファイル拡張子から自動的にアクション名を取得、.git/hooks/
以下にスクリプトを配置します。
.git/hooks/にファイルが存在しない場合は、自動作成も行います。
また、配置されるスクリプトは、__${スクリプト名}
という命名で.git/hooks/
以下にシンボリックリンクを貼った上で、各アクションのスクリプトからsoruceで呼び出す形になっています。
たとえば、上記のコマンドで personal_address_not_confirm.pre-commit
を展開した場合、 /path/to/target-git-repository/.git/hooks/pre-commit
には以下が書き込まれます。
#!/bin/bash
source $(dirname ${BASH_SOURCE})/__personal_address_not_confirm
そして、__personal_address_not_confirm
は、personal_address_not_confirm.pre-commit
へのシンボリックリンクになっています。
$ ls -la /path/to/target-git-repository/.git/hooks/
drwxr-xr-x 16 user staff 512 2 18 05:56 ./
drwxr-xr-x 12 user staff 384 2 18 05:33 ../
lrwxr-xr-x 1 user staff 92 2 18 05:56 __personal_address_not_confirm@ -> /path/to/script_dir/git-hooks/personal_address_not_confirm.pre-commit
-rwx------ 1 user staff 67 2 18 05:56 pre-commit*
(以下略)
これにより、git-hooksスクリプトを機能ごとに分割・一括管理した上で、Repositoryごとに展開できるようになりました。