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

永远 rebase

sub-branches rebase on master, master merges sub-branches. 差不多是这样。

rebase +1

远程分支咋办?假如我有两个远程分支:master 、dev 。master 作为主分支,dev 作为开发分支。在项目迭代中,master 分支可能会存在 bug 修复,这个 bug 修复完成后需要同步到 dev 分支。一个迭代结束后,dev 的代码需要合并到 master 分支。我现在对个人分支合并到远程分支采用的 rebase,两个远程分支的合并采用的是 merge 。现在纠结的是两个远程分支的合并,每次合并都会产生不必要的提交,如果 bug 修复次数很多,那分支就会显得很乱😂😂😂。

教程都是用 merge 的多,一般都写不能真实的反映“历史”,小项目随意。大项目的话 2 楼基本上是正解。

“历史是什么” :

merge 稳一点,虽然会多一次提交记录

同意 4 楼说的,自己有把握的两个分支用 rebase,对不熟悉的分支 merge

merge,因为 no-ff 后只带一条记录。
因为团队开发需要用一些 git 的 flow (任意),为了追述 MR 是 hotfix 还是 feature 、release 等,用 merge no-ff 。

个人开发和本地分支喜欢 rebase,涉及到远程多人开发的分支只能用 merge,rebase 会弄的协同困难。

一律采用 squash merge 。github PR 也只用 /允许 squash merge 。
太多的小的提交,或反复的修改后提交,会使历史记录很乱。合并成一个 squash merge,既简洁又能看到一个 feature merge commit 里的多次提交的记录。
尽可能在 Github 上面先创建 PR,再合并。

同意 2L

统一比选择难

说一次去年的线上的故障。
开发了 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 都没了)。

要 rebase 就全部 rebase,要 merge 就全部 merge

有人 rebase 有人 merge 才最伤

老大一开始就跟我说过要用 rebse 或者 git reset --soft,但是我没在乎,毕竟我看 廖雪峰阮一峰的教程都没说 rebase.. 出了故障才学会我可真是亏了。

rebase 能解决这个问题吗?只 reset 15 号的可行吗?

你这种情况可以因检出一个分支,然后把主分支回退,再把 17 好发布的那些更改用 cherry-pick 命令放到主分支上,发布主分支

如果 master 已经这样了就无解了,最后解决方法就是一直故障,然后立刻定位问题解决,足足持续了 2 个小时才解决。。。

我们当时发布工具比较垃圾,就是选择 master 上的 一个一个 commit 然后打包发布。 然后也没有打 tag 的习惯,如果有 tag 应该也是可以解决的,不确定。

#18 无解是因为乱用 merge 命令,应该所有的更改都从主分支检出新分支,每一个 commit 尽可能小,合并的时候照二楼说的先 rebase,再到主分支 merge(为什么要这样做?你可以在你的项目主分支和开源项目分别执行下 git log —oneline —graph 命令看看区别),最后发布完成给最新 commit 打版本 tag,更甚至建一个 changelog.md 文件记录每次版本号和更改内容

楼里不知道 git flow 流程的赶紧去学习一下

rebase,上一次 merge 应该是 2016 年了。

rebase +1,rebase 的提交记录更好理解

www.mail-archive.com/[email protected]/msg39091.html

reabse+1

Merge 多出来的提交不是无意义,它记录了一种合并关系,显式表明这个提交来自于一个分支。这也是 Git 分支的一大特性:Git 历史非一条直线,其中可以有岔路,或者说一个提交可以有多个父节点。Rebase 后再 fast forward 合并的话丢失了这种关系记录。

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

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

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

nvie.com/posts/a-successful-git-branching-model/
www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
git-scm.com/book/zh/v2

merge

#10 +1 “一律采用 squash merge 。 ”
大型项目也要考虑 merge,为了追溯真实提交情况。

假设一个场景,你想找 6 个月前有人做的 feature1 涉及到哪些文件改动。

  1. rebase,你需要翻很多 commit 。比如 03/14 ~ 03/18 之间 user1 的所有 commit
  2. merge 的话你只要看一个 commit 。或者是 gitlab/github 上的 mr

我会先把本地分支 rebase 到远程 master 分支,然后再发起 merge request 合并到 master 分支。

merge;

  1. 超大项目合码时 rebase 处理冲突的难度你不想体验
  2. rebase 本身破坏了真实 commit 之间的逻辑顺序,粗暴的采用时间序其实非常蛋疼不直观

历史可以分叉是很恶的(双关:恶心且邪恶),但是因为爱因斯坦的关系不得不这么干。
凡 git 的事想一想用邮件怎么玩。

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

rebase 只在本地操作.

要不然很容易出问题.

2 楼的原则,加上 29 楼的具体实践说明,其实就是现在业界的标准玩法了。

