リベースとマージのどちらを使う?
今回はリベースとマージのどちらを使うのか、この2つの使い分けについて解説します。
変更内容を取り込む際にマージを使うかリベースを使うかは、Gitの運用方針次第になります。
それぞれの特徴を見ていきます。
マージの特徴
○メリット
- コンフリクトの解決が比較的簡単
変更内容を取り込む際にコンフリクトしてしまった場合、マージの方がリベースよりもコンフリクトの解決が簡単です。
○デメリット
- マージコミットがたくさんあると履歴が複雑化する
例えばGitHubリポジトリの内容を自分のローカルに取り込みたくて「git pull」したとします。
その「git pull」するときに、「git pull」の挙動としてマージをしてしまうとそれだけでマージコミットがどんどんできていって、特に何かを変更したわけではないにも関わらず、いっぱいコミットができてしまいます。
そうすると履歴が複雑化してしまうというデメリットがあります。
リベースの特徴
○メリット
- 履歴をきれいに保つことができる
リベースを使うと履歴を一直線にすることができるため、履歴を綺麗に保つことができます。
○デメリット
- コンフリクトの解決が若干面倒(コミットそれぞれに解消が必要)
このようなそれぞれの特徴を踏まえて、マージとリベース、どのように使い分ければ良いか考えます。
まずマージに関しては作業の履歴を残したい場合、この場合はマージを使いましょう。
逆に作業の履歴を残さなくて良い場合、作業の履歴を残すよりも履歴を綺麗にしたいという場合はリベースを使うようにしていきましょう。
それぞれの特徴のところで、コンフリクトの解決に関してはマージの方がリベースよりも比較的簡単だということを述べました。
もう少しその点についてマージとリベースでコンフリクトはどのように違うのかと言うことについて詳しくみていきます。
マージの場合
今状況として「コミット1」があって、「index.html」と言うファイルの1行目を変更して、「コミット2」というコミットをしたとします。
この状況から別のブランチで作業して、同じ場所である「index.html」の1行目を変更してコミットしました。
それがコミット3です。
そのブランチでさらに「index.html」の1行目を変更しました。
そして「コミット4」になりました。
この状況で「コミット2」の方に「コミット4」の内容を取り込もうと、マージしたとします。
そうすると、どちらの変更でも「index.html」の1行目を変更しているため、コンフリクトが起きます。
これは良くあるマージのコンフリクトです。
ここで気をつけて欲しいのは、あくまでマージの場合、コンフリクトが1度しか発生しないと言うことです。
ではこれとリベースを見比べてみましょう。
リベースの場合
状況として「コミット1」があって、「index.html」の1行目を変更してコミットして、「コミット2」ができました。
ここで、ブランチを切り替えて同じように「index.html」の1行目を変更して「コミット3」、また同じ場所を変更して「コミット4」というコミットができました。
今自分が「コミット4」にいる状態でリベースコマンドを使って「コミット4」の親コミットを「コミット3」から「コミット2」に変更したとします。
リベースコマンドをすると何が起こるかと言うと、まず「コミット3」でコンフリクトします。
その後「コンフリクト4」でもコンフリクトして、「git rebase」コマンドの場合だと、コンフリクトが各コミット毎に発生します。
リベースの場合、リーベスに含まれるコミットが複数あった場合、それぞれのコミット毎にリベースを実行して、全部のコミットのリベースが完了した時点でリベースが完了するという挙動をします。
そのため、リベースコマンドを使うと、まず「コミット3」をリベースして、「コミット4」を次にリベースします。
結果として「コミット3」でも「コミット4」でも「index.html」の1行目を変更しているので、それぞれコンフリクトが発生して、2回コンフリクトの解消をしないといけないとなります。
そのため、マージとリベースではコンフリクトが起きた場合、マージの方はコンフリクトが1度しか発生しないので、比較的コンフリクトの解決が簡単なのですが、リベースの場合はコミット毎にコンフリクトが発生するので、コンフリクトの解消が面倒だという違いがあります。
今まで見てきた特徴を踏まえて、私の場合は次のような方針でリベースとマージを使い分けています。
- プッシュしていないローカルの変更にはリベースを使い、プッシュした後はマージを使う
プッシュしていないローカルの変更に他のブランチの変更内容を取り込みたい、そういう時は履歴を綺麗にしたいので、リベースを使うことが多いです。
逆にプッシュした後は必ずマージを使うようにしています。
プッシュした後にリベースを使うと前に見たように履歴が壊れてしまいます。
ですので、プッシュした後に関しては絶対にマージを使うようにしています。
後は、変更内容を取り込んだという履歴をちゃんと残したい時、もしくはそういったチームの運用方針の場合もマージを使うようにしています。
- コンフリクトしそうならマージを使う
コンフリクトしそうかどうかはどうやって分かるかというと、2つ方法があって、まず1つ目にチームで開発している場合は他の人が今どのような変更をしているかというのをだいたい把握しておいて、それでコンフリクトしそうかどうかを判断します。
他の人が何を変更しているのかをよく把握できていないと言ったケースも多いので、そのような場合は2つ目の方法、1回ローカルの変更をGitHubにプッシュして、プルリクエストを先に作成してしまいます。
するとGitHubは親切なので、コンフリクトしている場合GitHub上でコンフリクトしているよというアラートを表示してくれます。
それを見てコンフリクトしているというアラートが出たら、その場合はマージを使って変更内容を取り込むようにしています。 まだリベースになれていない場合は、このやり方をするのをオススメします。
その上でリベースになれてきたら、自分なりの履歴の管理の仕方の方針を決めて、その方針に従って使い分けていくのがよいでしょう。
参考図書
独学で挫折しそうになったら、オンラインプログラミングスクール