Stage 2・中級編 ー Lesson 2-2

git merge とは? ブランチを統合しよう

git merge で別ブランチのコミットを取り込み、変更を1本に統合する

💡 たとえるなら

ゲームのセーブには本来ない操作だが、別ルートで進めたセーブデータを本編のセーブに合流させるイメージ

前のレッスンで feature ブランチを作りました。実験がうまくいったら、いよいよその変更を main に取り込みます。これが マージ(merge) です。

マージって何?

「あるブランチで作ったコミットを、別のブランチに合流させる」操作です。コマンドはシンプル:

# 取り込む先(main)に乗り換える
$ git switch main
# feature の変更を main に取り込む
$ git merge feature

ポイントは 「乗り換えてから merge する」 こと。git merge X は「今いるブランチに X を取り込む」という意味なので、main に取り込みたいなら main にいる状態で実行します。

2種類のマージ:fast-forward と 3-way

git merge には2つの動き方があります。状況によって自動で切り替わるので、概念を押さえておけばOK。

① fast-forward マージ(早送り)

main が feature を作ったあと 何も進んでいない ときは、ただ main の HEAD を feature の先端まで「早送り」するだけ。新しいマージコミットは作られません。

A B C D main(旧) feature merge すると main の HEAD が D まで早送りされる(マージコミットなし)

「main は止まったまま、feature だけ進んだ」状況で起きます。

② 3-way マージ(合流)

main も feature も両方コミットが進んでいるときは、両者を統合した マージコミット が新しく作られます。

A B E C D M main feature M = マージコミット

3-wayマージは「2つのブランチが分岐してそれぞれ進んだ後、合流させた歴史」を残します。後から git log --graph で見ると枝分かれが視覚的にわかります。

💡 「3-way」の意味
Git は 3つのコミットを見比べてマージ結果を作ります:① 共通の祖先(分岐前のコミット)、② 自分の側の先端(main)、③ 相手の側の先端(feature)。この「3点を比較する」方式なので 3-way(3方向)マージと呼ばれます。

やってみよう ①:fast-forward マージ

まずは fast-forward から。my_project で:

# feature ブランチで作業
$ git switch -c feature
$ echo "新機能の説明" >> memo.txt
$ git add memo.txt
$ git commit -m "新機能の説明を追記"
# main に戻ってマージ(main は何も進んでいない!)
$ git switch main
$ git merge feature
Updating a1b2c3d..e4f5g6h
Fast-forward
 memo.txt | 1 +
 1 file changed, 1 insertion(+)

main が止まったままだったので fast-forward が起きました。「Fast-forward」と表示されていますね。マージコミットは作られていません。

やってみよう ②:3-way マージ

次は main も進んでいる状態 を作って、3-way マージを起こしてみます。さっきのマージで使った feature ブランチは消しておいて:

# いま main にいる前提。前回の feature を消す
$ git branch -d feature
# feature ブランチで別ファイルを追加
$ git switch -c feature
$ echo "feature の作業" > feature.txt
$ git add feature.txt
$ git commit -m "feature.txt を追加"
# main に戻って、別のファイルを変更(ここがポイント!)
$ git switch main
$ echo "main 側の追記" >> memo.txt
$ git commit -am "main で memo を更新"
# ここで merge → 両ブランチが進んでるので 3-way になる
$ git merge feature
Merge made by the 'ort' strategy.
 feature.txt | 1 +
 1 file changed, 1 insertion(+)

今度は 「Merge made by the ‘ort’ strategy」 と出て、マージコミットが作られた ことがわかります(“ort” は Git の3-wayマージのデフォルト戦略名)。

💡 マージコミットのメッセージ
3-wayマージするとエディタが開いて Merge branch 'feature' という既定メッセージが表示されます。そのまま保存して閉じればOK。

ちなみに今回は 違うファイル を別々に変更したのでぶつかりませんでしたが、同じファイルの同じ行 を別々に変更していると衝突します。詳しい対処は次のレッスン 「コンフリクトを解決する」 で扱います。

マージの結果を確認する

git log --oneline --graph --all で履歴を見ると、2つのマージの違いが一目でわかります。

fast-forward マージの結果

$ git log --oneline --graph --all
* e4f5g6h (HEAD -> main, feature) 新機能の説明を追記
* a1b2c3d 最初のメモを追加

1本道 で枝分かれがありません。マージコミットも作られていないので、見た目は「最初から1本だった」かのように見えます。HEAD -> main, feature と並んでいるのは、main と feature が同じコミットを指しているという意味(fast-forward の特徴)。

3-way マージの結果

$ git log --oneline --graph --all
*   9z8y7x6 (HEAD -> main) Merge branch 'feature'
|\
| * 5w4v3u2 (feature) feature.txt を追加
* | 7t6s5r4 main で memo を更新
|/
* e4f5g6h 新機能の説明を追記
* a1b2c3d 最初のメモを追加

枝分かれと合流 が線で表現されています。読み方:

  • 一番上 Merge branch 'feature' = マージコミット(さっきの 3-way で生まれたやつ)
  • |\ = ここから2つに枝分かれしている
  • | * = 右側の枝(feature)のコミット feature.txt を追加
  • * | = 左側の枝(main)のコミット main で memo を更新
  • |/ = ここで合流していた(共通の祖先 e4f5g6h

比較まとめ

fast-forward3-way
マージコミットできないできる
履歴の見た目一本道枝分かれが残る
起きる条件取り込み先が止まっていた両方が進んでいた
💡 常に 3-way にしたい場合
git merge --no-ff feature--no-ff を付けると、fast-forward 可能でも必ずマージコミットを作ります。「どの feature を統合したか」を履歴に残したいチームでよく使われます。詳しくは Stage 3-6 で扱います。

マージしたあとの feature ブランチ

main に取り込んだら、もう feature ブランチは不要なことが多いです。前レッスンの git branch -d で削除しましょう:

$ git branch -d feature
Deleted branch feature (was e4f5g6h).

「マージしてから削除」が定番フロー。

マージで衝突が起きたら?

両ブランチが 同じファイルの同じ行 を別々に書き換えていると、Gitは自動マージできずに止まります。詳しい解決方法は次のレッスン 「コンフリクトを解決する」 で扱います。

このレッスンのまとめ

できるようになったこと
git merge で別ブランチを取り込める
✅ 「取り込む先に乗り換えてから merge」が基本
fast-forward3-wayマージ の違いがわかった
git log --graph --all で履歴の構造を確認できる
✅ マージ後の feature ブランチは git branch -d で削除する

✏️ 理解度チェック

0 / 3 正解

各問題、選んだ瞬間に正解と解説が表示されます。気軽に試してください。

  1. Q1. feature ブランチを main に取り込みたい。正しい操作は?
  2. Q2. fast-forward マージとはどんなマージ?
  3. Q3. 両ブランチが分岐している場合の merge では何が作られる?