工作那么多年的老油条了,各种 git flow 用下来,你最后总会发现必须得有 MR/PR 这个环节。这一类的 flow 才是更适合实际工作的 flow,而且所谓的 rebase 和 merge 也没法完全割裂开来单独用的。

master 比较稳定,更改较少的情况下,可以试试 cherry-pick 到 dev 分支。

特性分支 rebase 主干分支,然后再 merge 主干

话说起来顾名思义都行
变基,是指『变更』提交的『基础』
合并,是指将两个分支的提交进行『合并』(我知道,循环定义了)
也就是说,当且仅当这些提交是那些提交的『基础』时才进行变基。

你可以 diff ...
据说 fossil 合并后分支会变成 tag 永久保留,你也可以轻易地看到这个 branch 的历史。
squash 似乎不太适合在时域上进行 bisect,这对于大型项目还是挺重要的。

微软这边的 git 项目合并 master 基本都 squash merge,保证 master 的记录是单线性的。至于合并前自己的分支爱怎么搞就怎么搞,PR 跑 build 和 test 能通过,不 conflict 就好了。

不过我基本都只用 rebase,只有极少数情况出现多人同时开发具有互相依赖性的东西才会 merge 。很多 mono repo 每天 master 要进几十上百个不同 team 做的东西,用 merge 会很糟心。

rebase 没有按时间排序,merge 按时间顺序

#13 有两点疑问

  1. 22 号的 bug,不能从 master 创建新分支修复吗?
  2. 22 号的 bug 是 15 号提交的,那在 15 号的分支上修复也可以吧?先 pull master 到 15 号分支,处理冲突,修复完 bug 再 merge 到 master

我是个人和团队也用 merge 。分支线是会显得乱,但是不会出问题。

为什么 ‘包含完整的提交历史记录’ 会是一个需要避免的问题? 你瞎搞之后丢记录了怎么办 行为艺术吗

我选择 rebase

一般原则 public branch 用 merge,private branch 用 rebase 。

但是用 rebase 后要怎么 revert/reset 这次 rebase 呢?

我用的 merge,因为场景并不复杂,merge 和 rebase 都差不太多。

两个远程分支用 rebase 反而会乱吧

我只知道当需要撤回合并操作的时候,特别是公司项目上线计划临时调整的时候,直接 revert merge 提交很方便,不知道 rebase 怎么处理,懂的可以分享一下。

个人觉得版本控制系统的核心价值是体现整个版本变更历史,使用 rebase 仅会留下提交,无法区分是合并操作还是提交操作,所以“merge 产生无意义的提交”可能连 Linus 本人都不赞同。

整理分支是另一码事,希望整理得多么“干净”取决于能接受丢失多少历史信息,就好比服务器每周五备份,周三的时候你想恢复周二的数据,对不起没有,只能恢复到上周五。

你可以不在一个分支上体现任何 merge 记录,但也同时放弃了利用 merge 记录做审计或操作的能力,如果综合权衡是适合当前项目管理的方案的话,也是可以的。

线上发版会基于 tag 来构建

#47 不是每个 feature 一个 tag 吧,总会有合并操作的,打 tag 也是在合并完的分支上打 tag 。

我的意思是用 tag 作为版本号,这样就隔离了版本与代码合并操作过程

"merge 会产生一个无意义的提交" rebase 只是重播一遍,并不能解决无意义提交 的问题

并入更改用 merge,散播更改用 rebase 。

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

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

↑ 大多数公司代码的 timeline 是这样的。

感觉 2#的说法是合理的
本地分支同步 dev 用 rebase 保证本地的提交和顺序是清楚的
dev 同步 hotfix 、合入 feature 用 merge,保证操作的节点顺序以及方便 revert

我之前也比较喜欢用 squash merge,后来发现对于一个开发周期比较长的 feature,squash 到一起太容易丢 context 了。比如某几行修改是为了解决需求中的某个 bug,这时候提单个 commit,后续再看知道这个修改是干啥的;几千行的大修改,squash 到一个 commit 里,过几个月完全不知道这几行为啥要这么改……

以前很少用 rebase,完全不知道有用 rebase 把提交历史记录整理一遍的说法,可能是每个人的习惯不同吧

不用 rebase 的好处是,每一个操作细节都清清楚楚的留下记录,提交记录重要的不是“简洁”、“好看”,而是可追溯性

merge 简单易懂,rebase 不是人人都会,另外 merge 包含完整的提交历史,这样出问题容易找人负责,谁改的谁写的一目了然,99%的公司都是 merge

#49 这里讨论的问题是 merge 和 rebase,发生在打 tag 之前,似乎和 tag 关系不大。

可以从 master 创建新的分支修复呀,也就是这么做的,但是定位问题然后修复上线用了 2 个小时。此时线上已经故障了两个小时了呀。 是比较严重的故障,需要立刻版本回退那种。

