rebase 还是 merge?

发布于 2021-09-18 11:01:05

大家在项目里合并代码是用 rebase 还是 merge ? 两个远程分支合并,用 merge 会产生一个无意义的提交,次数多了分支线就很乱。

Questioner
wiirhan
Viewed
22
76 个回答
dcalsky 2021-09-18 11:01:31
永远 rebase
dcalsky  21 2021-09-18 11:02:43
@dcalsky sub-branches rebase on master, master merges sub-branches. 差不多是这样。
lingxi27 2021-09-18 11:08:34
rebase +1
wiirhan 2021-09-18 11:19:20
@dcalsky 远程分支咋办?假如我有两个远程分支:master 、dev 。master 作为主分支,dev 作为开发分支。在项目迭代中,master 分支可能会存在 bug 修复,这个 bug 修复完成后需要同步到 dev 分支。一个迭代结束后,dev 的代码需要合并到 master 分支。我现在对个人分支合并到远程分支采用的 rebase,两个远程分支的合并采用的是 merge 。现在纠结的是两个远程分支的合并,每次合并都会产生不必要的提交,如果 bug 修复次数很多,那分支就会显得很乱😂😂😂。
bjfane 2021-09-18 11:19:33
教程都是用 merge 的多,一般都写不能真实的反映“历史”,小项目随意。大项目的话 2 楼基本上是正解。


