👊

Gitの特定ブランチへのコミットをLefthookで防止する

に公開

はじめに

GitHubのルールセットを活用することで特定ブランチ、例えばmainmasterブランチへのプッシュを防止することができます。しかし、この機能は

ルールセットは、GitHub Free と GitHub Free の Organization のパブリック リポジトリ、GitHub Pro、GitHub Team、GitHub Enterprise Cloud のパブリックとプライベートのリポジトリで利用できます。
https://docs.github.com/ja/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets

とある通り、GitHub Freeを利用しているプライベートリポジトリでは適用することができません。そこで、この記事ではGitのフック機能を利用することで特定のブランチへのコミットを防止します。

フックの管理にLefthookを利用しています。これにより、commitlint等の他のフックとも容易に共存させることができます。

この記事のゴール

  • mainブランチへのコミットを防止する
  • ただし、[commit-main]がコミットメッセージに含まれる場合のみ許可する

フックの設定

https://lefthook.dev/installation/index.html
各言語のパッケージマネージャー等を利用してインストールを完了させてください。

続いて、以下の2ファイルを作成します。

.lefthook/commit-msg/prevent-main-commit.sh
#!/bin/sh

COMMIT_MSG_FILE=$1

if ! grep -q "\[commit-main\]" "$COMMIT_MSG_FILE"; then
  echo "Error: You cannot commit to the main branch without the [commit-main] tag in the commit message."
  exit 1
fi
sed -i "s/\[commit-main\]//g" "$COMMIT_MSG_FILE"
lefthook.yml
commit-msg:
  scripts:
    "prevent-main-commit.sh":
      runner: bash
      only:
        - ref: main

.lefthook/commit-msg/prevent-main-commit.shが処理の本体です。$1には、commit-msgフックを実行したときにコミットしようとしているコミットメッセージが記載されたファイルのパスが代入されています。そのファイルの内容を読み取り、[commit-main]が含まれていなければメッセージを表示してコード1で終了します。含まれている場合には、それを取り除くようにコミットメッセージを書き換えます。終了コードが0以外になると、Gitがコミットを中止する仕様を利用しています。

lefthook.ymlでは、このスクリプトを実行方法とブランチの指定を定義しています。
なお、マージコミットはこの制限の対象外とする場合、

yml
 "prevent-main-commit.sh":
   runner: bash
   only:
     - ref: main
+  skip: merge

のようにskip設定を追加します。

また、今回はコミット時に制限を適用していますが、GitHubの挙動のようにプッシュ時に制限を適用するには、commit-msgフックの代わりにpre-pushフックを使うなどの変更が必要です。

Discussion