Gitのローカルブランチを保護したい!
2022-07-15
azblob://2022/11/11/eyecatch/2022-07-13-protect-git-local-branch-000.jpg

こんにちは、神取です。  
Steamのサマーセールでは、いろいろカートに入れては「やる時間ないな...」と思い、結局 "The Hex" しか買いませんでした。  
"The Hex" と "Inscryption"、面白いのでみんなにやってほしい。

さて、皆さん、 Git 使ってますでしょうか?

Gitを使っている中で、"作業するつもりでないブランチ (developなど) で作業を進めてしまい、それに気づかずコミットし、プッシュしたときに初めて気づく" なんて経験ありませんか?  
(僕はあります。)

上記のような場合、プッシュ先のホスティングサービスでブランチの保護設定がされていれば、プッシュがrejectされて間違いに気づけますが、  
設定されていなければ、気を使いながら戻さないといけません。

そこで私は考えました。  
「ローカルブランチも同じように保護できたらいいのに...」と。

今回は、ブログをご覧になっている方々の、Gitのローカルブランチを保護する方法について紹介します。  
環境については、以下の通りです。

> code --version
        1.69.0
        92d25e35d9bf1a6b16f7d0758f25d48ace11e5b9
        x64
        > git --version
        git version 2.37.0.windows.1

※ Googleで git ブランチ 保護 などの語で検索すると、GitHubのBranch protection ruleによるブランチの保護についての記事がたくさんヒットします。  
これはこれで大切なのでGitHubユーザーは設定しておきましょう。  
(今回は割愛します)

VSCodeの設定による保護

VSCode v1.68で、Git: Branch protection が追加されました。  
git.branchProtection に保護したいブランチを構成することで、  
保護されたブランチに直接コミットしようとしたときに、新しいブランチを作成するよう提案されるようになります。

早速設定していきましょう。

設定

VSCodeを開いて、Ctrl + Shift + P を押してコマンドパレットを開き、settings ui と入力し、基本設定: 設定 (UI) を開く を選択します。

設定の検索に git.branchProtection と入力します。  
(ワークスペース (リポジトリ) 毎に個別の設定をしたいときは ワークスペース、全体への設定をしたいときは ユーザー を選択します)

項目の追加 をクリックし、保護したいブランチ名を入力し、OK をクリックします。

必要なだけ、保護したいブランチを追加します。  
(今回は、main, master, develop を追加しました)

これで設定は完了です。

動作確認

適当なリポジトリを開いて、保護対象にしたブランチにスイッチし、適当なファイルを作成してコミットしてみます。

すると、以下のようなダイアログが表示され、自分が保護されているブランチで作業していることに気づけることが確認できます。

新しいブランチにコミットする を選択すれば、ブランチを作成してコミット、  
キャンセル を選択すれば、コミットせずにステージング状態のままになります。  
(そのままコミット を選択すれば、保護しているブランチにコミットできます)

また、VSCode v1.69で、git.branchProtection によって保護されたブランチのアイコンが閉じた鍵になるようになりました。  
可愛いですね。

🔒

Git Hooks による保護

VSCodeユーザーは、前述の設定で十分かと思います。  
しかしながら、「エディタはVimしか勝たん!VSCode使ってない!」という方や、「Gitはコマンドを打ってこそ!GUIは甘え!」という方もいるかと思います。  
そういった方向けに、Git Hooksを使用したブランチの保護について紹介します。

Git Hooks とは、Gitの特定の操作の時点でアクションを実行できる機能のことです。  
詳しい機能については、リンク先を参照ください。  
今回は、コミットが保護しているブランチに対するものかどうかコミット前にチェックするために、pre-commit のフックを使用します。  
実行したスクリプトが0以外のステータスで終了すると、コミットする前にコマンドが終了します。

コミットしようとしているブランチを確認し、保護対象かどうかによって終了コードを分岐するスクリプトを作成すればよさそうですね。

早速設定していきましょう。

設定

まず、適当なところにフック時に実行するためのスクリプトを作成します。  
(pre-commitのフックを使用するため、ファイル名は pre-commit にします)  
今回は、ユーザーフォルダ以下に git\hooks というフォルダを作成し、その中に pre-commit を作成します。

> mkdir $ENV:USERPROFILE\git\hooks
        > vim $ENV:USERPROFILE\git\hooks\pre-commit

pre-commit の内容は以下のようにします。

#!/bin/bash
        #!C:/Program\ Files/Git/bin/bash.exe
        PROTECT_BRANCHES=(main master develop)
        CURRENT_BRANCH_NAME=`git symbolic-ref HEAD | sed -e 's%^refs/heads/%%'`
        for PROTECT_BRANCH in ${PROTECT_BRANCHES[@]}
        do
            if [[ $CURRENT_BRANCH_NAME == ${PROTECT_BRANCH} ]]; then
                echo "error: ${PROTECT_BRANCH} にcommitしないで!!!"
                exit 1
            fi
        done

PROTECT_BRANCHES に保護したいブランチを空白区切りで列挙します。  
(今回は、main, master, develop を設定しました)

細かいスクリプトの内容については割愛しますが、

  1. コミットしようとしている現在のブランチ名を取得
  2. 保護したいブランチ名と現在のブランチ名を比較
  3. 一致したら、エラーとする

という処理をしています。

最後に、作成したスクリプトをフックするように設定します。  
以下のコマンドで設定できます。

# 現在のリポジトリにのみ設定する場合
        > git config --local core.hookspath $ENV:USERPROFILE\git\hooks
        # すべてのリポジトリに設定する場合
        > git config --global core.hookspath $ENV:USERPROFILE\git\hooks

動作確認

適当なリポジトリを開いて、保護対象にしたブランチにスイッチし、適当なファイルを作成してコミットしてみます。

> git branch
        * main
        > git add test.txt
        > git commit -m "test commit"
        error: main にcommitしないで!!!
        > git status
        On branch main
        Changes to be committed:
          (use "git restore --staged ..." to unstage)
                new file:   test.txt

はい。  
保護しているブランチでコミットしようとしたときに、error: main にcommitしないで!!! と表示され、コミットできなくなりました。

また、Git Hooksを使用した設定を行うと、VSCodeの設定によるダイアログにあった そのままコミット を選択しても、以下のようにエラーになるようになります。

今回は、Gitのローカルブランチを保護する設定・スクリプトを追加し、作業ミスを事前に防ぐ方法について紹介しました。  
ここまでご覧いただき、ありがとうございました。

参考文献