mrsaynothing.dev
git merge vs rebase——十秒做完决定

> git merge vs rebase——十秒做完决定▋

merge 还是 rebase,一个问题定生死:这个 commit 离开过你的机器吗?决策表、fast-forward 的真相,以及 rebase 翻车后的 reflog 抢救。

mrsaynothing· 2026年10月5日· 3 分钟读完

这两个命令你职业生涯里要敲几千次,而网上一半的教程把它讲成宗教——一边是「什么都 rebase,历史必须干净」,另一边是「永远别 rebase,会毁掉工作」。两边都没说那条十秒钟就能说完的真规则。这篇文章只给决策,不传教,它属于Undo Anything in Git 这个集群——和 git revert vs reset 是同一家族的「两个命令、一个问题」。Git 本身 2005 年 4 月用十天从零写成;merge 与 rebase 之争从那天起就没停过。

五分钟清单

git merge vs rebase——那一个问题 一个提交准备落地 git status -sb 显示领先/落后 别人会拉这条分支吗? 查分支,别靠直觉 git log origin/feature..feature 别人可能拉过 → 只属于你 → git merge 记录真实发生的事 git rebase 线性历史,方便评审 事后历史看着不对? git reflog → reset --hard HEAD@{1}
fig 1——merge 还是 rebase 的决策树。点你自己的分支。

git merge 和 git rebase 的区别是什么?

两个命令落地的代码一样;它们争的是之后的历史该怎么讲。merge 把分歧白纸黑字记下来:一个新提交、两个父提交,分叉为后来读图的人保留。rebase 假装你是从今天的基底出发的:你的三个提交被重放成三个全新提交、全新 SHA,旧的从任何分支都够不着了。

同五个提交,我在一次性 bench 里用 git 2.47.3 各跑了一遍。merge 那次保住了每个原始 SHA,只多了一个结;rebase 那次改掉了分支提交的身份——c3 进去时是 5d48940,出来变成 6b2be0a。同一棵树,两种历史:

merge 之后 c1 c2 c3 · c4 c5 merge 提交 · 两个父提交 rebase 之后 c1 c2 c5 c3' 6b2be0a c4' 67c0442 一条直线——没有结,旧 SHA 消失
fig 2——同一棵树,两种历史。带撇号的提交是同样的改动、新的身份。

验证:git log --oneline --graph → 出现 |\ 的结是 merge,一条直线说明 rebase 赢了

merge 什么时候变成 fast-forward?

如果你开分支之后 main 没动过,就没有东西可 merge——git 只是把标签往前滑。这就是 fast-forward,也是 rebase 派不用强求就能拿到干净直线的原因:把 feature rebase 到 main 上,merge 就变成 fast-forward,图保持一条直线。来自 bench:

* 67c0442 c4 feature 2      # rebase 后再 merge——fast-forward
* 6b2be0a c3 feature 1      # 新 SHA——rebase 前是 5d48940
* 1398836 c5 main moved on
* facd5a4 c2 main work
* f45b000 c1 base

五个提交,零个结。代价是:那些带撇号的 SHA 只存在于你的机器上。push 出去,同事的 clone 里还是原版——同一个提交的两个版本,正是第五节要讲的乱局。

验证:git merge --ff-only feature → 输出里出现 Fast-forward,没有产生 merge 提交

哪些提交还只属于你?

答案是一条命令,不是一种感觉。git log origin/main..HEAD 列出 origin 从没见过的提交——这些可以 rebase。反过来看的 git log origin/main..origin/main 按定义是空的;真正要查的是别人基于它干活的那条分支。只要列表里混进一个别的 clone 可能已有的提交——一次 push、一个开着的 pull request、一条推过又强推过的分支——它就不再只属于你,本地图怎么说都不算。

