Gitの基礎 - rebaseとmergeの使い分け

提供:MochiuWiki : SUSE, EC, PCB
2020年4月26日 (日) 08:41時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == mergeとrebaseは共に、異なるブランチの変更を統合するためのコマンドであるが、統合の方針に違いがある。<br> ここで…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
ナビゲーションに移動 検索に移動

概要

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


mergeを使用して統合する

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

masterブランチ上でコンフリクトを解決するのは好ましくない。なぜなら、解決の結果が正常に動作するかどうか実証する必要があるからである。
そこで、コンフリクトの解決の実証をローカルのtopicブランチで行うため、masterブランチ側の変更をtopicブランチに取り込む。
(ここで、コンフリクトを解決したものとする)

次に、このtopicブランチの変更をmasterブランチに取り込む。(masterブランチをチェックアウトする)
そして、topicブランチをmergeする。(この場合、--no-ffオプションを付加しない限りは、fast forward mergeになる)
これで、topicブランチのコミットと完全に同一のコミット(sha-1が同じ)が、masterブランチのコミットとして記録される。

ここでは、masterブランチへの最終mergeを自分で行ったが、チーム開発に於いては、masterブランチの管理者に対してプルリクエストを送る場合もある。


rebaseを使用して変更の起点を移動する

rebaseは、再度(re)起点を定める(base)という意味合いがある。

まず、統合前の状態では上記と同様に、masterブランチとtopicブランチの間にコンフリクトが発生することが分かっているので、
それを避けるため、topicブランチの起点を、masterブランチの最新のコミットで置き換える。

コンフリクトがあれば個別に解決する必要がある。rebaseでは、topicブランチのコミットを1つずつ順番にmasterブランチの最新の先端に付け加える処理が発生する。
例えば、topicブランチ上で、masterブランチとコンフリクトが発生するファイルを何度も繰り返し変更している場合は、その都度、競合が発生する可能性がある。
そのため、コミットの多いtopicブランチをrebaseする場合は、作業が煩雑になる。(事前に、topicブランチの履歴をrebase -i等を使用して簡略化すると緩和できる)

ここでは、rebaseした後のコンフリクトの解決と実証が正常に終了したものとする。

次に、この変更をmasterブランチに取り込む。
masterブランチをチェックアウトして、git merge --no-ff topic-branchコマンドを使用する。(必ず、--no-ffオプションを付加すること)
この時、topicブランチのコミットログがmasterブランチ側に残らない用に配慮する。(--no-ffオプションにより、fast forwardを行わない独立したコミットが作成される)

masterブランチのコミットログを見ると、基本的には直列の履歴かつ変更コミットが機能ごと(トピックブランチごと)に独立して見える。


備考

masterブランチを進める観点で見ても、作成される履歴に違いが出る。

一般的には、どのような履歴を残すかによって使い分けるが、masterブランチなどの統合ブランチの履歴をシンプルに保つ等の目的で、
以下のような方針で運営するケースもよく見られる。

masterブランチ等の統合用ブランチに、別ブランチをmerge(または、pull request)する前に、必ず最新のmasterブランチでrebaseしてからmerge(または、pull request)する。
これらは、チームの運営方針に合わせる。