Git:merge 与 rebase 分别解决什么问题

📅 2026/8/19 11:17:04
Git:merge 与 rebase 分别解决什么问题
merge 与 rebase 都是把「一条线上的新提交」合进「另一条线」的手段但它们改写历史的方式不同。团队里最常见的争论是该 merge 还是该 rebase争论背后往往不是品味而是两件事没分开要不要保留分叉痕迹以及这些提交有没有已经被人拉走过。下面先讲各自解决什么问题再讲怎么选。一、先说一个具体麻烦你从main拉出feature做了三个提交。与此同时main上又多了别人的两个提交。现在要把功能合回去。你会遇到两种「干净」的幻想1历史里能看出「曾经分叉又汇合」2历史看起来像一条直线仿佛你一直基于最新main开发merge 更接近前者rebase 更接近后者。两者都能让代码合在一起但仓库历史长得不一样后续出问题时的可读性也不一样。二、merge保留「汇合」这件事git merge把两条历史接在一起。若不能快进fast-forward通常会产生一个合并提交merge commit父母有两个一边是你的分支一边是目标分支。它解决的问题是1如实记录并行开发谁何时分叉、何时合入图上看得见2不改写已有提交的哈希在普通 merge 场景下对已经推送分享过的历史更安全3冲突集中处理一次在合并那一刻解决代价是1历史可能出现较多汇合节点图更「茂盛」2若团队不喜欢 merge commit需要额外约定如允许 fast-forward only下面是一个最小示例。gitcheckout maingitpullgitmerge feature上面代码中若双方都有独有提交Git 可能创建一个 merge commit。feature上的原提交哈希一般保持不变。三、rebase把提交「挪到新基地上重放」git rebase把你的提交拿起来按顺序在另一个基地常是更新后的main上重新应用。结果常常是一条更直的历史。它解决的问题是1让功能分支基于最新主干减少「我基于过时 main 开发」的漂移2历史更线性git log读起来像故事而不是树丛3合入前整理揉提交、改说明常配合 interactive rebase代价是1重写提交哈希重放后的提交不是原来的那些对象2若别人已经基于你的旧提交工作强推会让协作混乱3冲突可能在「重放每一个提交」时反复出现下面是一个最小示例。gitcheckout featuregitfetch origingitrebase origin/main上面代码中feature上的提交被接到最新origin/main后面。看起来整洁但本地原提交已被新提交替代。四、一张选择图核心判断只有一句这些提交是否已经分享给别人、并可能被别人当作基地1只在自己电脑上的分支 → rebase 通常安全也常更干净2已经 push 到共享远程、别人可能拉过 → 优先 merge若必须 rebase需要团队明确约定并协调3受保护的主干main/master→ 不要 rebase 公共历史很多团队的折中是1功能分支日常对main做 rebase保持分支新2合入主干用 merge或平台上的 merge / squash merge 策略3禁止对已共享历史私自push --force五、和 squash 的关系平台上的「Squash and merge」会把功能分支多个提交压成主干上的一个提交。它既不是经典 merge 图也不是简单 rebase而是合入时改写呈现方式。它解决的是主干只要「一个功能点一个提交」细节留在 PR 讨论里。代价是分支上的细粒度提交不会原样进入主干。对回溯「哪一行何时引入」有时不够细但主干更干净。选不选 squash是团队规范问题不必道德化。六、冲突时心态merge 冲突发生在汇合点一次处理「两边最终差异」。rebase 冲突可能在重放路径上多次停下每次处理「该提交相对新基地」的差异。若功能分支很长、与主干分叉很久rebase 可能更痛苦。这时不一定要硬 rebasemerge 一次、或先把大分支拆小往往更省命。七、常见误区1把 rebase 当成「更高级的 merge」它们目标不同没有绝对高下。2在公共分支上 rebase 后强推这是协作事故的常见来源。3为了直线历史无视可追溯性直线好看但有时 merge commit 能解释「为何同一天两条线」。4冲突时随意git rebase --skip跳过等于丢掉该提交的改动需非常确定。5忽略钩子与 CI 对「强制推送」的保护仓库规则在保护你不是故意刁难。八、小结merge 解决的是把分叉历史诚实汇合尽量不改写已分享的提交。rebase 解决的是把本地或约定可改写的提交挪到新基地上换一条更直的故事线。先问「提交有没有被人用过」再选命令。会选比争论哪一个「更正确」有用。完