由 2 楼的说法想到,这其实是一个思维模式问题,如果一直在主分支操作所有合并行为,包括解决冲突,那么可以一直用 merge 。如果操作主分支的人不希望承担解决冲突的责任,那么子分支用 rebase 的必要就来了。当然,直接把主分支往子分支合并也能解决一些问题,但这显然让分支流程变乱了,不管你坚持 merge 还是 rebase,都不推荐这么干。

这种直接把 9.22 号的 pr revert 掉不好使么

主干开发+小批量提交+squash merge

应该是好使的,但是都没用过呀。。。当时 gitlab 的 revert 试了一下没成功就没试了,主要是不熟悉。

看过一篇老外教程说:永远不要在公共分支 rebase

此 rebase 非彼 rebase

单人分支 rebase 公共分支;单人分支 merge into 公共分支

仔细想了一想,之前的长回复应该补充一点。主要是楼主的问题应该从两个角度回答,我只回答了其中一部分。

楼主问题提到“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 一下。如当前分支和合并前主分支时间隔的太久,直接合并后分支线头尾跨得太远,不好查看。或者是不想多个分支线合并后有交叉。(敲完才发现楼上 回复,参考其“理想的 timeline”一图,非常漂亮)
  • 或者分支合并前不做 rebase,降低工作流使用门槛。如 Github PR 合并方式:直接给你在网页上整一个按钮,点击后 merge --no-ff.

不管哪种情况,推荐 merge --no-ff 优先。一些特殊情况,如分支上 只有 1 个新提交,可以 --ff 快速推进。

都 2021 年了,竟然还有人在推荐 git flow

从 16 年到现在 一直 merge --no-ff 不会转为 rebase

我有时候先 interactive rebase,压掉不必要的 commit,最后 merge

“次数多了分支线就很乱,怎么办”,如果关注的问题是这个,那么就你需要先列出你操作的分支名,然后会走向两种解决方式:
1 增加分支,增加一个分支作为 ‘缓存分支’ ,即改变分支策略
2 分支不变,改变 merge 命令

如果是办法 1,那么解决办法会非常简单,你会把 ‘造成分支错乱’ 归因为提交错了分支线。

如果你需要前后三次提交(或无数次提交),将会导致分支错乱的原因是 你只有 2 个分支,自己的 branch -> main branch,这样 main branch 会很快被写入;将会导致分支清晰的原因是 你现在有 3 个分支,自己的 branch -> develop 分支 -> main branch 。这样因为多了一个分支,所以 develop 分支仍然会很乱( develop 分支是 ‘缓存分支’,等于为 main branch 作了缓存),故而 main branch 会保持清晰。

www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow 图示
www.atlassian.com/blog/git/written-unwritten-guide-pull-requests
如果是办法 2,那么,哈哈 ... 这根本不是 merge 命令的问题 ... 你需要重新归因,而不是拿出某个命令来就用。实际上它是 pull request 策略的问题,而不是 merge 策略的问题。它是人员责任划分的问题,不是 git 的问题:是责任划分来指导 pull request 策略和具体 git 操作,而不是相反。相反只有颠覆,而且是盲从般的颠覆

git flow 是严格了点,实践中未必要严格遵守使用,但其中的分支使用思想经久不衰。鉴于楼主目前遵循的分支使用逻辑是分支合并结果要保留单一直线,我觉的推荐 git flow 正合适。

我看到过很多人把 git 的 commit 当成草稿箱,提交很多无意义的改动,每次 commit 基本在修改错别字或者反复 rollback 。针对这种情况,在 PR merge 到 master 之前,最好 squash 。否则如果日后做 trouble shooting 的时候,其中的每条 commit 就是对别人的折磨。

项目比较复杂,多人同时维护。用 rebase 简直就是噩梦。

我们用这个导致了半年的 commit 丢失,负责人不会用,瞎用,,,,

永远 merge

这其实本质和团队质量有关:

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

如果有个总记不住拉代码,互相甩锅,功能拆分不完整,提代码就挂掉别人的功能,代码合进来也不知道对不对的团队,那只能先 merge…

这个可以灵活应对吧,能用 rebase 的情况下尽量 rebase 了

请求科代表做总结

开发分支 rebase,合并 master fast forward merge

我想了一下我们现在的流程
各个 feature 会从 master 拉取分支,比如 featureA 、featureB 、featureC
如果开发的过程中,master 上发现一个 bug,则会基于 master 拉取一个分支 hotfixA,改好合并到 master 自动发布
发布完触发各个 feature 分支会自动 merge master 。
假如这时候 featureA 测试完成,要发布的话,是 merge 到 master,发到线上发现有 bug,直接回滚上一个 merge 节点不就好了...
git reset HEAD^ --soft 然后再修改代码提交先回滚?

这样可以解决吗

rebase 容易翻车,操作不熟