よく分かる!git rebaseとmergeの違いと使い分け

最終更新:2018-02-12 by Joe

git rebase とgit mergeの違いと、使い分けについてです。

git rebase とgit mergeの違い

両方とも、異なるブランチの変更を統合するためのコマンドですが、統合の方針に違いがあります。

今回、masterブランチの過去のコミットから派生した機能開発用のブランチ「topic-branch」の変更を、「master」に取り込む、という状況で、2つの方法を比較してみます。

「git merge」を用いて統合する

さて、ブランチtopic-branchで機能を開発する間に、他のチームメンバーによってmasterがいつくか先にすすんでしまいました。このままmaterにtopicをマージすると、コンフリクト(競合、衝突)が発生してしまう事がわかっています。

git merge の開始直前

masterブランチ上でコンフリクトを解決するのは好ましくありません。解決の結果がうまくいったかどうか、テストする必要があるからです。テストされていない機能をmasterに入れてしまっては、チームの他の人に迷惑がかかってしまう可能性があります。

競合解消のテストをローカルのtopic-branchで行うため、いったんmaster側の変更をtopic-branchに取り込んでしまいます。

master -> topic-branch とマージします。

git merge の実行後

さて、マージ後のテストを行った結果は、どうやら大丈夫そうです。競合の解決はうまくいきました。

では、次は、この変更をmasterに戻します。まずは作業ブランチを移動します。masterをチェックアウトします。

先にトピックブランチにマージしてテスト。

そして、さっきテストし終わった、topic-branchをマージします。この場合--no-ffオプションを付けない限りは、fast forward マージになります。先程テストしたコミットと完全に同一のコミット(sha-1が同じ)が、masterブランチの新しい歴史として記録されました。

最後に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 などを使って簡略化しておくと、緩和できます)

git rebase の実行直後

さて、リベースがうまく行きました。あとは、リベース後のコミットをテストして、大丈夫そうであれば、この変更をmasterに取り込みます。

まずはmasterに戻って・・

masterをチェックアウト

使うべきコマンドは「git merge --no-ff topic-branch」です。topic-branch のコミットログが、master側に残らない用に配慮します。「--no-ff」オプションにより、fastforward(早送り)を行わない、独立したマージコミットが作られます。

トピックブランチをマージ。早送りしない。

こうして、masterブランチは、きれいな歴史になりました。mergeするよりも、こちらの方が幾分シンプルに見えます。masterの歴史だけ見ると、基本的には直列の開発履歴で、かつ、変更コミットが機能ごと(トピックブランチごと)に独立して見えるからです。

もし、「-no-ff」オプションを使わないと、このような歴史になってしまいます。

rebase の後、早送りマージしてしまった場合。

masterの歴史に、topic-branchの作業途中の変更内容が含まれてしまいました。masterの歴史をキレイに保つ意味では、これは好ましくありません。

rebase とmerge 使い分け、まとめ

上記のように、masterを進める観点で見ても、作成される歴史に違いがでます。基本的には、どのような歴史を作りたいか?によって使い分けますが、一般的に「masterなどの統合ブランチの歴史をシンプルに保つ」などの目的で、以下のような方針で運営するケースもよく見られます。

  • master(などの統合用ブランチに)に、トピックブランチをマージする前に(もしくはpull requestする前に)必ず最新のmasterでリベースしてからマージする(もしくはpull requestする)

こちらは、チームの運営方針にあわせましょう。

参考リンク