git rebase とgit mergeの違いと、使い分けについてです。
目次
git rebase とgit mergeの違い
両方とも、異なるブランチの変更を統合するためのコマンドですが、統合の方針に違いがあります。
今回、masterブランチの過去のコミットから派生した機能開発用のブランチ「topic-branch」の変更を、「master」に取り込む、という状況で、2つの方法を比較してみます。
「git merge」を用いて統合する
さて、ブランチtopic-branchで機能を開発する間に、他のチームメンバーによってmasterがいつくか先にすすんでしまいました。このままmaterにtopicをマージすると、コンフリクト(競合、衝突)が発生してしまう事がわかっています。
masterブランチ上でコンフリクトを解決するのは好ましくありません。解決の結果がうまくいったかどうか、テストする必要があるからです。テストされていない機能をmasterに入れてしまっては、チームの他の人に迷惑がかかってしまう可能性があります。
競合解消のテストをローカルのtopic-branchで行うため、いったんmaster側の変更をtopic-branchに取り込んでしまいます。
master -> topic-branch とマージします。
さて、マージ後のテストを行った結果は、どうやら大丈夫そうです。競合の解決はうまくいきました。
では、次は、この変更をmasterに戻します。まずは作業ブランチを移動します。masterをチェックアウトします。
そして、さっきテストし終わった、topic-branchをマージします。この場合--no-ffオプションを付けない限りは、fast forward マージになります。先程テストしたコミットと完全に同一のコミット(sha-1が同じ)が、masterブランチの新しい歴史として記録されました。
こうして、masterに、無事にテストされた変更を取り込む事ができました。
今回masterへの最終マージを自分で行いましたが、チーム開発に置いては、masterの管理者に対して、プルリクエストを送るかもしれませんね。
変更の起点を移動する「git rebase」
一方のrebaseです。英語の語感が少しある方であれば、単語からイメージしやすいと思いますが、再度(re)起点を定める(base)という意味合いがあります。
まずは、統合前の状態です。さきほどと同じです。
masterと、topic-branchの間にコンフリクトが発生することがわかっているので、それを避けるため、topic-branchの「起点」を、masterの最新コミットで置き換えます。
このコマンドにおいても、競合があれば個別に解決する必要があります。rebase コマンドでは、topic-branchのコミットを「一つずつ」順番にmasterの最新の先端に付け加えていく処理が発生します。
ですので、例えば、もしtopic-branch上で、masterと競合が発生するファイルを何度も繰り返し変更している場合は、その都度、競合が発生する可能性があります。その為、歴史の長いトピックブランチをリベースするときは、作業が大変になる事もあります。(事前にtopic-branchの履歴をrebase -i などを使って簡略化しておくと、緩和できます)
さて、リベースがうまく行きました。あとは、リベース後のコミットをテストして、大丈夫そうであれば、この変更をmasterに取り込みます。
まずはmasterに戻って・・
使うべきコマンドは「git merge --no-ff topic-branch」です。topic-branch のコミットログが、master側に残らない用に配慮します。「--no-ff」オプションにより、fastforward(早送り)を行わない、独立したマージコミットが作られます。
こうして、masterブランチは、きれいな歴史になりました。mergeするよりも、こちらの方が幾分シンプルに見えます。masterの歴史だけ見ると、基本的には直列の開発履歴で、かつ、変更コミットが機能ごと(トピックブランチごと)に独立して見えるからです。
もし、「-no-ff」オプションを使わないと、このような歴史になってしまいます。
masterの歴史に、topic-branchの作業途中の変更内容が含まれてしまいました。masterの歴史をキレイに保つ意味では、これは好ましくありません。
rebase とmerge 使い分け、まとめ
上記のように、masterを進める観点で見ても、作成される歴史に違いがでます。基本的には、どのような歴史を作りたいか?によって使い分けますが、一般的に「masterなどの統合ブランチの歴史をシンプルに保つ」などの目的で、以下のような方針で運営するケースもよく見られます。
- master(などの統合用ブランチに)に、トピックブランチをマージする前に(もしくはpull requestする前に)必ず最新のmasterでリベースしてからマージする(もしくはpull requestする)
こちらは、チームの運営方針にあわせましょう。