正常开发中,我们会从 main->new branch feature 分支,当我的 feature 开发完毕后,origin/main 可能已经更新了很多个 commit(从其他 feature 合并而来),很可能存在冲突,此时我有两种操作方式,哪种方式更好?

先在本地将 main pull 之后,将 local/main 合并到 local/feature,在本地解决冲突,然后 push origin feature,然后在 gitlab(或 github)提交 merge request(或者 pull request),将 origin/feature 合并到 origin/main
直接 push origin feature,然后在远程提交 merge request(或者 pull request),在远程仓库(可视化工具)解决冲突,然后将 origin/feature 合并到 origin/main

这两种方式各有什么优缺点?哪种更适合企业项目的团队合作?

远端不用重新 approve .

approve 肯定都需要,我们是两个同事 review 后 approve,才能进行 mr

1 才有机会解决冲突后运行和测试

除非你们有足够好的自动测试 不需要手工保证

你的意思是 1 可以在 mr 之前基于 feature 进行测试是吗?我这里简化了,实际上肯定不是直接 mr 到 main 上,一般会有个 dev,2 的话也可以 mr 之后基于 dev 测试

我的意思是 重新 approve.

推荐 1 是吧

1

1

1 提交代码要保证合并的正确性和正常运行

对于比较大的团队和比较严格的规范,应该是远程合并,因为普通开发不应该有 write main 分支的权限。
而对于简单项目和小团队,以及实际操作上来讲,本地合并的话,解决冲突更容易,因为远程合并一般都是命令行或者 web 界面,本地可以用一些 gui ,鼠标点点就能正确合并,操作更简单。

楼上在说啥😂

#10
让 feat 分支 rebase main ,然后解决冲突后 push feat 再 开启 mr 就可以了,不用 merge 到 main 再 push main 。

其实没有「远端解决冲突」一说,所有的合并和冲突解决都是在叶子节点上或者说都在 git 的「客户端」进行的。

web 界面只是自动做了一些额外的事情而已,跟你在本地用一个 GUI git 工具是一回事。

当我的 feature 开发完毕后,origin/main 可能已经更新了很多个 commit(从其他 feature 合并而来),很可能存在冲突

此时你 不应该进行任何 MERGE 尝试

  1. 首先应该在本地切换到主分支( dev/main/master ),拉取远程的状态。由于你不可能直接在这些分支上修改,所以这一步不会产生任何冲突,仅仅是快进
  2. 切回到你的开发分支,把它 rebase 到主分支上,此时产生冲突,resolve ,重新 commit 。
  3. 由于到目前为止你还是只修改过你的独占分支(就算有远程也是可以随便 push -f 的那个),所以不会对其他人产生任何影响。
  4. push 你 rebase 过后的分支,开启或刷新 MR

之所以采用不断 rebase 分岔点的方式是因为,如果你不进行 rebase ,merge 操作时是一个三路合并,而三路合并是一个既复杂又存在危险性 的操作

(!!) 其实我本来想简单解释一下为什么三路合并会出问题,但在我搜索看了近一个小时文章后,我选择放弃解释:
www.waynerv.com/posts/git-merge-intro/
git-repo.info/zh_cn/2020/03/something-about-git-merge/
actake.github.io/2021/03/21/git%E5%BF%85%E7%9F%A5%E5%BF%85%E4%BC%9A-%E5%88%86%E6%94%AF%E5%90%88%E5%B9%B6%E9%82%A3%E4%BA%9B%E4%BA%8B/

(!!) 因为这已经是我至少第 4 次搜索这个问题然后仍然没有完全搞懂了

如果采用 rebase - merge FF 的方式,合并操作等同于在对方分支上把你做过的操作重放一次,心智负担和风险要小得多。只有一个麻烦,因为是重放,当你的 commit 序列一开始就与对方冲突时,这个冲突会有传递性(对方是 A ,你第一个提交把 A 改成了 B ,后面的提交全部在用 B ;发现冲突后你决定改成 C ,那么当你 rebase 重放的时候所有后面的提交都会不断发生 B 和 C 的冲突)。但这都可以通过减少 commit 修改量和 squash 改善,相比起三路合并能由(虽然存在疏忽的)正常操作产生 无感知的 丢修改相比,成本显然要低得多

如果在本地已经有冲突的代码,为什么还要提交到远端去? 别人把这个冲突代码拉下来不是骂娘。 所有冲突都应该在本地解决。

一般这种情况是 1.git fetch 2.git rebase origin/master 3. git push -f (rebase 之后 feature 分叉了所以需要 force push)

这两个方式没有什么本质区别呀,核心都是自己 feature 分支随便玩,自己单独开发的话 rebase 都可以,保证合入不冲突,最后 mr 到 main 都只有一个 commit 。
区别就是你在本地 editor 解决冲突还是网页端可视化工具解决冲突。大概率复杂情况下你都需要在本地解决的,网页端都不好搜索代码

我倾向于本地解决冲突,还能顺便测试一下是否运行正常

rebase 到 main 和 merge main 有什么区别呢?

不太复杂的小项目 git flow 感觉有点复杂, Github flow 就够用了

rebase 吧, 你的基被人往前推了, 那就变基到跟线上一致提个 PR, 主线分支光溜溜一根挺好的.

git rebase ,本地解决冲突,再 push 上去。

rebase 后分岔点在基分支的最前端,merge 的分岔点是基分支上历史中的某个点。rebase 的历史永远是线性的,merge 会织出复杂的演化网。