“历史是什么” :
<img src = 'https://i.bmp.ovh/imgs/2021/09/13b50db1bb1ef78f.png' />
zjsxwc  1 2021-09-18 11:25:55
merge 稳一点,虽然会多一次提交记录
zjsxwc 2021-09-18 11:27:09
同意 4 楼说的,自己有把握的两个分支用 rebase,对不熟悉的分支 merge
konakona 2021-09-18 11:42:15
merge,因为 no-ff 后只带一条记录。
因为团队开发需要用一些 git 的 flow (任意),为了追述 MR 是 hotfix 还是 feature 、release 等,用 merge no-ff 。
peterswan  1 2021-09-18 11:46:22
个人开发和本地分支喜欢 rebase,涉及到远程多人开发的分支只能用 merge,rebase 会弄的协同困难。
Pipecraft  4 2021-09-18 11:48:39
一律采用 squash merge 。github PR 也只用 /允许 squash merge 。
太多的小的提交,或反复的修改后提交,会使历史记录很乱。合并成一个 squash merge,既简洁又能看到一个 feature merge commit 里的多次提交的记录。
尽可能在 Github 上面先创建 PR,再合并。
monkeyWie 2021-09-18 11:53:06
同意 2L
wellsc 2021-09-18 12:16:28
统一比选择难
weiwenhao 2021-09-18 12:18:53
说一次去年的线上的故障。
开发了 a 功能在 9 月 15 号(cimmit 在 9 月 15 号,测试了一周,9 月 22 号上线(合到 master)

修复了 b bug 在 9 月 17 号,当天上线。(合到 master)

9 月 22 号发版遇到 bug 需要回退。因此新的故障是 15 号提交的,所以此时需要回退到 15 号之前的 master 分支才能修复, 所以导致 17 号的修复分支没了( 15 号到 22 号其实不止 17 号一个功能没了,7,8 个 feature/或者 fix 都没了)。
dilu 2021-09-18 12:21:53
要 rebase 就全部 rebase,要 merge 就全部 merge

有人 rebase 有人 merge 才最伤
weiwenhao 2021-09-18 12:23:47
老大一开始就跟我说过要用 rebse 或者 git reset --soft,但是我没在乎,毕竟我看 廖雪峰阮一峰的教程都没说 rebase.. 出了故障才学会我可真是亏了。
plko345 2021-09-18 12:29:10
@weiwenhao rebase 能解决这个问题吗?只 reset 15 号的可行吗?
karott7 2021-09-18 12:34:05
@weiwenhao 你这种情况可以因检出一个分支,然后把主分支回退,再把 17 好发布的那些更改用 cherry-pick 命令放到主分支上,发布主分支
weiwenhao 2021-09-18 12:34:54
如果 master 已经这样了就无解了,最后解决方法就是一直故障,然后立刻定位问题解决,足足持续了 2 个小时才解决。。。
weiwenhao 2021-09-18 12:36:19
@karott7 我们当时发布工具比较垃圾,就是选择 master 上的 一个一个 commit 然后打包发布。 然后也没有打 tag 的习惯,如果有 tag 应该也是可以解决的,不确定。
karott7 2021-09-18 12:43:04
@weiwenhao #18 无解是因为乱用 merge 命令,应该所有的更改都从主分支检出新分支,每一个 commit 尽可能小,合并的时候照二楼说的先 rebase,再到主分支 merge(为什么要这样做?你可以在你的项目主分支和开源项目分别执行下 git log —oneline —graph 命令看看区别),最后发布完成给最新 commit 打版本 tag,更甚至建一个 changelog.md 文件记录每次版本号和更改内容
karott7  1 2021-09-18 12:43:50
楼里不知道 git flow 流程的赶紧去学习一下
xlsepiphone 2021-09-18 12:44:52
rebase,上一次 merge 应该是 2016 年了。
daolanfler 2021-09-18 13:34:16
rebase +1,rebase 的提交记录更好理解
icylogic 2021-09-18 13:55:45
https://www.mail-archive.com/dri-devel@lists.sourceforge.net/msg39091.html
bingyiyu 2021-09-18 14:01:30
reabse+1
Kobayashi  28 2021-09-18 14:26:36
Merge 多出来的提交不是无意义,它记录了一种合并关系,显式表明这个提交来自于一个分支。这也是 Git 分支的一大特性:Git 历史非一条直线,其中可以有岔路,或者说一个提交可以有多个父节点。Rebase 后再 fast forward 合并的话丢失了这种关系记录。

一个好的分支名直接解释了这个分支在做什么,如修复某个 bug 、新增了某个功能。而分支内每个提交则记录了实现的步骤。从这个角度来看,分支也可以视作一个大的提交。

Merge 而不是 rebase + fast forward 。更不要随意 merge --squash,而是根据情况使用。保留多个提交的实现步骤,如果这个功能出了问题,方便以后细分,定位问题。

楼上很多人对 Git 的理解水平真的不大行,只是在把 Git 当 SVN 来用……
建议了解了解 git-flow,理解一下分支。多翻翻 pro git book.

https://nvie.com/posts/a-successful-git-branching-model/
https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
https://git-scm.com/book/zh/v2
yalin 2021-09-18 14:27:46
merge
vjnjc  1 2021-09-18 14:39:16
#10 +1 “一律采用 squash merge 。 ”
大型项目也要考虑 merge,为了追溯真实提交情况。

假设一个场景,你想找 6 个月前有人做的 feature1 涉及到哪些文件改动。
1. rebase,你需要翻很多 commit 。比如 03/14 ~ 03/18 之间 user1 的所有 commit
2. merge 的话你只要看一个 commit 。或者是 gitlab/github 上的 mr
bingyiyu 2021-09-18 14:47:25
我会先把本地分支 rebase 到远程 master 分支,然后再发起 merge request 合并到 master 分支。
acidsweet 2021-09-18 15:03:10
merge;
1. 超大项目合码时 rebase 处理冲突的难度你不想体验
2. rebase 本身破坏了真实 commit 之间的逻辑顺序,粗暴的采用时间序其实非常蛋疼不直观
2i2Re2PLMaDnghL  1 2021-09-18 15:16:41
历史可以分叉是很恶的(双关:恶心且邪恶),但是因为爱因斯坦的关系不得不这么干。
凡 git 的事想一想用邮件怎么玩。

1. 什么时候 rebase ?
你以别人的软件的 v1.0 为基础开始写一个 feature,还没写完的时候 v1.1 发布了,你把新代码 pull 下来并把你的修改全都 rebase 到 v1.1 上去。

2. 什么时候 merge ?
别人为你的软件写了一个 feature,然后 send-email 给了你。你拿到了他的 patch 并 merge 进你的代码。
chaleaoch 2021-09-18 15:19:35
rebase 只在本地操作.

要不然很容易出问题.
OliveGlaze  1 2021-09-18 15:20:29
2 楼的原则,加上 29 楼的具体实践说明,其实就是现在业界的标准玩法了。

工作那么多年的老油条了,各种 git flow 用下来,你最后总会发现必须得有 MR/PR 这个环节。这一类的 flow 才是更适合实际工作的 flow,而且所谓的 rebase 和 merge 也没法完全割裂开来单独用的。
kxuanobj 2021-09-18 15:23:52
@wiirhan master 比较稳定,更改较少的情况下,可以试试 cherry-pick 到 dev 分支。
index90 2021-09-18 15:24:25
特性分支 rebase 主干分支,然后再 merge 主干
2i2Re2PLMaDnghL 2021-09-18 15:26:18
@2i2Re2PLMaDnghL 话说起来顾名思义都行
变基,是指『变更』提交的『基础』
合并,是指将两个分支的提交进行『合并』(我知道,循环定义了)
也就是说,当且仅当这些提交是那些提交的『基础』时才进行变基。

@vjnjc 你可以 diff <merge 的主支>...<merge 的分支>
据说 fossil 合并后分支会变成 tag 永久保留,你也可以轻易地看到这个 branch 的历史。
squash 似乎不太适合在时域上进行 bisect,这对于大型项目还是挺重要的。
maplerecall 2021-09-18 15:31:05
微软这边的 git 项目合并 master 基本都 squash merge,保证 master 的记录是单线性的。至于合并前自己的分支爱怎么搞就怎么搞,PR 跑 build 和 test 能通过,不 conflict 就好了。

不过我基本都只用 rebase,只有极少数情况出现多人同时开发具有互相依赖性的东西才会 merge 。很多 mono repo 每天 master 要进几十上百个不同 team 做的东西,用 merge 会很糟心。
MrGoooo 2021-09-18 15:34:51
rebase 没有按时间排序,merge 按时间顺序
oneisall8955  1 2021-09-18 15:36:26
@weiwenhao #13 有两点疑问
1. 22 号的 bug,不能从 master 创建新分支修复吗?
2. 22 号的 bug 是 15 号提交的,那在 15 号的分支上修复也可以吧?先 pull master 到 15 号分支,处理冲突,修复完 bug 再 merge 到 master
Felldeadbird 2021-09-18 16:08:19
我是个人和团队也用 merge 。分支线是会显得乱,但是不会出问题。
charlie21 2021-09-18 16:08:54
为什么 ‘包含完整的提交历史记录’ 会是一个需要避免的问题? 你瞎搞之后丢记录了怎么办 行为艺术吗
mdyy777 2021-09-18 16:22:32
我选择 rebase
MinonHeart 2021-09-18 16:25:17
一般原则 public branch 用 merge,private branch 用 rebase 。

但是用 rebase 后要怎么 revert/reset 这次 rebase 呢?
cco 2021-09-18 16:30:49
我用的 merge,因为场景并不复杂,merge 和 rebase 都差不太多。
dyxLike 2021-09-18 16:40:27
两个远程分支用 rebase 反而会乱吧
securityCoding 2021-09-18 16:54:11
@libook 线上发版会基于 tag 来构建
libook 2021-09-18 16:59:25
@securityCoding #47 不是每个 feature 一个 tag 吧,总会有合并操作的,打 tag 也是在合并完的分支上打 tag 。
securityCoding 2021-09-18 17:02:10
@libook 我的意思是用 tag 作为版本号,这样就隔离了版本与代码合并操作过程
leafre 2021-09-18 17:06:40
"merge 会产生一个无意义的提交" rebase 只是重播一遍,并不能解决无意义提交 的问题
msg7086  1 2021-09-18 17:07:11
并入更改用 merge,散播更改用 rebase 。

人人都懂 Git 的小公司 merge 的时候可以用普通的 merge commit,不是人人都懂 Git 的公司(特别是参差不齐的大公司)或者合并非常频繁的大公司,可以善用 squash merge 。(我司一般都是 squash merge,因为合并太频繁了,经常 Rebase 不现实。)



↑ 理想的 timeline 应该是类似这样的。



↑ 大多数公司代码的 timeline 是这样的。
reactna1ve  1 2021-09-18 17:12:36
感觉 2#的说法是合理的
本地分支同步 dev 用 rebase 保证本地的提交和顺序是清楚的
dev 同步 hotfix 、合入 feature 用 merge,保证操作的节点顺序以及方便 revert

我之前也比较喜欢用 squash merge,后来发现对于一个开发周期比较长的 feature,squash 到一起太容易丢 context 了。比如某几行修改是为了解决需求中的某个 bug,这时候提单个 commit,后续再看知道这个修改是干啥的;几千行的大修改,squash 到一个 commit 里,过几个月完全不知道这几行为啥要这么改……
Biwood 2021-09-18 17:22:19
以前很少用 rebase,完全不知道有用 rebase 把提交历史记录整理一遍的说法,可能是每个人的习惯不同吧

不用 rebase 的好处是,每一个操作细节都清清楚楚的留下记录,提交记录重要的不是“简洁”、“好看”,而是可追溯性
wangbenjun5 2021-09-18 17:25:03
merge 简单易懂,rebase 不是人人都会,另外 merge 包含完整的提交历史,这样出问题容易找人负责,谁改的谁写的一目了然,99%的公司都是 merge
libook 2021-09-18 17:27:42
@securityCoding #49 这里讨论的问题是 merge 和 rebase,发生在打 tag 之前,似乎和 tag 关系不大。
weiwenhao 2021-09-18 17:28:38
@oneisall8955 可以从 master 创建新的分支修复呀,也就是这么做的,但是定位问题然后修复上线用了 2 个小时。此时线上已经故障了两个小时了呀。 是比较严重的故障,需要立刻版本回退那种。
Biwood 2021-09-18 17:29:07
由 2 楼的说法想到,这其实是一个思维模式问题,如果一直在主分支操作所有合并行为,包括解决冲突,那么可以一直用 merge 。如果操作主分支的人不希望承担解决冲突的责任,那么子分支用 rebase 的必要就来了。当然,直接把主分支往子分支合并也能解决一些问题,但这显然让分支流程变乱了,不管你坚持 merge 还是 rebase,都不推荐这么干。
wingtao 2021-09-18 17:29:21
@weiwenhao 这种直接把 9.22 号的 pr revert 掉不好使么
penghong 2021-09-18 17:33:22
主干开发+小批量提交+squash merge
weiwenhao 2021-09-18 17:34:08
@wingtao 应该是好使的,但是都没用过呀。。。当时 gitlab 的 revert 试了一下没成功就没试了,主要是不熟悉。
zhw2590582 2021-09-18 17:34:10
看过一篇老外教程说:永远不要在公共分支 rebase
oakland 2021-09-18 17:47:49
@zhw2590582 此 rebase 非彼 rebase
xiubin 2021-09-18 18:01:59
单人分支 rebase 公共分支;单人分支 merge into 公共分支
Kobayashi 2021-09-18 18:02:56
@Kobayashi 仔细想了一想,之前的长回复应该补充一点。主要是楼主的问题应该从两个角度回答,我只回答了其中一部分。

楼主问题提到“2 个远程分支合并”,是指原来两个分支都和主分支在一条线上,如果一个分支先合并,另一个与更新后的主分支分叉,合并时发生了 merge --no-ff. 按照楼主的理解,分支合并应该是

1. rebase 到主分支,保证分之合并前没有分叉
2. merge --ff,保证分支合并后没有额外 merge commit.

其实这就是 非 Git 版本控制工具 的分支使用逻辑:分支历史必须是一条线,分支起点必须是主分支最新提交。如 SVN.

这个问题实际要分为 2 个部分:

1. 合并分支前是否要 rebase?
2. merge 是否使用 --ff 快速推进,要不要不产生额外的“合并提价”?

我之前的回复其实只针对了 2,介绍了 Git 分支非一条支线的特性,推荐了 --no-ff 合并。

如果只针对 1,可以根据情况:

- 如果向主分支合并时发生冲突,可以合并时解决冲突。或者 rebase 解决冲突,再合并。
- 或者你想让提交历史图( timeline )更清晰一点,也可以先做 rebase 一下。如当前分支和合并前主分支时间隔的太久,直接合并后分支线头尾跨得太远,不好查看。或者是不想多个分支线合并后有交叉。(敲完才发现楼上 @msg7086 回复,参考其“理想的 timeline”一图,非常漂亮)
- 或者分支合并前不做 rebase,降低工作流使用门槛。如 Github PR 合并方式:直接给你在网页上整一个按钮,点击后 merge --no-ff.

不管哪种情况,推荐 merge --no-ff 优先。一些特殊情况,如分支上 只有 1 个新提交,可以 --ff 快速推进。
ospider  1 2021-09-18 18:22:38
都 2021 年了,竟然还有人在推荐 git flow
supereasy 2021-09-18 18:31:14
从 16 年到现在 一直 merge --no-ff 不会转为 rebase
celeron533 2021-09-18 20:11:53
我有时候先 interactive rebase,压掉不必要的 commit,最后 merge
Kobayashi 2021-09-18 20:36:14
@ospider git flow 是严格了点,实践中未必要严格遵守使用,但其中的分支使用思想经久不衰。鉴于楼主目前遵循的分支使用逻辑是分支合并结果要保留单一直线,我觉的推荐 git flow 正合适。
kaneg 2021-09-18 20:38:06
我看到过很多人把 git 的 commit 当成草稿箱,提交很多无意义的改动,每次 commit 基本在修改错别字或者反复 rollback 。针对这种情况,在 PR merge 到 master 之前,最好 squash 。否则如果日后做 trouble shooting 的时候,其中的每条 commit 就是对别人的折磨。
xuanbg 2021-09-18 20:51:35
项目比较复杂,多人同时维护。用 rebase 简直就是噩梦。
wukongkong 2021-09-18 21:08:36
@Pipecraft 我们用这个导致了半年的 commit 丢失,负责人不会用,瞎用,,,,
beichenhpy 2021-09-18 21:11:37
永远 merge
namelosw 2021-09-18 21:53:18
这其实本质和团队质量有关:

其实最理想的情况就是努力培养团队,素质都不错,需求拆分合理,自动化测试和持续集成也做得很好,所有人在同一个分支疯狂 rebase 效果是最好的。

如果有个总记不住拉代码,互相甩锅,功能拆分不完整,提代码就挂掉别人的功能,代码合进来也不知道对不对的团队,那只能先 merge…
devfeng 2021-09-19 03:20:40
这个可以灵活应对吧,能用 rebase 的情况下尽量 rebase 了
IvanLi127 2021-09-19 13:04:49
请求科代表做总结
24bit 2021-09-19 19:41:08
开发分支 rebase,合并 master fast forward merge
Jsonz 2021-09-19 22:33:04
@weiwenhao 我想了一下我们现在的流程
各个 feature 会从 master 拉取分支,比如 featureA 、featureB 、featureC
如果开发的过程中,master 上发现一个 bug,则会基于 master 拉取一个分支 hotfixA,改好合并到 master 自动发布
发布完触发各个 feature 分支会自动 merge master 。
假如这时候 featureA 测试完成,要发布的话,是 merge 到 master,发到线上发现有 bug,直接回滚上一个 merge 节点不就好了...
git reset HEAD^ --soft 然后再修改代码提交先回滚?

这样可以解决吗