没 push 的工作正是 rebase 的用武之地:把它重放到最新基底上,你的 pull request 读起来就是一条干净的序列。这也是为什么「pull 时 rebase」对私有分支是合理默认——git pull --rebase 把你的本地提交重放到新来的提交之上,而不是每次同步都织一个 merge 结。stash 的边角案例在 git stash a single file;fork 的变体在 sync fork with upstream。

rebase 共享分支会毁掉什么?

每个握有旧提交的 clone,从这一刻起和你的机器对现实各执一词。rebase 给你的提交换了新 SHA;推上去的分支和重写后的分支不再共享任何祖先,下一次 push 就得用 --force——而所有拉过旧版本的人都得把他们手里那份「已不存在」的提交 merge 进来。这些复制品会在之后每一次 merge 里冒头。这不是思想实验:重复提交的 merge 就是经典后续,解开它要搭上一个下午。

规则只有一句话,官方书里它是以警告的形式出现的,不是建议:

Do not rebase commits that exist outside your repository and that people may have based work on.

—— Pro Git 第 2 版,「Git Branching — Rebasing」

钉在具体 SHA 上的评审评论也在同一步里脱落——rebase 一个开着的 pull request,评审语境就跟着旧提交 ID 一起蒸发。

怎么撤销一个 rebase?

旧提交在 rebase 之后还活着——reflog 就是它们的藏身处。rebase 只挪分支指针,不删对象。bench 里的抢救现场,在 reset --hard 扔掉两个提交之后:

$ git reflog | head -4
1398836 HEAD@{0}: reset: moving to HEAD~2
67c0442 HEAD@{1}: merge feature: Fast-forward
1398836 HEAD@{2}: checkout: moving from feature to main
67c0442 HEAD@{3}: rebase (finish): returning to refs/heads/feature

$ git reset --hard HEAD@{1}
HEAD is now at 67c0442 c4 feature 2

一行,分支复原,staged 和 unstaged 的工作一并回来。reflog 条目默认大约 90 天后过期,所以抢救是有限时的——机制和 git undo last commit 里的一样,revert/reset 的完整决策在 git revert vs reset。

验证:git reflog | head -3 → rebase 前的分支尖端正停在 HEAD@{1},一次 reset 之遥

你的处境该用哪个命令?

这张表就是全文的六行版——按你分支的处境筛选。

全部 feature 共享 本地 fork 抢救
处境 命令 为什么赢
你的 feature 分支,从没 push git rebase main 线性历史,评审看到一条干净序列
别人会拉的分支 git merge 不改写 SHA——谁的 clone 里都不会长出重复提交
本地 main 分叉了,什么都没 push git pull --rebase 同步不打结;你的提交留在最上面
fork 追 upstream git merge upstream/main 和 fork sync 的流程一致;PR 保持挂着
必须立刻落地的 hotfix git merge --no-ff 就算能 fast-forward,也留下修复进入的可见标记
rebase 翻车了 git reset --hard HEAD@{1} reflog 还留着 rebase 前的尖端(约 90 天)
--squash 算哪边的?

git merge --squash 把一条分支的改动收进来、作为一坨未提交的东西放进 stage——没有 merge 提交,也不和分支历史挂钩。搜索下拉里「merge vs rebase vs squash」天天有人搜:squash 是第三个答案,用来在落地净改动之后把分支扔掉。它不改写任何东西;只是拒绝记录分支的内部过程。

团队能不能只选一个?

很多团队就这么干:本地 rebase、共享 merge 是最常见的政策,GitHub 的 merge 按钮默认也走 merge。「全都 rebase」同样可行——前提是共享分支永远不被改写,这是所有工作流争论里唯一活到最后的一句话。

十秒版本,最后一次:这个提交离开过你的机器吗?没有 → rebase。有 → merge。错的那个还是赢了的时候,reflog 就是出口——其余的撤销地图在 git undo hub。

faq

— mrsaynothing

$ 下一篇实战指南,直达邮箱

每篇一封。修完就走。

self-hosted · 无第三方 · 一键退订

这是什么?