git merge と git rebase の違いとは?

Gitでブランチを統合する方法は主にmergerebaseの2つです。どちらもブランチの変更を取り込む操作ですが、履歴の残り方がまったく異なります。

  • merge: 2つのブランチの履歴を合流させ、マージコミットを作成する
  • rebase: ブランチの起点を移動し、直線的な履歴を作る

merge の仕組み

# feature ブランチを main にマージ
git checkout main
git merge feature

マージ後の履歴:

*   マージコミット (main)
|\
| * feature のコミット3
| * feature のコミット2
| * feature のコミット1
|/
* main の既存コミット

merge のメリット

メリット説明
履歴が正確いつ、どのブランチからマージされたかが明確
安全既存のコミット履歴を書き換えない
シンプルコンフリクト解消が1回で済む

merge のデメリット

  • マージコミットが増え、履歴が複雑になりやすい
  • git log --graph で見ると線が入り組む

rebase の仕組み

# feature ブランチを main の先頭に移動
git checkout feature
git rebase main

リベース後の履歴:

* feature のコミット3 (feature)
* feature のコミット2
* feature のコミット1
* main の最新コミット (main)
* main の既存コミット

rebase のメリット

メリット説明
履歴がきれい直線的で読みやすい
bisect しやすい線形履歴なのでバグ追跡が容易
レビューしやすいPR の差分が明確

rebase のデメリット

  • コミットハッシュが変わる(履歴の書き換え)
  • コンフリクトをコミットごとに解消する必要がある
  • 使い方を誤ると他の開発者に影響を与える

使い分けの基本ルール

rebase を使うべきケース

# 1. ローカルの feature ブランチを最新の main に追従させる
git checkout feature
git rebase main

# 2. PR作成前にコミットを整理する
git rebase -i HEAD~3

ポイント: まだプッシュしていないローカルのコミットに対して使う。

merge を使うべきケース

# 1. PR を main にマージする(GitHub上で実行)
# 2. リリースブランチを main に統合する
git checkout main
git merge release/v1.2.0

ポイント: 共有ブランチへの統合には merge を使う。

黄金ルール: rebase してはいけないケース

公開済み(プッシュ済み)のブランチを rebase してはいけません。

# NG: 他の開発者も使っている共有ブランチを rebase
git checkout main
git rebase feature  # 絶対ダメ!

# NG: プッシュ済みのブランチを rebase して force push
git push --force origin shared-branch  # 他の開発者が困る

なぜダメなのか?

rebase はコミットハッシュを書き換えます。他の開発者がすでにそのコミットを基に作業していた場合、整合性が取れなくなります。

Interactive Rebase でコミットを整理する

# 直近3つのコミットを整理
git rebase -i HEAD~3

エディタが開き、以下のような内容が表示されます:

pick abc1234 ログイン機能を追加
pick def5678 typo修正
pick ghi9012 テストを追加

# コマンド:
# p, pick = そのまま使う
# r, reword = コミットメッセージを変更
# e, edit = コミットを編集
# s, squash = 前のコミットに統合(メッセージ結合)
# f, fixup = 前のコミットに統合(メッセージ破棄)
# d, drop = コミットを削除

よく使うパターン

pick abc1234 ログイン機能を追加
fixup def5678 typo修正
pick ghi9012 テストを追加

typo修正を前のコミットに統合し、きれいな履歴にします。

コンフリクト解消のコツ

merge のコンフリクト解消

git merge feature
# コンフリクト発生
# ファイルを編集して解消
git add .
git commit  # マージコミットが作成される

rebase のコンフリクト解消

git rebase main
# コンフリクト発生(コミットごとに発生する可能性あり)
# ファイルを編集して解消
git add .
git rebase --continue  # 次のコミットに進む

# リベースを中断したい場合
git rebase --abort

便利なツール

# マージツールを使う
git mergetool

# コンフリクトマーカーの見方
<<<<<<< HEAD
main ブランチの内容
=======
feature ブランチの内容
>>>>>>> feature

チーム開発でのベストプラクティス

推奨ワークフロー

# 1. feature ブランチを作成
git checkout -b feature/login main

# 2. 開発を進める
git commit -m "feat: ログイン画面を実装"
git commit -m "fix: バリデーションエラーを修正"

# 3. PR作成前に main を取り込む(rebase)
git fetch origin
git rebase origin/main

# 4. コミットを整理(必要に応じて)
git rebase -i origin/main

# 5. プッシュ(初回は -u、リベース後は --force-with-lease)
git push --force-with-lease origin feature/login

# 6. GitHub上で PR を作成 → Squash and Merge

—force-with-lease を使う理由

# NG: force push は他の人のプッシュを上書きする
git push --force

# OK: force-with-lease は他のプッシュがあると失敗する
git push --force-with-lease

GitHub のマージ戦略

戦略履歴おすすめ
Create a merge commitマージコミットあり大規模プロジェクト
Squash and merge1コミットに圧縮機能単位が明確な場合
Rebase and mergeリベースして統合線形履歴を重視する場合

おすすめ設定

多くのチームでは Squash and merge がバランスが良いです:

  • PRの細かいコミットが1つにまとまる
  • mainの履歴がきれい
  • PRのタイトルがコミットメッセージになる

設定でデフォルトを変更する

# pull 時にデフォルトで rebase する
git config --global pull.rebase true

# merge 時にデフォルトで --no-ff にする(常にマージコミットを作る)
git config --global merge.ff false

# rebase 時にautosquashを有効にする
git config --global rebase.autosquash true

よくあるトラブルと対処法

rebase 中にコンフリクトが多すぎる

# rerere を有効にすると、同じコンフリクトの解消を記憶してくれる
git config --global rerere.enabled true

rebase を元に戻したい

# reflog から rebase 前の状態を見つける
git reflog
# abc1234 HEAD@{2}: rebase (start): checkout main

git reset --hard HEAD@{2}

merge を取り消したい

# マージコミットを取り消す
git revert -m 1 <merge-commit-hash>

まとめ

場面推奨
ローカルで最新のmainを取り込むgit rebase main
PR作成前のコミット整理git rebase -i
PRをmainに統合merge(Squash and merge推奨)
共有ブランチの統合git merge
リリースブランチの統合git merge --no-ff

rebaseとmergeはどちらが「正しい」ということはなく、場面に応じて使い分けることが大切です。チームでルールを統一し、一貫した運用を心がけましょう。


ブランチの差分確認には、Assistyのdiffツールが便利です。ブラウザ上で2つのテキストの差分を視覚的に比較できます。