つい先日、GitHubで管理していたテスト用中央ブランチに、チームメンバーが誤ってgit push --force
してしまい、
一部の歴史が消失するという事件が起きました。
ぎゃあああ!なんばしよっとね!うっかりでしたじゃ済まんばい!
とか思っていたらJenkinsの開発者みたいなスゴい人でもやらかしちゃうみたいです。
Jenkinsの開発者、間違えて一ヶ月前のローカルレポジトリをgit push --forceしてしまう
http://cpplover.blogspot.jp/2013/11/jenkinsgit-push-force.html
スゴい人でもやらかすんだから、平民の我々もそのうちやらかすに違いない。
緊急バグ修正などで慌てていたら尚更ですね。(というか自分が一番やりかねない)
というわけで、何とか仕組みの上で防くことができればと思って仕掛けることにしました。
以下のスクリプトを、.git/hooks/pre-push
というファイル名で保存。
push直前に起動し、大事なブランチへgit push --force
しそうであればエラーを返して食い止めてくれます。
#!/bin/sh
# 守りたいbranch名を定義
PROTECTED_BRANCHES=( trunk master )
CURRENT_BRANCH=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
PUSH_COMMAND=$(ps -ocommand= -p $PPID)
IS_DESTRUCTIVE='force|delete|\-f'
WILL_REMOVE_PROTECTED_BRANCH=':'$PROTECTED_BRANCH
for i in "${PROTECTED_BRANCHES[@]}"
do
PROTECTED_BRANCH=$i
# エラーメッセージを定義
MESSAGE="$PROTECTED_BRANCH には $PUSH_COMMAND してはダメです!先にgit fetch && git merge origin $PROTECTED_BRANCH すること。by pre-push hook"
if [[ $PUSH_COMMAND =~ $IS_DESTRUCTIVE ]] && [ $CURRENT_BRANCH = $PROTECTED_BRANCH ]; then
echo $MESSAGE
exit 1
fi
if [[ $PUSH_COMMAND =~ $IS_DESTRUCTIVE ]] && [[ $PUSH_COMMAND =~ $PROTECTED_BRANCH ]]; then
echo $MESSAGE
exit 1
fi
if [[ $PUSH_COMMAND =~ $WILL_REMOVE_PROTECTED_BRANCH ]]; then
echo $MESSAGE
exit 1
fi
done
exit 0
参考
https://gist.github.com/pixelhandler/5718585
自前の開発環境に合わせて少々書き換えました。
本当はサーバサイドフックを利用して、特定ブランチへの破壊的なpushであれば云々、みたいな処理にしたかったのですが、
GitHub上で実現する方法がよくわからず、やむなくpre-pushを利用しました。
クライアントサイドフックなので、中央ブランチを完璧に守るためには、開発者全員の環境に仕掛ける必要があります。
あと、pre-pushはgitのバージョンによっては動かないそうです。
1.8.2以上であれば動作するはずです。(1.8.3環境で確認済み)