找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3482

积分

0

好友

478

主题
发表于 2026-2-11 20:11:00 | 查看: 33| 回复: 0

写这篇文章的契机,源于一次内部技术分享会上的讨论。mergerebase这两个指令都用于合并分支代码,rebase能将分支记录变成一条直线,而merge则保留了分支的树状图。我个人更习惯使用merge,因为在大部分开发场景中,我很少需要用到rebase

我们先来看一个开发中非常常见的场景:两个人基于 master 分支的同一个提交节点,各自拉出一个新的 feature 分支。其中,开发者 A 有多个提交,并率先完成任务,将代码合入了 master;开发者 B 也有多个提交,但在他完成任务准备合入 master 时,发现有冲突。我在这里模拟的每个提交都是针对同一个文件进行的,因此每个提交都可能产生冲突。

在 A 合并到 master 之前,分支图如下:

Git分支图,显示feature/A和feature/B从同一起点master分支分叉

可以看到,feature/A 分支和 feature/B 分支都是基于 master 分支最新的提交(docs: add README)拉出的,两条分支并行开发。为了模拟时间线,我用顺序数字表示提交信息,两条分支各自新增了 3 个提交。

现在,我们通过 merge 把 A 合并到 master 上:

git checkout master
git merge feature/A

Git分支图,显示feature/A分支已通过merge合并到master

可以看到,A 通过 merge 合入 master 后,masterfeature/A 的提交历史一致了。

好了,文章开头提到的场景已经出现。此时如果你是开发者 B,你会怎么做?如果是我,可能会选择 merge。那我们先看看使用 merge 会是什么效果。

git checkout master
git merge feature/B

由于我的每个提交都是针对同一个文件进行更改的,所以最终合并时必然需要解决冲突。

代码编辑器界面显示Git合并冲突

可以看到,这里的冲突是让你一次性集中解决的。解决完冲突后提交,再看分支图:

Git分支图,显示feature/B通过merge合入master,并产生新的合并提交节点

可以看到,merge 会创建一个新的合并提交,将两个分支平行地合并,从而保留了完整的分支历史。

这里顺便解释一下,为什么有时候 merge 没有产生额外的合并节点(MR节点),有时候却有。其实,大多数情况下都会有。比如这里的 feature/A 合入 master 就没产生合并节点,而 feature/B 后合入 master 就产生了。

merge 是否会创建新的合并节点,取决于 Git 是否需要执行一次“非快进式”合并。

  • 快进式合并(fast-forward)
    master 分支的指针还停留在你分支起点的那个提交时。比如 feature/A 合入到 master 的时候,masterfeature/A 的起点相同,这就属于快进式合并,不会产生新的合并节点。
  • 非快进式合并
    master 分支相较于你的分支有了新的提交时。比如 feature/B 的起点是最初的 docs: add README,但在 feature/B 准备合入 master 时,master 的最新提交已经是 feature/Adocs: 5 了。在这种情况下,即使没有代码冲突,非快进式合并也会产生一个新的合并提交节点。

好了,我们现在撤消 feature/Bmastermerge 操作,改用 rebase 来看看有什么不同。

撤消之后,分支图恢复到如下状态,master 仍然停留在 docs: 5

Git分支图,显示撤销合并后,master在docs:5,feature/B保留原有提交

现在使用 rebase

git checkout feature/B
git rebase master

可以看到,编辑器提示需要解决冲突,而这还只是第一个提交产生的冲突:

代码编辑器显示Git交互式变基过程中的冲突解决界面

如果每个提交都有冲突,那么每个提交都需要单独解决一次冲突。 一旦解决完一个冲突并执行 git add,再运行 git rebase --continue,Git 就会继续重放下一个提交。如果同一段代码在多个提交中都修改过,你会感觉像是在重复解决同一个冲突。

现在,我们回过头来看 rebase 完成后的 Git 历史树:

Git分支图,显示feature/B通过rebase变基到master后,提交历史成为一条直线

可以看到,最终的 Git 提交历史变成了一条直线,看起来确实整洁不少。并且,你的分支所有提交的“时间线”都被提前了,不会产生合并提交节点。但是,你分支上原本的提交哈希值(如 docs: 3docs: 4docs: 6 对应的哈希值)都发生了变化。

结论

从 Git 提交记录看

  • merge:保留了原始的历史记录,是非线性的,容易产生合并提交节点。
  • rebase:将自己的提交记录“移植”到目标分支的最新提交之后,并更改你之前提交的 commit hash,历史是线性的,不会产生多余的合并提交节点。

从解决冲突的方式看

  • merge:在最终合并的那一刻,一次性解决所有冲突,直接对比两个分支代码的最终形态。
  • rebase:会按顺序逐个重放你的所有提交,但凡有冲突的提交,都需要你依次去解决。

rebase 有其适用的场景,但如果不清楚其后果,无脑使用 merge 是更稳妥的选择。一般来说,只有在你独自使用的、且尚未推送到远程仓库的分支上,可以使用 rebase 来整理提交历史。其余所有情况,尤其是在团队协作的共享分支上,都建议使用 merge

重要警告:绝对不能在公共分支(如团队共用的 developmaster 分支)上使用 rebase,因为这会改变公共分支上已有的提交哈希值,导致其他协作者拉取代码时出现混乱和冲突。

至于 git rebase -i,它是一个强大的工具,可以用来整理提交顺序、合并多个小提交或改写提交信息,但这通常用于本地分支的个人提交整理。

希望这篇基于具体场景的对比,能帮助你在日常的 Git 工作流中,做出更合适的选择。如果你有更多关于版本控制或 DevOps 实践的想法,欢迎到技术社区交流探讨。

一张表示惊讶或思考的幽默表情包




上一篇:腾讯混元 HY-1.8B-2Bit 开源:超低比特量化,0.3B端侧AI模型手机耳机可跑
下一篇:掌握这些CSS字体与文本属性,搞定90%网页文字样式问题
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-23 13:08 , Processed in 0.388750 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表