あるある
自分で作成したブランチで作業している間に開発ブランチの変更が先に進み、自分のブランチは古いままというとき、何気にgit rebase main
とか実行したくなりますが、ちょっと待ちましょう。
本流のブランチの変更を自分の作業ブランチに取り込むときには、rebaseとmergeの2つの方法があり、使い分けるのが望ましいです。
rebaseとmerge、そもそもどう違うの?
よく見かけるのと同じ図です。
originはgitサーバー上にあるブランチ、localは手元で作業するブランチとします。
rebase
rebaseは図のように、ブランチの根っこをすげ替える動かし方をします。
コミットログが図の上の状態の時、localブランチをチェックアウトした状態でgit rebase origin
を実行すると、コミットログは図の下のようになります。
もともとoriginの左から2番目のコミットから枝分かれを始めたところを、rebaseを行うことによってoriginの最新のコミットから枝分かれしたように書き換えられます。
rebaseの注意点
rebaseはコミットを移動させるのではなく、前のコミットを消して新たに同じ内容のコミットをつくる
この図では、あたかもlocalブランチのコミットをoriginブランチの右端にそっくりそのまま移動したように見えますが、実際は違います。
これはrebaseの内部操作に由来します。rebaseコマンドを叩くと、rebaseする前のコミット内容をコピーし、新たにコミットを作成 しています。同じ内容だけど、別のコミットになってしまうのです。
pushしたあとrebaseし、さらにpushするときは-f
オプションをつけて実行するのはこれが理由で、同じ内容でも前のコミットが別物になっているため、無理矢理上書きしてあげないとpushできません。
上書きするので、それまでに行われていた変更が失われる可能性があったり、他の人がpullしたときにmergeする必要が出てきたりと、push後にrebaseするのはデメリットしかありません。
また、rebaseはrebaseしたことを記録することができないのも注意です。
merge
それに対してmergeは、rebaseのように根っこをすげ替えることはせず、新たにコミットを作成してmainの変更を取り込みます(コミットマージ)。
図でいうと、localブランチをcheckoutした状態でgit merge origin
を実行します。
mergeではrebaseのように破壊的な変更が行われることはなく、mergeしたことも記録することができるため、安全にmainの変更を取り込むことができます。
ただ、rebaseとは違って変更を取り込んだことが記録されるため、頻繁に行うとマージの履歴がたくさんできてしまうのが気になるかもしれません。
結局どう使い分ければいいの?
Pull Requestベースのコードレビューをやっている前提で書きますが、
pushするまではrebase、pushしたあとはmerge
です。
pushする前
push前であれば、rebase、merge両方できます。両方できるとなると、履歴がスッキリするrebaseするほうがいいでしょう。自分の作業ブランチでgit rebase <本流のブランチ名(develop/master/mainなど)>
を叩けば変更を取り込めます。
また、rebaseはコミットをまとめることもできるため、レビュー前にコミットをまとめてスッキリさせておくとレビュワーの負担が減ります。(僕はこれをやるのを忘れてpushしてしまうことが多いのですが。いけませんね。)
push後
すでに述べた通り、push後はmerge一択です。rebaseしてはいけません。
最後に
pushするまではrebase、pushしたあとはmerge
です!
参考