git push で自動デプロイ時に git pull は使わない方がいいかもしれない

heroku みたいな git push で自動デプロイする方法として、よく post-recceive フックで git pull する方法が載っているけど、
自分の場合、それがうまくいかなかったことがあったのでメモ。



よくある post-receive フックの内容は以下の通り。

#!/bin/sh

(
cd /path/to/repository
git --git-dir=.git pull origin master
)


普通の使い方をしている限り、この方法で問題なく動作する。
が、うっかり git push -f してしまうといろいろとややこしいことになる。*1


というのも、git pull は結局 git fetch + git merge でしかないので、(本当はなかったことにしたい) 以前の HEAD と新たな HEAD をいい感じにマージしてしまうからこんなことになる。


デプロイ先ではコミットなんてしないんだからマージなんかせずに素直に HEAD を移動するだけでいいじゃない、というわけで代わりに git reset を使うようにしてみたのが以下の通り。

#!/bin/sh

(
cd /path/to/repository
GIT_DIR=.git
git fetch
git reset --hard origin/master
git clean -fdx
)


順に解説していく。

GIT_DIR=.git

複数回 git のコマンドを実行するのに毎回オプションを指定するのも面倒なので、環境変数で指定するようにした。

git fetch
git reset --hard origin/master

git pull が git fetch + git merge なら、こっちは git fetch + git reset だ!というわけでやってみました。

git fetch のオプションに --prune や --no-tags を指定したり、refspec (+refs/heads/master:refs/remote/origin/master みたいなの) を指定したりすると余計なものまで fetch しなくなるので高速化が図れるかも。*2

git reset に --hard を指定しているのはワークツリーごと HEAD を変更しないと意味がないから。
詳しくは 前回記事 参照。

git clean -fdx

git reset --hard をしたときにバージョン管理対象外のファイルが出来てしまうことがあるので、それを削除するために入れている。*3



というわけで、git pull は使い所を間違えると怖いよね、というお話。
そもそもリモートに送ったコミットを書き換えるなって?
自分ひとりで使ってる非公開リポジトリだからいいんですよ…。

*1:取り消したつもりの内容が残ってしまったり、コンフリクトしてにっちもさっちもいかなくなったり

*2:前者はちょっと違うけど

*3:間違ったファイルをコミットして、それを削除してコミットし直すとこういうことが起きる