開発ブログ

WWWクリエイターズが送る、Git、CSS、HTML、コマンドライン、Macの便利機能など、開発に関する役立ち情報発信します。気まぐれに更新。

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

最終更新:2017-07-03 by Joe

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

rebase とmergeの違い

両方とも、異なるブランチの変更を統合するためのコマンドですが、統合の方針に違いがあります。masterの過去のコミットから派生した機能開発用のブランチ「topic-branch」の変更を、「master」に取り込む、という状況で、2つの方法を比較してみます。

「merge」を用いて統合する

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

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

競合解消後のテストをローカルのtopic-branchで行うため、いったんmaster -> topic-branchにマージします。

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

では、この変更をmasterに戻します。

まずはmasterに移動します。

そして、さっきテストし終わった、topic-branchをマージします。

こうして、masterに、変更を取り込む事ができました。

変更の起点を移動する「rebase」

一方のrebaseです。英語の語感が少しある方であれば、単語からイメージしやすいと思いますが、再度(re)起点を定める(base)という意味合いがあります。

まずは、統合前の状態です。

masterと、topic-branchの間にコンフリクトが発生することが想像されるので、それを避けるため、topic-branchの「起点」を、masterの最新コミットで置き換えます。

このコマンドに置いても、競合を解決する必要があります。topic-branchのコミットを一つずつmasterの最新に付け替えていきますので、例えば、もしtopic-branch上で同じ箇所を何度も繰り返し変更している場合は、その都度、競合が発生する可能性があります。その為、歴史の長いブランチをリベースするときは、作業が大変になる事もあります。(事前にtopic-branchの履歴をrebase -i などを使って簡略化しておくと、緩和できます)

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

まずはmasterに戻って・・

使うべきコマンドは「git merge --no-ff topic-branch」です。fastforward(早送り)により、履歴が統合されるのを避け、独立したマージコミットが作られます。

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

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

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

使い分け

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

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

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

参考リンク