Git Rebase の仕組み完全図解

コンフリクトの発生メカニズムとMergeとの違いを5分で理解する

1. 開発現場でよくある課題

あなたは新機能を開発するためにfeatureブランチを作成し、作業を進めています。

その間に、チームメンバーがmainブランチに新しい変更を追加しました。

あなたの作業を統合する前に、mainブランチの最新の変更を取り込む必要があります。

C1 C2 C3 C4 main F1 F2 F3 feature ← チームメンバーの変更 ↑ ここから分岐

あなたがF1を作った後、mainにC3とC4が追加された状態

2. 統合の2つの方法:MergeとRebase

Merge(マージ)
Rebase(リベース)
概念: 2つの流れを合流させる
概念: あなたの変更を最新の土台に移し替える
結果: 新しい「統合コミット」が作られる
結果: あなたのコミットが書き換えられる
履歴: 分岐と合流が記録される
履歴: まっすぐな一本線になる

Merge の結果

Merge 統合コミット

Rebase の結果

C1 C2 C3,C4 F1' F2' F3' まっすぐな履歴(F1,F2,F3は新しいコミットとして再作成)

3. Rebaseの仕組み:4つのステップ

1
共通の祖先を見つける
Gitはfeatureブランチmainブランチが分岐した地点(共通の祖先コミット)を自動的に探します。上の例ではC2が共通の祖先です。
C2 共通の祖先
2
あなたの変更を「差分」として記録
featureブランチ上の各コミット(F1, F2, F3)について、「何が変わったか」という差分情報を抽出します。これをパッチと呼びます。パッチはGitの内部フォルダに一時的に保存されます。
📝 パッチとは?

パッチは「どのファイルの、どの行を、どう変更したか」という情報です。例えば:

- 古い行: const version = "1.0";
+ 新しい行: const version = "2.0";
3
ブランチを最新の位置にリセット
featureブランチのポインタを、mainブランチの最新コミット(C4)の位置に移動します。この時点で、あなたのF1, F2, F3は一時的に消えたように見えます(実際にはパッチとして保存されています)。
C1 C2 C3 C4 ← feature featureブランチがC4を指している(F1,F2,F3は一時保存中)
4
パッチを一つずつ再適用
保存しておいたパッチ(F1, F2, F3の差分)を、順番に一つずつC4の上に適用していきます。それぞれのパッチ適用時に新しいコミット(F1', F2', F3')が作成されます。元のF1, F2, F3とは異なる識別番号を持つ、全く新しいコミットです。
⚠️ 重要なポイント

Rebaseでは各パッチを一つずつ適用するため、コンフリクトが発生する可能性もパッチごとにあります。これがMergeとの大きな違いです。

4. コンフリクトが起きる仕組み

Gitはファイルをマージするときに3-way merge(3点マージ)という仕組みを使います。

これは3つのバージョンを比較して、自動的に統合できるかを判断する方法です。

共通の祖先
function hello() {
  return "Hello";
}
あなたの変更
function hello() {
  return "Hello World";
}
相手の変更
function greet() {
  return "Hello";
}

自動解決できる場合:

• 異なる行を変更している → 両方の変更を統合

• 片方だけが変更している → その変更を採用

手動が必要な場合(コンフリクト):

同じ行を両方が異なる方法で変更

• 同じ行を一方が変更、もう一方が削除

コンフリクトの表示例

<<<<<<< HEAD
function hello() {
  return "Hello World";
}
=======
function greet() {
  return "Hello";
}
>>>>>>> applying: F1

赤色:あなたの変更 / 緑色:mainブランチの変更 / オレンジ:区切り線

5. MergeとRebaseのコンフリクトの違い

Merge のコンフリクト
Rebase のコンフリクト
発生タイミング: 統合時に1回だけ
発生タイミング: 各コミット適用時に複数回の可能性
比較対象: 2つのブランチの「最終状態」を比較
比較対象: 各パッチを順番に適用して比較
解決: すべてのコンフリクトを一度に解決
解決: 1つずつ解決してgit rebase --continue

なぜRebaseで同じコンフリクトが複数回現れるのか?

例えば、F1, F2, F3のコミットが全て同じファイルの同じ行を変更していて、mainブランチでもその行が変更されていた場合:

1️⃣
F1のパッチを適用
コンフリクト発生 → 手動で解決 → git rebase --continue
2️⃣
F2のパッチを適用
また同じ箇所でコンフリクト発生 → 再度解決 → git rebase --continue
3️⃣
F3のパッチを適用
さらにコンフリクト発生 → 解決 → 完了
⚠️ Rebaseの注意点

複数のコミットが同じ箇所を変更している場合、Mergeなら1回のコンフリクト解決で済むところを、Rebaseでは各コミットごとに解決が必要になります。

これは、Rebaseが「コミットを一つずつ順番に再生する」という仕組み上、避けられない特性です。

💡 Mergeの場合

Mergeは2つのブランチの最終的な状態だけを見て統合するので、中間のコミットがどうであれ、コンフリクトは1回の判定で完結します。

6. いつ何を使うべきか

✅ Rebaseが適している場面

個人のブランチで、まだ誰とも共有していない変更

• 履歴をまっすぐにしてレビューしやすくしたい

• 作業中に頻繁にmainの最新を取り込みたい

• コミットを整理(squash)してから統合したい

✅ Mergeが適している場面

複数人が使っているブランチの統合

• いつ統合したかの記録を残したい

• 長期ブランチで、同じ箇所に多数の変更がある

• コンフリクト解決を1回で済ませたい

🚫 絶対に避けるべき

公開済みのブランチ(他の人がベースにしている可能性があるブランチ)に対してRebaseを実行すること

理由:Rebaseはコミットを書き換えるため、他の人の作業に深刻な混乱を招きます。これは「Rebaseの黄金律」と呼ばれています。

判断フローチャート

📋 このブランチは他の人と共有している?

YES: Mergeを使う

NO: 次へ

📋 まっすぐな履歴にしたい?

YES: Rebaseを使う

NO: Mergeでも良い

📋 同じファイルの同じ箇所を複数コミットで変更している?

YES: Mergeの方が楽かも(コンフリクト解決1回で済む)

NO: Rebaseでクリーンな履歴に

📚 情報ソース