管理 git 分支时,用 merge 还是 rebase?

前段时间看到了一篇文章------《直接使用git pull拉取代码,被同事狠狠地diss了!》,以及最近的项目也是要求用 rebase,于是研究了一下。

不喜欢浪费时间,所以先说结论:

对于普通公司的项目,采用 merge 优于 rebase。

更多的场景,应当根据具体需要,选择合适的策略。

如果你对场景感兴趣,可以继续看后文。

提交历史复杂,难以追溯问题

推荐用 rebase 的理由,大多数都是讨厌"乱糟糟的 git log --graph 结果",从而导致"观感不适",最后得出"提交历史复杂,难以追溯问题"的结论。

在评论(忘了是哪篇了)中有人提到 Vue 项目用的是 rebase,所以我们先看一下 Vue 提交历史的 Graph (使用的命令是 git log --graph --oneline --decorate --all ),可以看到是笔直的一条线。

接着是我自己的一个项目,使用同样的命令,挑了一个比较复杂的部分截了个图,跟 Vue 的一对比,明显更复杂。

如果单看这两个案例,好像 rebase 的结果更"清爽"一点?实际上并非如此。

同样是第二张图的项目,下面这张截图也挺"清爽"的:

并且这张图中还能看到"明显的分叉",分两次(v0.34.1 以后,有两个 tag)完成了好些个改动。

比如从 e977bab 这次提交以后,单独"切"出来完成了两次版本的更新。v0.35.0 提交了 6 项内容,v0.36.0 提交了十几项内容。中间在 63f07f5 这次提交以后,合并进了左侧分支,完成 v0.35.0 版本的发布(tag: v0.35.0 对应的 * 在左侧,而不是在右侧)。

所以提交历史复杂,并不是用 merge 的问题,或者说不仅仅只是用 merge 的问题。

如果仅仅只是为了得到干净且清爽的提交记录,我们只需要在生产(或主要)分支上,使用 git log --oneline​ 命令,就能得到跟前面 Vue 提交历史相似的结果。(之所以说相似,是因为看不到分叉)

同样的结果,在 VS Code 里,使用 GitLens 插件也能看到(我一般就在这里看历史记录)。

到这里,我们可以得出一个结论:想要干净的提交历史,并不一定需要 rebase。

但是我们还有一个问题没有解决,

如何保持干净的 graph 结果

全都用 rebase,一定会造成"开发过程丢失"的问题,也就是:

  1. 谁从什么版本开始开发了哪些功能
  1. 开发完某个功能以后,在什么时候合并进了主分支

关于这两个问题,我知道可以通过其他方式解决。

比如前段时间,我接手了一个模块,其中有一部分是前人开发到一半还没有正式上线(分支A),另一部分是线上问题修复和新需求的开发(一堆 feat 分支)。

我为什么知道是开发了一半呢?因为我发现 release 中不包含需要解决的问题,切到 uat 分支才发现有相关代码。理所当然的,我就从 uat 分支里切了一条分支出来继续开发。

开发了一段时间以后,我提前开始考虑上线的事情,结果想起我是从 uat 分支切的代码,而这个项目,有好几个模块,虽然我负责的模块只有我在开发,但是其他模块也有人在提交代码。如果直接上线我现在这个分支,将会有内容被错误上线的风险。

所以我需要知道前人开发到一半的功能,究竟是从 release 的哪个版本以后开始的,以及具体提交了哪些内容,也就是前面的问题 1。

在我不确定前人什么时候离职、之前有没有其他人接触过这部分功能、不知道前人使用的是哪个开发分支的情况下,我是通过对比 uat 与 release 分支的方式,幸运的是,对比结果很容易就出来了,release 对应的 commit 切出分支,一个个 cherry-pick 就解决了。

在编写这篇文章时,我尝试用 graph 完成这个对比过程,很遗憾, graph 结果比第二张图还要夸张,就不放图了。

看过开篇提到的文章以后,我开始留意当前项目的提交历史,只能说很符合"提交历史复杂"的情况。

