git merge と git rebase の違いとは?
Gitでブランチを統合する方法は主にmergeとrebaseの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 merge | 1コミットに圧縮 | 機能単位が明確な場合 |
| 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つのテキストの差分を視覚的に比較できます。