こんにちは!
新卒1年目初めての案件に配属され、新卒研修と比べてより自分で考えて作業をする時間が増えました。しかし、GitやGitHubの操作にはまだ慣れない日々を送っています。
今回は、私自身の「安全のための記録」としてpre-pushフックの設定方法について書いていきます。
ヒヤリとした体験
ある日、作業ブランチでリモートの変更を取り込むためにgit rebaseを実行し、いつものようにgit pushを実行したところ、プッシュ先が意図せずリモートのmasterブランチになっていたことがありました。
幸いすぐに元の状態に戻せたこと、そしてリポジトリの運用開始前だったため大事には至りませんでしたが、この出来事は私にとって大きな危機感となりました。
この経験から、「自分の意識や注意だけに頼ることは危険であり、技術的なミスは技術的な仕組みで防ぐべきだ」
という重要な学び、その解決策として、Gitのツールであるpre-pushフックについて知りました。
原因究明:なぜプッシュが許されたのか
masterブランチにプッシュしてしまった根本原因は、ローカルブランチのトラッキング情報(上流ブランチ)が間違って設定されていたことです。
Gitでは、git pushやgit pullを実行する際、どのローカルブランチがリモートのどのブランチと対応しているか(トラッキング)を参照します。
私は何かの操作の際に、作業ブランチの上流ブランチを誤ってリモートのorigin/masterに設定してしまったようです。
この設定ミスが、普段通りのgit push操作で「意図せずmasterにプッシュする」という破壊的な行動を許してしまう土台を作ってしまいました。
対処とリスク:なぜ予防が不可欠なのか
意図せずプッシュされたコミットを元に戻すため、私はすぐにgit push --forceを使ったコマンドで正しい状態のコミットを上書きしました。
しかし、この操作はリモートの履歴を問答無用で上書きするコマンドです。
もし運用中のリポジトリであれば、同じ時間にプッシュしていた他のメンバーの作業履歴を巻き込んで消してしまい、大きなトラブルになっていた可能性があります。
この経験から、リカバリーの良し悪しに関わらず、この危険な状況自体を絶対に作ってはならないという強い教訓を得ました。
自己防衛策としてのpre-pushフック導入
上記の反省から、「自分のミスをリモートに反映させないガードレール」としてpre-pushフックを導入しました。
pre-pushフックとは?
pre-pushフックは、git pushコマンドが実行され、リモートにデータが転送される直前に動くスクリプトです。
このスクリプトが「問題あり」と判断すると、プッシュ処理は強制的に中断され、データはリモートへ送られません。
これは、ヒューマンエラーによる破壊的な操作を未然に防ぐ「最後の砦」として機能します。
実践!ガードレールとなるスクリプトと機能解説
私が設定したpre-pushスクリプトは、最も危険な操作である
「masterブランチへのプッシュ」のみをチェックし、他の操作の柔軟性を確保します。
【ファイルの作成と準備】
まず、通常非表示の.git/hooks/フォルダにファイルを作成する必要があります。
VS Codeでエクスプローラーに.gitフォルダが表示されない場合は、設定を開き、files.excludeの項目から.gitに関する設定を一時的に削除すると表示されます。
.git/hooks内にpre-pushというファイルを作成し、以下のスクリプトを貼り付けました。
#!/bin/bash
# 禁止したいブランチ名を指定
# 複数ブランチを指定することもできます
# 例)"master develop" ← masterブランチとdevelopブランチを指定
TARGET_BRANCHES="master"
while read local_ref local_sha1 remote_ref remote_sha1
do
# プッシュ先のブランチ名を取得
BRANCH_NAME="${remote_ref##refs/heads/}"
for branch in $TARGET_BRANCHES
do
if [[ "$BRANCH_NAME" = "$branch" ]]; then
echo ""
echo "🚨 ERROR: 【 $branch 】ブランチへの直接プッシュは禁止されています!"
echo "🚨 プッシュを中止しました。Pull Request (PR) 経由でのマージを推奨します。"
echo ""
exit 1 # プッシュを拒否
fi
done
done
exit 0 # チェックOK。プッシュ続行
pre-pushフックスクリプトの実行時、Gitはプッシュに関する情報を標準入力を通じてスクリプトに渡します。
スクリプト内の while read ... ループは、その情報を受け取る部分です。
特に重要なのは、プッシュ先のリモートブランチ名を示す remote_ref です。
この値を基に、スクリプトはプッシュ先が危険なブランチ(今回のスクリプトではmaster)でないかを判断しています。
| 変数名 | 意味 |
|---|---|
| local_ref | プッシュ元のローカルブランチ名 |
| local_sha1 | プッシュ元(ローカル)ブランチの最新コミットハッシュ |
| remote_ref | プッシュ先のリモートブランチ名 |
| remote_sha1 | プッシュ先(リモート)ブランチの現在の最新コミットハッシュ |
【ブロック範囲の拡張】キーワード包含チェックの導入
今回は指定したブランチ名と完全に一致したものが判断されていますが、if [[ "$BRANCH_NAME" = "$branch" ]]; thenを下記のように変更すると単語を含むブランチをまとめてチェックすることも可能です。
if [[ "$BRANCH_NAME" == *"$branch"* ]]; then
設定手順
-
ファイル名の確認/作成:
.git/hooks/フォルダ内にpre-pushファイルを作成し、
上記のスクリプトを貼り付けて保存します
(Gitフックとして機能させるには、ファイル名はpre-pushである必要があります) -
実行権限の付与:
以下のコマンドで、スクリプトが実行できるように権限を与えます
(リポジトリのルートディレクトリで実行)
chmod +x .git/hooks/pre-push
実際に動作するかテストしてみた
実際に練習用のリポジトリでpre-pushフックが動作するかをテストしました。
作業ブランチ(test/pre-push-fail)を作成し、上流ブランチがmasterになっている状態でプッシュをしてみます。
git branch -vvで上流ブランチの設定を確認するとmasterになっています。
この状態でプッシュをしてみます。
プッシュをしようとするとエラーが出てプッシュができないようになっています。
pre-pushフックが正常に動作しました!
【注意】--no-verifyオプションについて
git pushコマンドに--no-verifyオプションを付けて実行した場合、
pre-pushフックはスキップされ、意図的に動作しません。
このオプションを使うと誤ってmasterにプッシュされる危険性が非常に高くなるため、使用しないよう十分に注意してください。
自己防衛の重要性
今回の経験を通じて、私は「自分で気を付けるのには限界がある」という学びを得ました。
pre-pushフックは、誰も見ていないローカル環境でのミスがリモートに反映されるのを防ぎ、自分自身の安全を守るための強力なガードレールだということを知りました。
そしてGitの機能、ツールを知るきっかけにもなりました。
引き続き、GitやGitHubの操作をより安全で快適にするためのツールや仕組みを積極的に調べて、日々の開発に活かしていこうと思います!

