カレーのライスをtech忘れ

odmishienのtechメモ

git push --force はなぜダメなの

git push --force って、なんか絶対やってはいけない空気を感じますよね。でも結局なぜダメなのか。あまり理解してない気がしたので、まとめてみます。

そもそもどういう場面で使うか

チーム開発をやっている時に、remoteとlocalで整合性が取れなくなることがある。例えば以下のような状況では git push できない。

<remote>           <local>
     A                 A
     |                 |
     B                 B
     |                 |
     C                 D ← 存在するはずの C というコミットがないのでpushできない!!

この場合いくつか解決方法はあるが、私だったら以下のようにする。

git checkout local
git stash save "stash D" // Dという変更を一旦退避
git merge remote // remoteをマージして Cコミットを取り込む
git stash apply // これで A-B-C-D という順番になる
git push // ✌️

しかし様々な事情から stashした変更をapplyするのがめちゃくちゃ面倒だったりして、「あ〜もう上書きしていいスカ(笑)」となる瞬間は誰しもある。ここで登場するのが git push --force である。

force オプションの何が悪か

1人で開発している際には大きな問題になることはなさそう。問題となるのはチーム開発の時である。先ほどの例を取ると、 git push --force はかなり破壊的なコマンドであることが分かる。

git checkout local // local は A-B-D, remoteは A-B-C
git push --force // remote は localに上書きされてしまい、 A-B-Dとなる(Cというコミットが消える)

チームのメンバーのコミットを吹き飛ばす、みたいなことが起こり得るので結構危険である。

じゃあ脳死で force-with-lease を使えばいいのか

--force-with-lease はこのような破壊的な force オプションを回避するためのオプションである。--force-with-lease はremoteのrefsとlocalのrefsを比較して、他人のコミットなどがあればpushをrejectしてくれる君である。これで安心。

と言いたいところだが、仕組みとしてこの--force-with-lease を付けていても、remoteを上書きしてしまう場合がある。それは push する前に fetchコマンドを実行していた時である。 --force-with-lease は詰まるところ単にremoteのrefsとlocalのrefsを比較しているだけなので、ここが揃ってしまっていたら、すり抜けてしまう。

結局どうしたらいいの

チームメンバーとコミュニケーションを取りながら開発をしましょう。極力共同で作業しているブランチでは --force--force-with-lease も使わないようにして、面倒でもブランチの整合性を取るように心がけましょう。

参考記事

www.atlassian.com

git-scm.com

denpa-shinbun.com