Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

同じgit-hooksをリポジトリ個別指定で使えるようにするスクリプトを書いた

More than 1 year has passed since last update.

先日、globalなgit-hooksを設定して、すべてのリポジトリで共有のhooksを使うという記事で、git-hooksをグローバルに展開する方法を書きました。

今回は、それとは逆にRepositoryごとに個別にスクリプトを展開することが目的です。

グローバルなgit-hooksを用意したくないケース

基本的にはグローバルなgit-hooksの設定を用意すればいいですが、Repositoryごとに行うかどうかを変えたいということもあります。

例えば、会社のgitリポジトリにコミットする際に、git.emailが個人のものになっていないかチェックするスクリプトは、会社のRepositoryには適用したいですが、個人のRepositoryには適用したくないといった形です。

これを実現するために以下のスクリプトを書きました。

copy-git-hooks.sh
#!/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が個人のものになっていないかチェックするスクリプト」であれば、以下のようになります。

personal_address_not_confirm.pre-commit
#!/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ごとに展開できるようになりました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away