git rebaseとgit mergeの違いを理解することは、すべてのGit開発者にとって不可欠です。
Git Mergeの仕組み
Git mergeは2つの親コミットを持つ新しいマージコミットを作成します。
メインブランチでgit merge featureを実行すると、Gitは共通の祖先を見つけます。
Git Rebaseの仕組み
Git rebaseはブランチのコミットを別のブランチの先端に移動して履歴を書き換えます。
フィーチャーブランチからgit rebase mainを実行すると、コミットが一つずつ再適用されます。
ビジュアル比較
BEFORE (both branches have diverged from common ancestor C2):
C5---C6---C7 (feature)
/
C1---C2---C3---C4 (main)
AFTER git merge feature (from main):
C5---C6---C7
/ \
C1---C2---C3---C4----M8 (main) ← merge commit M8 has 2 parents
(feature still points to C7)
AFTER git rebase main (from feature):
C1---C2---C3---C4 (main)
\
C5'---C6'---C7' (feature) ← new commits (different SHAs)
Then fast-forward merge:
C1---C2---C3---C4---C5'---C6'---C7' (main, feature) ← linear history機能比較
| 項目 | git merge | git rebase |
|---|---|---|
| 履歴 | すべての履歴を保持 | 線形履歴 |
| コミットSHA | 元のSHAを保持 | 新しいSHAを作成 |
| 安全性 | 非破壊的 | 破壊的 |
| コンフリクト解決 | マージコミットで一度に解決 | コミットごとに解決 |
| チーム向け | 共有ブランチに安全 | 共有ブランチに危険 |
| 取り消し | 簡単 | 難しい |
| git bisect | マージコミットでノイズ | クリーンな履歴で有効 |
| グラフ表示 | 複雑なグラフ | クリーンな一本線 |
Git Merge詳細
ファストフォワードマージ
ターゲットブランチに新しいコミットがない場合。
# Fast-forward merge (no merge commit created)
git checkout main
git merge feature
# Before:
# C1---C2 (main)
# \
# C3---C4 (feature)
# After:
# C1---C2---C3---C4 (main, feature)
# main pointer simply moved forward3方向マージ
両方のブランチが分岐した場合。
# Three-way merge (creates merge commit)
git checkout main
git merge feature
# Before:
# C3---C4 (feature)
# /
# C1---C2---C5---C6 (main)
# After:
# C3---C4
# / \
# C1---C2---C5---C6---M7 (main) ← merge commit M7--no-ff
ファストフォワードが可能でもマージコミットを強制。
# Force merge commit even when fast-forward is possible
git checkout main
git merge --no-ff feature
# Before:
# C1---C2 (main)
# \
# C3---C4 (feature)
# After (with --no-ff):
# C1---C2---------M5 (main) ← merge commit preserves branch history
# \ /
# C3---C4 (feature)
# After (without --no-ff, default):
# C1---C2---C3---C4 (main, feature) ← no evidence of branchGit Rebase詳細
基本的なRebase
標準的なrebaseはコミットをターゲットブランチの上に再適用します。
# Basic rebase workflow
git checkout feature
git rebase main
# Before:
# C3---C4 (feature)
# /
# C1---C2---C5---C6 (main)
# After rebase:
# C3'---C4' (feature) ← new commits!
# /
# C1---C2---C5---C6 (main)
# Then merge (fast-forward):
git checkout main
git merge feature
# C1---C2---C5---C6---C3'---C4' (main, feature) ← linear!インタラクティブRebase
コミットの変更、スカッシュ、並べ替え、削除が可能。
# Interactive rebase - clean up last 4 commits
git rebase -i HEAD~4
# Editor opens with:
pick abc1234 Add user model
pick def5678 Fix typo in user model
pick ghi9012 Add user validation
pick jkl3456 Fix validation edge case
# Change to:
pick abc1234 Add user model
fixup def5678 Fix typo in user model # squash into previous, discard message
pick ghi9012 Add user validation
fixup jkl3456 Fix validation edge case # squash into previous, discard message
# Result: 2 clean commits instead of 4
# "Add user model" (includes typo fix)
# "Add user validation" (includes edge case fix)
# Interactive rebase commands:
# pick = use commit as-is
# reword = use commit but edit message
# edit = use commit but stop for amending
# squash = meld into previous commit (keep message)
# fixup = meld into previous commit (discard message)
# drop = remove commit entirely--onto Rebase
コミットのサブセットを別のベースにrebase。
# --onto: Move a branch to a different base
# Scenario: feature-b was branched from feature-a by mistake
# You want feature-b based on main instead
# D---E (feature-b)
# /
# A---B---C (feature-a)
# |
# F---G (main)
git rebase --onto main feature-a feature-b
# Result:
# D'---E' (feature-b) ← now based on main
# /
# A---B---C (feature-a)
# |
# F---G (main)コンフリクト解決
mergeとrebaseの両方でコンフリクトが発生する可能性があります。
Mergeコンフリクト
すべてのコンフリクトが一度に表示されます。
# Merge conflict workflow
git checkout main
git merge feature
# CONFLICT (content): Merge conflict in src/app.ts
# Automatic merge failed; fix conflicts and then commit
# 1. Open conflicting files and resolve
# 2. Stage resolved files
git add src/app.ts
# 3. Complete the merge
git commit # creates merge commit with conflict resolution
# Abort merge if needed
git merge --abortRebaseコンフリクト
コミットごとにコンフリクトが発生する可能性があります。
# Rebase conflict workflow
git checkout feature
git rebase main
# CONFLICT in commit C3: Merge conflict in src/app.ts
# 1. Resolve conflict in src/app.ts
git add src/app.ts
# 2. Continue rebase to next commit
git rebase --continue
# May hit another conflict in C4...
# CONFLICT in commit C4: Merge conflict in src/utils.ts
git add src/utils.ts
git rebase --continue
# Abort rebase if it gets too complex
git rebase --abort # returns to pre-rebase state
# Skip a problematic commit during rebase
git rebase --skipチームワークフロー
Mergeベースのワークフロー
最も一般的なチームワークフロー。
# GitHub Flow (merge-based)
git checkout -b feature/add-auth
# ... make commits ...
git push -u origin feature/add-auth
# Open PR on GitHub
# Review + approve
# Click "Merge pull request" (creates merge commit)
# Or "Squash and merge" (single commit)Rebase後Mergeワークフロー
rebaseで更新してからmerge。
# Rebase-before-merge workflow
git checkout feature/add-auth
# ... make commits ...
# Before opening PR, update with latest main
git fetch origin
git rebase origin/main
# Force push (safe because it's your own branch)
git push --force-with-lease origin feature/add-auth
# Open PR on GitHub
# Merge with --no-ff to record integration point
git checkout main
git merge --no-ff feature/add-authSquash and Mergeワークフロー
すべてのコミットを1つにまとめます。
# Squash and merge (via GitHub UI or CLI)
git checkout main
git merge --squash feature/add-auth
git commit -m "feat: add authentication system"
# Before:
# main: A---B---C
# feature: A---B---D---E---F---G
# After squash-merge:
# main: A---B---C---H ← H contains all changes from D+E+F+G
# (feature branch can be deleted)Rebaseの黄金律
共有リモートブランチにプッシュ済みのコミットをrebaseしないでください。
# DANGEROUS: Rebasing a shared branch
git checkout shared-feature
git rebase main
git push --force # !! This rewrites history for everyone!
# SAFE: Rebasing your own local branch
git checkout my-local-feature
git rebase main
# No push yet, or push --force-with-lease to your own branch
# SAFE: Using --force-with-lease instead of --force
git push --force-with-lease origin my-feature
# Fails if remote has commits you haven't seenベストプラクティス
Git Rebase vs Merge Best Practices:
1. Use merge for integrating feature branches into main
- Creates clear integration points
- Safe for shared branches
- Easy to revert entire features
2. Use rebase to keep feature branches up-to-date
- git rebase main (before opening PR)
- Creates clean, linear history
- Makes code review easier
3. Use interactive rebase to clean up before PR
- Squash fixup commits
- Reword unclear commit messages
- Drop debugging commits
4. Use git pull --rebase as default
- Avoids unnecessary merge commits
- git config --global pull.rebase true
5. Never rebase shared/pushed commits
- Only rebase your own unpushed work
- Use --force-with-lease, never --force
6. Use squash-merge for feature branches
- One clean commit per feature on main
- Detailed commits preserved in PR history
7. Use --no-ff for important merges
- Preserves the fact that a branch existed
- Makes git log --first-parent usefulよくある質問
rebaseとmergeのどちらを使うべき?
ローカルブランチの更新にrebase、メインブランチへの統合にmergeを使用。
rebaseは危険?
共有ブランチでは危険。ローカルの未プッシュコミットには安全。
squash and mergeとは?
すべてのコミットを1つにまとめてマージ。
rebaseを取り消せる?
はい。git reflogを使用。
チームはrebaseとmergeのどちら?
ハイブリッドアプローチが最適。
git pull --rebaseとは?
マージコミットの代わりにrebaseで更新を取得。
関連ツールとガイド
- JSON Formatter - Format Git config files
- Diff Checker - Compare code changes before merge
- Git Commands Cheat Sheet
- Git Branch Naming Convention
- Git Cherry-Pick, Revert, and Reset Guide
- Git Workflow Strategies