rebase 目标分支和本分支不对等,你基本不可能搞错(不可能试图把 main 拼到 feature 上,只会把 feature 拼到 main )
而 merge 就可 tm 难说了,feature merge main 和 main merge feature 观感上根本没什么区别,看起来也都很合法,但形成的历史网是不同的,出问题的时候根本没人有能力搞清楚发生了啥

正常都是开发者自己解决冲突,不限制解决冲突使用的工具,测过了再提交,而不是滥用 Web 编辑器让公共分支变成不可维护的临时 patch 火葬场。
Reviewer 只需要对 diff 和 merge 结果负责,不需要对具体操作负责。
即便要用统一的 flow ,Gerrit 之类也有事后诸葛亮。
实际上 merge 不见得应该是首选项。要 merge 还是 ff-only 还是 squash ,你作为分支的 owner 应该清楚。或者你就不管,活干完了找清楚怎么处理这些问题的专人负责。

你说的两点都是错的。
2 不行因为 WebUI 功能太弱,建议本地合并完做好测试再推。
1 的话做法错了,主线往分支合并用 rebase 更好。以前 git 那个坑爹的命令行默认 pull 执行 merge ,然后不懂的小白就跟着 pull 了瞎 merge 。现在新版本的 git 命令行总算是去掉了默认 merge 的行为。

尝试在团队推行过 "Require branches to be up to date before merging"
最后发现 PR 合并效率太低,一次只能合并 1 个人 PR ,合并后其他人又要 rebase master ,然后重新等 CI 。。

#13,我明白了,谢谢

根本上还是在于是在 feat 分之还是 main 分支上处理冲突,肯定都会是在非 main 分支处理的。

比如我所在的团队,均是 feat owner 拉取 main 分支的,解决冲突后,再 MR 到 main.且为了 Review 体验方便,一般是在本地,当然特别小的 diff 的话,上游 WEB 也 OK

1 、feat 要合并前,先 rebase master 分支,本地解决完冲突后,推送到 feat

我之前恰好看过几篇文章,似乎是推荐先 rebase

求问一下这样是否可行?

假如某 feature 需要多人共同开发,于是事先约定大家新建、改动什么文件,保证不会互相干扰,于是从 main 分出 feature_branch ,并又分成多个个人的 feature_individual_branch ,最后开发完成之后合并进 feature_branch ,如何?

如果互相之间有干扰,比如说有个配置文件多人需要共同编辑,那看来只能指认其中一人为 feature reviewer ,把这些共同的文件承担下来了。

#12
新手求问,如果能够让每个 feature branch 在合并之前都自动进行 rebase ,是好是坏?

有冲突的时候 rebase 不成功的,没冲突的情况下,rebase 没问题

看公司,如果这个项目是你单独维护,甚至可以直接 push main 分支,能解决冲突就好。
如果是多人协作 且你不是分支管理员 的情况下 通常使用 1.
但需要注意的是,无论哪种方案,冲突都是在本地解决的,不允许在线上( web )处理。

也是,看来还是冲突最麻烦。那么是否应该尽量从项目架构上去避免冲突?比如说尽量避免多人修改同一个文件的情况出现?虽然未必现实。。。

把 main 分支更新下,然后重新 checkout 一个新分支,把修改加进去,然后用这个新分支发送 pr

当然是本地处理, 远程当成存档点来用. 我个人是倾向自己处理冲突合并的, 基本没 rebase 过.

你说的 2 ,其实就是 1 ,只不过是把解决冲突的操作从本地搬到远程而已。2 发生冲突的时候,你并不是必须用远程仓库的工具,还可以本地在 feture 解决冲突并推送之后,再重新执行 MR/PR 的合并。MR/PR 开启之后,是可以继续提交的。

如果你执行的是全 merge ,并没有 rebase 的话,2 会更省事点。

如果是用 rebase 来搞准线性历史的话,要用 1 的方式。

想问下,如果 rebase 的话,这个时候我的分支会有别人的 feature 或者 bugfix 的代码了,但是如果别人的 feature 这个版本不上,我的分支要上,怎么把别人的那部分拆出来?

cherry-pick

冲突要在 rebase 解决,不要在 merge 时候再去解决冲突。。我一直记得这句话

我们公司要求,所有冲突必须要求本地解决,一律 request 的冲突全部打回

在他的 feture 合并前,应该准备好 4 个分支:1 你的 feature ,2 他的 feature ,4 有他 feature 的版本分支,4 没有他 feature 的版本分支

我们之前的实践是,有一个接受所有 feature 的分支,比如 master ,然后给每个要发布的版本建 release 分支,他先写完后合并到 master ,你写完,rebase master ,合并。此时 master 上有两个 feature ( 0-2-1 ),而 release 上还什么都没有。 像 gitlab 有 自动 cherry-pick 的功能,web 界面上点一下可以把整个 MR (比如你合并 master 那个 MR )里的所有 commit 都 cherry-pick 到另一个分支上( release )。pick 完 release 看起来像是( 0-1 ),但 1 的那些 commit 不是原始 commit ,是 cherry-pick 重新生成的。

如果 web 界面没有这个功能,就只能在本地手动 pick 一堆 commit 。不过鉴于 release 分支一般也不需要保留修改历史( master 已经有了),所以可以把 feature 的所有 commit 都 squash 成一个 pick 给 release ,这样手动也不会很麻烦

理论上是 1,但是小公司菜逼多,都是让他们推上来发 PR ,我来解决冲突然后合并