前提
mainブランチやmasterブランチで動作を確認している中で、そのままコードを修正し、勢いでコミットまでやってしまい、後から「あれ?作業ブランチ切れてなくね・・・?」したことありませんか?
私はあります。
リモートにpush前ならすぐに$ git reset --soft HEAD^
して作業ブランチでコミットし直せばいいですし、push後ならgit revertしたらいいですが、それも手間なので根本的に制限しておきます。
同じGitHooksを使って、以前はこんな記事も書きました。
【Git Hooks】コミット時にコミットメッセージの自動チェックを行う。【commit-msg】
この記事ではmainブランチなど主要ブランチへのコミットの制限方法について解説していきます。
Git Hooksとは
Git Hooksとはgitに標準で備わっている機能の一つで、git commit
やgit push
が実行された際に、スクリプトを実行することができ、そのスクリプトの実行結果によってコマンドを通すかどうかを自動的に判断させることができます。
8.3 Git のカスタマイズ - Git フック
実行されるスクリプトファイルは以下の場所に置かれています。
$ ls .git/hooks
applypatch-msg.sample post-update.sample pre-merge-commit.sample pre-receive.sample update.sample
commit-msg.sample pre-applypatch.sample pre-push.sample prepare-commit-msg.sample
fsmonitor-watchman.sample pre-commit.sample pre-rebase.sample push-to-checkout.sample
これらのファイル名は予約されていて、gitコマンドが実行されたときに任意のタイミングでそれぞれのスクリプトが動作することになっています。
デフォルトでは全てのファイルの拡張子が.sample
となっているため、どのスクリプトファイルも動作していませんが、拡張子.sample
を取り除くと、gitコマンドのそれぞれのタイミングでファイルに記述されているスクリプトが実行されます。
(シェルスクリプトの拡張子.sh
は不要です。)
完成形
作業ブランチのチェック
ここではコミットが実行される前に、現在作業中のブランチが事前に設定したブランチ(main, master)かどうかを判定し、
該当するならコミットの実行を取り消し、
該当しなければコミットの実行を行う(次のGitHooksを実行する)。
ということをやっていきます。
準備
先に以下のコマンドでgit環境を作っておきます。
$ mkdir sample; cd sample; git init; touch test.txt
次にデフォルトで用意されているGitHooksスクリプトを確認します。
$ ls .git/hooks
applypatch-msg.sample post-update.sample pre-merge-commit.sample pre-receive.sample update.sample
commit-msg.sample pre-applypatch.sample pre-push.sample prepare-commit-msg.sample
fsmonitor-watchman.sample pre-commit.sample pre-rebase.sample push-to-checkout.sample
作業ブランチのチェックで必要になるのは、pre-commit
というファイルです。
元から用意されているpre-commit.sample
には手をつけず、新規で作成していきます。
$ vi .git/hooks/pre-commit
pre-commitファイルを作成し以下のように書きます。
#!/bin/bash
echo -e "\033[37;1m🪝 Running Git Hooks: pre-commit\033[0m"
# 現在のブランチ名を定義
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
readonly BRANCH_NAME
# 終了コードを定義。0: OK, 1: NG
code=0
# コミットするブランチ名のチェック
echo -en " - コミットするブランチ名のチェック: "
## コミットを拒否するブランチ名を定義
readonly INCORRECT_BRANCHES=("master" "main")
if echo "${INCORRECT_BRANCHES[@]}" | grep -q "$BRANCH_NAME"; then
echo -e "\033[31;22mNG\033[0m"
echo -e "\033[31;22m================================================================"
echo -e "${BRANCH_NAME}ブランチへのコミットは禁止されています。"
echo -e ""
echo -e "禁止されているブランチ"
echo -e " ${INCORRECT_BRANCHES[*]}"
echo -e "================================================================\033[0m"
code=1
else
echo -e "\033[32;22mOK\033[0m"
fi
# 終了宣言
if [ ${code} -eq 0 ]; then
echo ""
echo -e "\033[32;1m✨ALL PASS!!\033[0m\n\n"
else
echo ""
echo -e "\033[31;1mGit Hooks: pre-commit: NG\033[0m\n\n"
fi
exit ${code}
コードの解説は後述します。
最後に、作成したスクリプトファイルに実行権を与えます。
$ chmod a+x .git/hooks/pre-commit
解説
#!/bin/bash
シバン行。
このスクリプトはbash($
)環境用に作成しました。
zsh(#
)等他の環境でshellを使用されている場合は文字の色など正常に表示されない可能性があります。
echo -e "\033[37;1m🪝 Running Git Hooks: pre-commit\033[0m"
# 現在のブランチ名を定義
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
readonly BRANCH_NAME
# 終了コードを定義。0: OK, 1: NG
code=0
-
echo -e "\033[37;1m🪝 Running Git Hooks: pre-commit\033[0m"
pre-commitが実行されていることをターミナルに出力します。 -
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
-
readonly BRANCH_NAME
変数BRANCH_NAME
に$ git rev-parse --abbrev-ref HEAD
の実行結果、つまり現在のブランチ名を代入します。
readonlyで変数BRANCH_NAME
は読み込み専用となるため、再代入等をできないようにします。 -
code=0
このスクリプトがエラー終了なのか正常終了なのかのフラグです。
初期値0は正常終了、つまりコミットのプロセスが問題なく実行されます。
この後のチェック項目(バリデーション等)に失敗すると、codeに1を代入しています。
# コミットするブランチ名のチェック
echo -en " - コミットするブランチ名のチェック: "
## コミットを拒否するブランチ名を定義
readonly INCORRECT_BRANCHES=("master" "main")
-
echo -en " - コミットするブランチ名のチェック: "
-e
出力文字の色を変えるためのオプション
-n
末尾に改行を付けない。ので、次にechoで出力される文字列は続けて横に表示されます。 -
readonly INCORRECT_BRANCHES=("master" "main")
変数INCORRECT_BRANCHES
を宣言し、静的な配列を代入しています。
静的な値を代入する際は変数の宣言と同時にreadonly
をつけることができます。
ここで代入している配列はコミットを禁止するブランチ名です。
if echo "${INCORRECT_BRANCHES[@]}" | grep -q "$BRANCH_NAME"; then
echo -e "\033[31;22mNG\033[0m"
echo -e "\033[31;22m================================================================"
echo -e "${BRANCH_NAME}ブランチへのコミットは禁止されています。"
echo -e ""
echo -e "禁止されているブランチ"
echo -e " ${INCORRECT_BRANCHES[*]}"
echo -e "================================================================\033[0m"
code=1
else
echo -e "\033[32;22mOK\033[0m"
fi
-
if echo "${INCORRECT_BRANCHES[@]}" | grep -q "$BRANCH_NAME"; then
制限するブランチの配列に対してgrepコマンドを使って、現在のブランチ名が含まれているかどうかで成否判定を行います。
-q
オプションはターミナルにgrepコマンドの結果を出力させません。
# 終了宣言
if [ ${code} -eq 0 ]; then
echo ""
echo -e "\033[32;1m✨ALL PASS!!\033[0m\n\n"
else
echo ""
echo -e "\033[31;1mGit Hooks: pre-commit: NG\033[0m\n\n"
fi
exit ${code}
-
if [ ${code} -eq 0 ]; then
ここまでのチェックで問題がなければcodeの値は0
のままとなるので✨ALL PASS!!
の文字を表示させるか、
チェックに問題がありcodeの値が1
になっていればGit Hooks: pre-commit: NG
の文字を表示させ、
最後にexitで正常終了か異常終了します。
このスクリプトが正常終了の場合はgit commiitコマンドが実行され、異常終了の場合はgit commitコマンドは取り消されます。
実行
実際に動かしていきます。
mainブランチ等の制限されたブランチに移動し、準備の所で用意したtest.txtをステージングし、コミットメッセージは仮に'test'としてコミットしてみます。
$ git branch
$ git commit -m 'test'
$ git log --oneline
を確認すると、先ほどのコミットが実行されていないことが確認できます。
次に正常終了するパターンを確認します。
developブランチを作成して移動し、同様にコミットしてみます。
$ git checkout -b develop
$ git branch
$ git commit -m 'test'
これで再度$ git log --oneline
を確認するとコミットができていることが確認できるかと思います。
コミット時にスクリプトの実行をスキップしたい場合
スクリプトの実行をスキップしたい場合は--no-verify
オプションつけてgit commitを行えば、チェックを通さず、コミットが即時に実行されます。
まとめ
今回ご紹介したスクリプトファイルは私のGitHubリポジトリにも同じものを置いてあります。
参考になれば幸いです。
参考
【Git Hooks】コミット時にコミットメッセージの自動チェックを行う。【commit-msg】
シェルスクリプトのechoで”問題なく”色をつける(bash他対応)
git rev-parseを使いこなす