后续我开始留意 graph 结果,但请原谅我现在没办法立即贴上一张相对比较完美的 graph 结果,只能用下面这部分了。

可以看到我线性完成了很多功能(箭头顺序),并且每隔一段时间就会提交合并发布测试版(图中 \ 的地方,以及每一个 Accept Merge Request)。

单看这张图,就能直观的看出我是从什么版本开始开发,提交了哪些 commit,以及通过 Commit 次数和 Merge 分叉可以看出我的开发习惯(小步提交,及时 Merge)。

不难得出结论:合理的分支管理,能比 rebase 提供更多的有效信息。

对于一般公司的项目而言,更需要看重这些点,一旦需要追根溯源的时候,很容易通过 graph 记录,找到关键的节点。

对于 Vue 这样的开源项目来说,谁在什么时候开发了哪些功能不是重点,保持一个干净清爽的提交历史反而是更需要在意。比如 vue-core 项目的 graph 记录是这样的,第一眼看过去是很乱的。也许正是出于这个原因,所以后面采用了 rebase 的方式吧。

回到我们的问题:如何保持干净的 graph 结果?

每次开发都是从主分支切出新的分支进行开发,

  • 如果有测试环境,则每次合并至测试分支进行单独发布
  • 如果没有测试环境,则直接合并回主分支

甚至如果是单人开发,最后可以只用一个分支开发。

但是一般公司的项目至少会区分正式和测试两个环境,并且会有多人同时开发不同功能的情况,所以每次新切分支,上线前合并至线上分支的方式,是最能保持 graph 结果干净的管理模式。

而不是首次进项目创建一个分支以后,一直使用这一个分支进行开发,不仅 graph 结果复杂,也更容易出现代码冲突的场景。

哪怕这个流程已经足够简单了,但是对于 Vue 这样的开源项目,要求并保证每个贡献者都能按照这样的方式参与贡献是很不实际的一个选择,不如干干净净的。

结尾碎碎念

我不太喜欢花时间研究技术细节,因为大多数技术需要用到的场景很少,比如 git 的 graph 结果就是属于 20% 场景中的那部分。

但碍于绝大多数内容都在解释技术,很少有人从这样的场景角度去讨论使用场景,我决定写下这篇内容。

写文章真得是一件很累人的事情,总共这些信息摄入、结果验证,在我实际生活中占据不到半小时,这还是保守说法显得谦逊。

实际上可能总共就一两分钟,因为分支管理模式已经成为习惯了,只需要在实际开发过程中稍微切换一下,再加以验证结果就行。

但整篇文章从创建到写完,中间的各种细节加起来一共花了我三个多小时,希望能帮人换换视角吧。

相关推荐
叹一曲当时只道是寻常1 分钟前
Softhub软件下载站实战开发(十六):仪表盘前端设计与实现
前端·golang
超级土豆粉7 分钟前
npm 包 scheduler 介绍
前端·npm·node.js
bug爱好者7 分钟前
原生小程序如何实现跨页面传值
前端·javascript
随笔记10 分钟前
uniapp开发的小程序输入框在ios自动填充密码,如何欺骗苹果手机不让自动填充
前端·ios·app
bug爱好者16 分钟前
原生微信小程序最实用的工具函数合集
前端·javascript
3Katrina20 分钟前
JS事件机制详解(2)--- 委托机制、事件应用
前端·javascript·面试
Allen Bright25 分钟前
【CSS-15】深入理解CSS transition-duration:掌握过渡动画的时长控制
前端·css
张鑫旭26 分钟前
40岁老前端2025年上半年都学了什么?
前端
前端wchen27 分钟前
Vue 3 组件通信实战系列(一)父子组件通信的标准姿势:Props 与 Emit(含实战与进阶技巧)
前端·vue.js
Jerry Lau40 分钟前
go go go 出发咯 - go web开发入门系列(一) helloworld
开发语言·前端·golang