📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
ページの作成:「== 概要 == mergeとrebaseは共に、異なるブランチの変更を統合するためのコマンドであるが、統合の方針に違いがある。<br> ここで…」 |
|||
| (同じ利用者による、間の4版が非表示) | |||
| 1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
Gitにおいて、mergeコマンドとrebaseコマンドは、異なるブランチの変更を統合するためのコマンドである。<br> | |||
しかし、統合の方針に違いがある。<br> | |||
<br> | |||
ここでは、masterブランチの過去のコミットから派生した機能開発用のtopicブランチの変更を、masterブランチに取り込むという状況で、2つの方法を比較する。<br> | |||
<br><br> | |||
== merge / rebaseの主な違い == | |||
==== 履歴の形状 ==== | |||
* merge | |||
*: ブランチの履歴がそのまま保持されて、マージコミットによって結合点が作られる。(木構造) | |||
* rebase | |||
*: コミットを付け替えることにより、直線的な履歴になる。 | |||
<br> | |||
==== コミットの同一性 ==== | |||
* merge | |||
*: 元のコミットのSHA-1ハッシュが保持される。 | |||
* rebase | |||
*: 新しいコミットが作られるため、SHA-1ハッシュが変更される。 | |||
<br> | |||
==== コンフリクト解決 ==== | |||
* merge | |||
*: マージ時に1回だけコンフリクトを解決する。 | |||
* rebase | |||
*: 付け替える各コミットにおいて、潜在的にコンフリクトが発生する可能性がある。 | |||
<br><br> | |||
== mergeを使用すべき場合 == | |||
* フィーチャーブランチの履歴を残す場合 | |||
* 既にリモートで共有されているブランチの場合 | |||
* コンフリクト解決を1度で済ませる場合 | |||
* チーム開発で作業の証跡を残す場合 | |||
<br><br> | |||
== rebaseを使用すべき場合 == | |||
* 直線的な履歴が必要な場合 | |||
* ローカルでのみ使用しているブランチの場合 | |||
* 細かい作業履歴を整理する場合 | |||
* master / mainブランチの最新変更を取り込む場合 | |||
<br><br> | |||
== Publicブランチでのrebase禁止 == | |||
既にプッシュされたブランチをrebaseする場合、他の開発者の環境で問題が発生する。<br> | |||
<br> | |||
# 絶対に避けるべき操作 | |||
git checkout main | |||
git rebase feature | |||
<br><br> | |||
== rebase後のプッシュ == | |||
<code>--force-with-lease</code>オプションとは、他の開発者が同じブランチを更新していないことを確認できる安全なオプションである。<br> | |||
<br> | |||
# rebase後に強制プッシュが必要 | |||
git push --force-with-lease origin feature | |||
<br><br> | |||
== マージ戦略の一貫性 == | |||
チーム内で統一されたマージ戦略を実施することが重要となる。<br> | |||
<br> | |||
# マージコミットを必ず作成する場合 | |||
git merge --no-ff feature | |||
# rebaseした後にマージする場合 | |||
git checkout feature | |||
git rebase main | |||
git checkout main | |||
git merge --ff-only feature | |||
<br><br> | |||
== 推奨される方法 == | |||
==== 作業開始時の基点更新 ==== | |||
git checkout main | |||
git pull | |||
git checkout -b feature | |||
# 作業開始 | |||
<br> | |||
==== 定期的な同期 (rebaseの場合) ==== | |||
git checkout feature | |||
git rebase main | |||
# コンフリクト解決 | |||
git push --force-with-lease origin feature | |||
<br> | |||
==== マージ前の履歴整理 ==== | |||
# コミットを整理 | |||
git rebase -i HEAD~3 | |||
# スカッシュマージ | |||
git merge --squash feature | |||
<br><br> | <br><br> | ||
== mergeを使用して統合する == | == mergeを使用して統合する == | ||
下図のように、topicブランチで開発する間において、masterブランチのコミットが先に進んでいるとする。<br> | |||
この時、materブランチにtopicブランチをmergeすると、コンフリクト (競合、衝突) が発生する。<br> | |||
[[ファイル:Git Merge Rebase 1.png|フレームなし|中央]] | |||
<br> | <br> | ||
しかし、masterブランチ上でコンフリクトを解決するのは好ましくない。<br> | |||
これは、解決の結果が正常に動作するかどうか実証する必要があるからである。<br> | |||
<br> | <br> | ||
そこで、まず、コンフリクトの解決の実証をローカルのtopicブランチで行うため、masterブランチ側の変更をtopicブランチにmergeする。<br> | |||
topicブランチに取り込むことにより、コンフリクトを解決する。<br> | |||
<br> | <br> | ||
次に、masterブランチをチェックアウトして、topicブランチの変更をmasterブランチにmergeする。<br> | |||
この時、--no-ffオプションを付加しない限りは、fast forward mergeとなる。<br> | |||
<br> | |||
これにより、topicブランチのコミットと完全に同一のコミット (sha-1が同じ) が、masterブランチのコミットとして記録される。<br> | |||
<br> | |||
チーム開発の場合、masterブランチへの最終マージにおいては、masterブランチの管理者に対してプルリクエストを送信して実施してもらう場合もある。<br> | |||
<br><br> | <br><br> | ||
| 23行目: | 113行目: | ||
rebaseは、再度(re)起点を定める(base)という意味合いがある。<br> | rebaseは、再度(re)起点を定める(base)という意味合いがある。<br> | ||
<br> | <br> | ||
下図に、最初の状態 (rebase前) を示す。<br> | |||
mainブランチとtopicブランチが分岐しており、両ブランチで並行開発が行われているものとする。<br> | |||
[[ファイル:Git Merge Rebase 2.png|フレームなし|中央]] | |||
<center>図. rebase前の状態</center><br> | |||
<br> | |||
まず、統合前の状態では、masterブランチとtopicブランチの間にコンフリクトが発生する。<br> | |||
それを避けるため、topicブランチの起点をmasterブランチの最新のコミットで置き換える。<br> | |||
<br> | |||
コンフリクトがあれば個別に解決する必要がある。<br> | |||
<u>rebaseでは、topicブランチのコミットを1つずつ順番にmasterブランチの最新の先端に付け加える処理が発生する。</u><br> | |||
<br> | <br> | ||
例えば、topicブランチ上において、masterブランチとコンフリクトが発生するファイルを何度も繰り返し変更している場合は、その都度、競合が発生する。<br> | |||
そのため、コミットの多いtopicブランチをrebaseする場合は作業が煩雑になる。<br> | |||
事前に、topicブランチの履歴を<code>rebase -i</code>コマンド等を実行して簡略化すると緩和できる。<br> | |||
<br> | |||
# topicブランチの起点が、mainブランチの最新コミットに移動する。 | |||
# topicブランチのコミットが順次適用されて、新しいコミット (3'、6'、8') として再作成される。<br>この過程でコンフリクトが発生する可能性があり、その都度解決が必要となる。 | |||
[[ファイル:Git Merge Rebase 3.png|フレームなし|中央]] | |||
<center>図. rebase後の状態 (topicブランチの起点が移動)</center><br> | |||
<br> | <br> | ||
ここでは、rebaseした後のコンフリクトの解決と実証が正常に終了したものとする。<br> | ここでは、rebaseした後のコンフリクトの解決と実証が正常に終了したものとする。<br> | ||
<br> | <br> | ||
次に、この変更をmasterブランチに取り込む。<br> | 次に、この変更をmasterブランチに取り込む。<br> | ||
masterブランチにチェックアウトして、<code>git merge --no-ff <ブランチ名 例: topic></code>コマンドを使用する。<br> | |||
<u>必ず、<code>--no-ff</code>オプションを付加すること。</u><br> | |||
<br> | |||
この時、topicブランチのコミットログがmasterブランチ側に残らないように配慮する必要がある。<br> | |||
<code>--no-ff</code>オプションにより、fast forwardを行わない独立したコミットが作成される。<br> | |||
<br> | |||
masterブランチのコミットログを見ると、基本的には直列の履歴かつ変更コミットが機能ごと (topicブランチごと) に独立して見える。<br> | |||
<br> | |||
# --no-ffオプションにより、マージコミットが明示的に作成される。 | |||
# topicブランチの変更が1つのまとまりとしてmainブランチに統合される。 | |||
# mainブランチの履歴が直線的に保たれる。 | |||
[[ファイル:Git Merge Rebase 4.png|フレームなし|中央]] | |||
<center>図. 最終的なmerge (--no-ff) 後の状態</center><br> | |||
<br><br> | |||
== rebase時のコンフリクトが発生する条件 == | |||
下図において、コンフリクトが発生する条件は、<br> | |||
topicブランチのコミット (3', 6', 8') が、mainブランチの新しいコミット (4, 5, 7) と「同じファイルの同じ部分」を変更している場合のみである。<br> | |||
<br> | |||
[[ファイル:Git Merge Rebase 5.png|フレームなし|中央]] | |||
<br> | |||
# コミット3'のrebase時 | |||
#* masterブランチの4では、別ファイル(file_b.txt)の変更なのでコンフリクト無し。 | |||
#* masterブランチの5において、同じファイル (a.txt) を変更しているためコンフリクトが発生。 | |||
#: <br> | |||
# コミット6'のrebase時 | |||
#* これもa.txtファイルの変更であるため、masterブランチの変更とコンフリクト発生の可能性がある。 | |||
#: <br> | |||
# コミット8'のrebase時 | |||
#* masterブランチの7は、別ファイル (c.txt) の変更であるためコンフリクト無し。 | |||
#* ただし、masterブランチの4で変更されたb.txtファイルとコンフリクト発生の可能性がある。 | |||
<br> | |||
したがって、<br> | |||
* 異なるファイルの変更 | |||
*: コンフリクトは発生しない。 | |||
* 同じファイルでも異なる箇所の変更 | |||
*: コンフリクトは発生しない。 | |||
* 同じファイルの同じ箇所を変更 | |||
*: コンフリクトが発生する。 | |||
<br> | <br> | ||
このため、rebaseの作業量は以下に示す要因によって大きく変わる。<br> | |||
* 変更ファイルの重複度 | |||
* 変更箇所の重複度 | |||
* topicブランチのコミット数 | |||
<br><br> | <br><br> | ||