关于 git 工作流的问题,从某个节点分叉出去的两个分支,如何高效合并? merge ? rebase ? cherry-pick ?

2023-12-05 16:33:06 +08:00
 Albertcord

当前问题:
项目从分支 develop 分叉一个项目分支 master 去做项目交付,然后后面开始两个分支的开发,时不时 master 修复 bug ,develop 加特性(两者加的文件有交集)

提给 master 的 mr 偶尔也会 cherry-pick 给 develop ,但是偶尔着急的没这么做,现在两个分支大概有四十五个 commit 的差异(老脸一红,打的补丁缺失多,有些是需求变更,项目着急要),尝试了下 develop rebase master ,发现冲突太多了

想问问有经验的大佬们,这种情况怎么做?

好像最简单的是 git merge ,但是对 merge 有疑虑,担心丢失代码和 git 分支相交太多

还有一种想到的,就是从当时分叉出去的节点的 hashxxxx ,计算哪些没同步提到 develop ,再对这些 commit 做逐步地 cherry-pick 然后提 Merge Request 合并

但模拟了下节点的合并,好像这样最后 develop merge 到 master 也会有冲突,因为 master 多出的节点 A',会放在 develop 多出的节点 B'上,而 rebase 其实是把 B'放在 A'上,保持一条线
也就是现在想到的方法有三种:

  1. git rebase (develop rebase master)
  2. git merge (develop merge master)
  3. diff 出 master 比 develop 多的 commit ,逐个 cherry-pick 到 develop

还有其他高效方法吗?

2214 次点击
所在节点    git
20 条回复
maocat
2023-12-05 16:36:39 +08:00
相关人员坐一起,然后 merge ,再然后挨个变动问,要不要
chenliangngng
2023-12-05 16:46:22 +08:00
分歧少就 rebase ,分歧稍微多一点(超过 3 个)就 merge
huangcjmail
2023-12-05 16:54:50 +08:00
我们有个项目有两个巨长的分支 master 和 dev ,就是用的 cherry-pick 合并的。开始还好,后面渐渐的就有问题了,会发现有个非常古早的代码没有合并进去。而且长时间没有合并的两个分支,在 idea 里查看分支图的时候也特别不方便。不知道是不是我们 cherry-pick 的姿势有问题。
leemars
2023-12-05 16:54:51 +08:00
你现在这个模式大致上就是 trunk 开发 release 分支发布的模式,trunk = develop ,release 分支 = master ,在这个模式下,release 分支是不应该 merge 回 trunk 的。所以应该选择 「 diff 出 master 比 develop 多的 commit ,逐个 cherry-pick 到 develop 」这条路,再精确点说,应该是「逐一检查 master 上的提交,判断是否需要 cherry-pick 到 develop 」。
bojackhorseman
2023-12-05 16:59:46 +08:00
我用的 git-flow ,现在为了好看,先 rebase 再 git flow <subcommand> finish ...
bojackhorseman
2023-12-05 17:02:35 +08:00
@bojackhorseman 用 git-flow ,当 hotfix 完成的时候会同时合并到 master 和 develop ,并创建一个 tag ; feature 完成只会合并到 develop ; release 完成同 hotfix ;

HangoX
2023-12-05 17:09:06 +08:00
最简单的是 merge ,当你推送只有一个 commit 的时候,遇到冲突,可以考虑用 rebase ,这样是最简单也能减少无用的 merge 节点
Albertcord
2023-12-05 17:32:35 +08:00
@leemars 这种模式的前提好像是 cherry-pick 到 develop 之后,不打算将 develop merge 到 master 上,不过现在也确实是这样的,如果后面项目交付变成现在的版本,就直接交付 develop 分支的代码,那现在的分支管理应该要变更下
develop (开发) - master (迭代产物)- master-xxx (迭代产物、交付项目分支)
因为 master 几乎不交付,所以不会在 master 上 hotfix ,主要在 master-xxx 上交付/hotfix ,那后续可以考虑你说的这种方式开发
justplaymore
2023-12-05 17:34:03 +08:00
你现在两个分支合并的难点在于日积月累产生的大量冲突,这不是具体合并方式能解决的。

git 工作流是在项目推进过程中起作用的,帮助每次迭代快速解决小量冲突,而不是在项目不遵循工作流导致分支管理出问题了,开始想用 git 工作流解决已经产生的问题。这不是 git 工作流的用法,git 工作流的存在是在事前避免这种情况,而不是事后解决这种情况。

无论用哪种方式,最终的瓶颈都是解决冲突,只能靠熟悉这两个分支的开发人员去手动解决冲突,没捷径。
Albertcord
2023-12-05 17:34:08 +08:00
@bojackhorseman 原来还有这个工具,我一直都是手动操作,这个可以在后面推广,赞
Albertcord
2023-12-05 17:35:41 +08:00
@justplaymore 确实是这样,根源是一开始 hotfix 时没有时刻采用 6L 的做法,现在补救确实没有捷径
Immortan
2023-12-05 17:39:20 +08:00
这个场景 merge 一劳永逸。rebase 和 cherry pick 麻烦且反而容易出错不好保证一致。
Exxfire
2023-12-05 18:14:12 +08:00
吐槽下我的领导,他强烈要求我们使用 beyond compared 进行合并
1xiaozi
2023-12-05 19:11:40 +08:00
@bojackhorseman 你好,有个问题咨询下。因为 hotfix 会同时合并到 develop 和 master ,feature 又从 develop 切出来,最后形成 release ,release 测试完成后又合并回 master 和 develop 。

