理解 git rebase 和 git merge 的区别对每个 Git 开发者都至关重要。两个命令都将更改从一个分支集成到另一个分支,但方式截然不同。
Git Merge 的工作原理
Git merge 创建一个新的合并提交,它有两个父提交,合并两个分支的历史。原始分支历史被完整保留。
当你在主分支上运行 git merge feature 时,Git 找到共同祖先,创建合并提交,并推进主分支指针。
Git Rebase 的工作原理
Git rebase 重写历史,将你的分支提交移动到另一个分支的顶端。
当你从功能分支运行 git rebase main 时,Git 识别共同祖先,暂时移除你的提交,快进到 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 | 合并提交可能干扰 | 干净的线性历史有助于 bisect |
| 图形可视化 | 带分支的复杂图 | 干净的单线 |
Git Merge 详解
快进合并
当目标分支没有新提交时,Git 可以简单地向前移动分支指针。
# 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 forward三方合并
当两个分支都有新提交时,Git 执行三方合并。
# 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:始终创建合并提交
--no-ff 标志强制 Git 创建合并提交,即使可以快进。
# 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
交互式 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
--onto 标志让你将提交子集 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 时,冲突可能在每次提交重放时出现。
# 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 的工作流
最常见的团队工作流,完全使用 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 更新功能分支,然后合并。
# 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 工作流
将所有功能分支提交合并为单个提交。
# 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 vs merge?
使用 rebase 在开 PR 前更新本地功能分支。使用 merge 将功能分支集成到主分支。
rebase 危险吗?
Rebase 重写历史,对共享分支危险。对本地未推送的提交是安全的。
什么是 squash and merge?
将功能分支的所有提交合并为目标分支上的单个提交。
可以撤销 rebase 吗?
可以。使用 git reflog 找到 rebase 前的状态。
团队应该用 rebase 还是 merge?
大多数团队受益于混合方法:开发者在本地 rebase,然后 squash-merge 到主分支。
git pull --rebase 是什么?
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