请问这个时候在 master 上是否会存在同一个 hotfix 的不同提交记录。
tuwulin365
2023-12-05 19:37:58 +08:00
@Exxfire #13 解决冲突用 bc 感觉好用
leemars
2023-12-05 19:38:02 +08:00
@Albertcord 最好的避免 merge 地狱的方式就是不 merge 。在 trunk - release branch 的模式里,所有的 cherry-pick 都是从 trunk -> release branch ,纯单向,不会搞错。

还是以你的项目为例,在 trunk ( develop ,其实应该用 master 更合适)上开发,当需要交付版本的时候,拉出一个 release branch ( master ,其实应该用 release-v1.0 类似的更合适),开始在这个分支上进行针对交付的修改(关闭某些未完成的功能,调整某些版本号标识,比如 VERSION="dev" -> VERSION="v1.0",etc )。这个分支就是「专属于该发布使用的」。

接下来看一些具体情况:

Q:发现 v1.0 有 bug 需要修复怎么办?( hotfix 场景)
A:如果是 release-branch 独有的问题,在 release-branch 上修复即可;如果是 master 上也存在的问题,先在 master 上修复,然后 cherry-pick 到 release-branch 上。

Q:需要在 v1.0 上发布新的功能怎么办?( release 场景)
A:首先已知所有新功能都应该在 master 上开发,而不是在 release-branch 上。接着需要根据增加新的功能涉及的 commit 范围做出选择,1. 从 trunk 上重新开一个 release branch 分支走完整的发布流程,或者 2. 从 trunk 上 cherry-pick 修改到 release branch 上。

可以看到不管什么场景下,都是 trunk -> release branch 单向的。

对于 git flow ,我其实是不太能理解为什么它是 work 的…… 比如当你需要同时维护多个版本线 (v1.x ,v2.x )时,git flow 的 main 分支是单线的,怎么办;比如当你在 main 上需要标记 VERSION="v1.0",从 main 派生出的 develop 分支上需要标记 VERSION="dev",然后 develop 分支又会在 release 时合入 main 分支( VERSION 打架),hotfix 分支又会从 main 分支派生出来同时合入 main 分支和 develop 分支( VERSION 又打架)……
nothingistrue
2023-12-05 19:46:40 +08:00
公共分支不能做 rebase ,提前把 rebase 排除。
Albertcord
2023-12-05 23:25:22 +08:00
@leemars 很赞,确实比较符合
GeruzoniAnsasu
2023-12-06 00:57:19 +08:00
排除 merge.

merge 绝对不能在双向相互合并的两个分支上进行,否则大概率丢代码。 双向相互合并是指同时存在 D->M 与 M->D 两个方向的合并( merge. cherry-pick 会产生不同 hash 的 commit 所以不会踩三路合并丢代码的坑)


一般这种岔出去比较远了又想统一回来的场景最好的方法是 squash + 重新分岔,但有个缺点是历史可能会丢(因为重新分岔后新的 dev 或者新的 main 就不是原来的了,在非直属继承的那个分支上只能看到一个很大改动的 squash commit )不过 git flow 发展到你现在这个场面就是没严格遵循或根本就没定好清晰的规范导致的,所以大概率历史对你们来说也不是那么重要,squash 掉也不会不可接受。


我们过去的实践是 dev 分支必须包含主线所有分支上的所有改动,包括 bugfix 但不包括 release-featureA / release-featureB 这种版本特化的 commit. dev 永远保持线性历史,即它不会接受其它分支的 merge ,只接受 rebase. 这样一来 bugfix 也好,feature commit 也好,都会拍平成对等的 commit ,不会结出 merge 蜘蛛网。feature 分支在并入( rebase )到 dev 上之前 reviewer 会要求一定程度的 squash, 保证一个 commit 既不太大没法 diff 也不至于太琐碎 spam 历史线。 然后 master 只作为 tag 使用,打 tag 时往 master 上 merge 一份( dev->master )打在 master 上。只要保证 master 上不自行产生 commit ,只接受 dev 的合并,它就永远不需要解决冲突
vvdsaa
2023-12-06 10:14:28 +08:00
平时工作流感觉就不太合理:按理说不应该在直接在 master 修 bug ,master 有的问题 dev 分支也有,所有日常应该先在 dev 分支提交 pr ,merger 后再 cherrypick 到 master 。保证两个分支一致(或者上面说的直接 git flow )
现在这种情况:建议花点时间整理一下 dev 分支,看看 master 的修了的 bug ,dev 分支还能不能复现,回归完之后,直接从 dev 新开一个分支,强推到远端 master
之后严格按照工作流走应该就没问题了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/997811

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX