今天遇到个 git 合并丢代码的场景。 featB->featA->master featA 基于 master 开发,featB 基于 featA 开发。featA 合入 master 后,我直接在 featB 分支上 git merge master ,出了问题。 具体如下 featA 对于 file1 加了 line70 ,featB 对 file1 删了 line70 ,在 featB 上 merge master 后,git 自动 merge 的结果是 line70 依然还在
1
xiaozhu5 3 天前
git reflog 和 git fsck 两个结合看一下应该找到丢失信息
|
2
gesse 3 天前 1
featA 添加了 line70 ,并合并进了 master ,你在 featA 上 fork 出 featB ,featB 上删除了 line70 后,又把 master 合并进 featB ,这不就是在 featB 上添加了 line70 吗? 一点毛病没有。
|
3
mark2025 3 天前 1
为什么要在 featB 上面执行 featB 分支上 git merge master ? 这就是混乱的根源
1. 要么是在 featB 分支上 `git merge featA` 2. 要么是在 featB 分支上 `git rebase master` |
4
netabare 3 天前 via Android
所以不要把 master 合入分支,分支上面只用 rebase 。
|
5
GeruzoniAnsasu 3 天前 2
https://v2ex.com/t/843165#r_11508345
> (!!) 其实我本来想简单解释一下为什么三路合并会出问题,但在我搜索看了近一个小时文章后,我选择放弃解释: https://www.waynerv.com/posts/git-merge-intro/ https://git-repo.info/zh_cn/2020/03/something-about-git-merge/ https://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 次搜索这个问题然后仍然没有完全搞懂了 |
6
mark2025 3 天前
@GeruzoniAnsasu 三路合并就是人多嘴杂,你不仔细查看变动就无法确定最终合并结果是否符合预期。所以基于 rebase 的线性合并在团队开发中是最高效的( gitlab 可以设定强制线性合并)
|
8
sagaxu 3 天前
git merge 除非是 fast forward ,在处理多个分支修改同一文件时,不一定符合你的预期。
所以这种情况用 rebase 甚至是 reset --soft ,然后手动处理变更会更好。 |
9
GeruzoniAnsasu 3 天前
@mark2025 人多嘴杂是其次,问题在用好 merge 要制定的规范(比如不允许 pull-merge ,自己的分支不能 merge 别人分支后再 merge 到 main……等等,它一点也不直观。
你搞不清楚需要哪些规范才能保证不发生 A merge B 然后 B merge A 导致代码没了这种问题 |
10
mark2025 3 天前
@GeruzoniAnsasu 这是我所有 git 项目钩子自动执行的:
git config --global i18n.commitencoding utf-8 git config --local core.autocrlf input git config --local core.eol lf if [ -z "$CI" ]; then git config --local core.filemode false git config --local core.hooksPath ./.githooks git config --local core.ignorecase false git config --local core.precomposeUnicode true git config --local fetch.prune true git config --local pull.rebase true git config --local push.autoSetupRemote true git config --local push.followTags true git config --local rebase.autoStash true git config --local remote.origin.prune true git config --local remote.origin.tagopt --tags git config --local remote.pushdefault origin git config --local rerere.enabled true fi; |
11
leonshaw 3 天前 via Android
base 没有 line70 ,那严格来说 B 并没有基于 A 开发
|
12
leonshaw 3 天前 via Android
B merge master 没有问题,但是不能 merge 没有进 master 的 A
|
13
LeeEnzo 3 天前
强制 rebase 和 squash 开发
|
14
freesun165 OP @netabare 话虽如此,但我这个业务场景经常一个分支测一个月以上,一百多个提交,每次 master 更新,我挨个 rebase 下,成本太高了
|
15
freesun165 OP @leonshaw 我是直接在 featA 上切出去 featB
|
16
freesun165 OP @mark2025 俺这规范就是上线前 featB 需要合并 master 推到远端,远端 master 再合并 featB ,然后就出现了这么不符合知觉的事
|
17
riceball 3 天前
还是 Git 操作要有规范,这个彼此合并,左右互搏,啧啧,建议使用 Git flow 规范,有 git 插件支持: https://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html
|
18
rbaloatiw 3 天前
按你的描述感觉不太可能, 你有最小可复现样例吗, 可以发出来看看
|
19
coolcoffee 3 天前
git 的分支操作应该是像一颗树一样,开叉但是不会互相交叉。 如果平行的节点需要互相同步,那么应该一方往上推到相同的节点,另外一方再去拉,这样就遇到相同修改就必定会产生冲突。
|
20
BeautifulSoap 3 天前 via Android 2
唔嗯?这情况你确定真没冲突吗?
|
21
kivmi 3 天前
太危险了,master -> branch , 相当于你对同一行的修改无效啊,各种冲突吧?可以从 master 同时 fork 几个分支?一个为开发分支,一个为上线分支?当需要上线时,合并到上线分支,然后合并到 master ,一直保持上线分支跟 master 保持一致,实现快速上线。貌似 git flow hotfix 也可以做到。
|
22
zthxxx 3 天前
老生常谈话题之「不要把 master/dev 合到自己的分支」,开发时也始终应该像 GitHub / GitLab 那样的「把自己分支合到 master/dev (主干分支)」
|
23
leonshaw 2 天前
@freesun165 #15 这样 base 应该是切出去时的 commit ,不然就是后面这个 commit 被重写掉了,最好画个图看看。
|
24
BeautifulSoap 2 天前
我实际在本地测试了一下
最终结果是 master 合并入 feature b 后的确没报冲突,但被 feature b 删除的 line 也没再次出现 可能 lz 实际给个最小可复现例子比较好 |
25
nightwitch 2 天前
git 只允许 fast-forward 就不容易出现这个问题。
三路合并一定要小心,否则很容易出现冲掉别人的代码 / 别人的代码把自己的冲掉 / 两边的代码合并到了一起导致逻辑不对了。 |
26
FrankAdler 2 天前
我也遇到过,目前没有头绪,我是 master 拉出来的分支,修改后合并到 dev 分支,cicd 到测试 k8s ,出现过几次代码没有提示冲突,但是丢了几行。
|
27
jqtmviyu 2 天前
|
30
networm 2 天前
@freesun165 #14 使用 Fork 的 Leaning Branch 功能进行同步,只需要一键就可以自动同步。
另外推荐看下: Git 精干分支 - 狂飙 https://networm.me/2022/09/11/git-lean-branching/ 合并分支只会通过合并提交引入最终的修改,而不是合并分支中的原始提交。 因为合并提交也是提交,但是大家潜意识都不会关注合并提交的改动,因此可能会在冲突解决中引入大量的错误的修改。 如果一个东西容易引起错误,那么建议减少这个东西的使用,使用 Lean Branching 方案就可以。 在与主干同步的时候,相当于检出到主干新建分支,将原有分支上的东西 cherry-pick 到新分支,因此需要逐个解决冲突。 这个方案的核心是只在最后时合并提交,同时由于主干与功能分支的起点之间没有提交,合并提交引入的修改全部都是功能分支的改动。由于前面已经处理了冲突,这里逻辑上就不需要处理冲突,从根本上去除了冲突的处理。 |
31
chenluo0429 2 天前 via Android 2
我猜 featA 合并到 master 时,经过了 sqlash 之类的操作,导致 master 上 featA 的修改与 featB 所基于的 featA 并不是同一笔提交,而是修改内容相同的两笔不同提交
|
32
feelapi 2 天前
merge, rebase 两种思路是冲突的。不建议混合使用。
merge ,master->A->B ,不能跨越这个流程,B 回到 master ,也要顺序回去。merge 之前,要先从 parent 更新,例如 B 回到 master: 1. merge A->B 2. Merge B->A 3. merge master->A 4. merge A->master |
33
wgbx 2 天前
Git 不是万能药,要遵守 git flow
|
34
freesun165 OP @rbaloatiw 发了,大佬可以看下
|
35
freesun165 OP @jqtmviyu 就是( main ) git merge A 时加上 squash 就可以复现了,因为当时是 gitlab 上勾选了 squash commit 的选项
|
36
freesun165 OP @chenluo0429 是的
|
37
ryougifujino 2 天前
不用 squash 就不会有这个问题,squash 等于把历史线改了。还有功能基于功能分支进行切换是典型的“穷人的模块化架构”,一般采用 feature flags 类似的技术比较好。建议看一下 Martin Fowler 的这篇版本控制流的文章: https://v2ex.com/t/1072486
|
38
zhuisui 2 天前 1
你要想充分利用 git merge 的自动冲突解决能力,就不要做 squash 、fixup 、amend 、cherry-pick 这类会修改 commit 的操作,你必须在 merge 时原样保留各路 branch 。
你只要记住,如果你要 merge branch ,一定要保留 branch 原来的样子。因为 git 就是利用 branch 和 merge commit 来决定冲突解决结果的。 |
39
proxychains 2 天前
rebase 的好
|
40
p1gd0g 2 天前
之前遇到几次丢代码,几个主流的 git 软件都看不到哪一次提交丢了,只有 tortoisegit 可以。
但我们没有用 squash ,我也没搞清楚同事到底是怎么操作的。 |
41
wangtian2020 2 天前
不使用 sourcetree 喜欢用命令行操作 git 导致的
|
42
leonshaw 2 天前
很明显你 squash 把 A/B 分支点删除了,导致和 main 的分叉在 local/A 以前,这一行的添加删除都发生在分叉以后,互相抵消了。
因为 A 已经合并了,所以 B 合并前应该 git rebase --onto main local/A 把 A 排除掉。 |
43
blublu 2 天前 via iPhone
因为你 feat A 和 master 的 base 点( b1 )并没有 line 70 ,当你把 feat A 向 master 进行 merge 并且用 squash 方式时,新的 merge 点有了 line 70 ,记为 m1 ,当在 feat B 删掉 line70 时,和 base 点(与前面 feat A 与 master 的 base 点一样( b1 ),因为用的是 squash ,所以这个 base 点并不是 feat A 中 co 到 feat B 的那个节点)的对比中,并不会出现 line70 的变更,因为和 feat A 增加的那一行已经抵消掉了,因此会把 m1 与 b1 对比中增加的 line70 加入到 featB 中新的 merge 点 m2
不知道我这样说你清楚了么?不清楚可以再多读几遍,应该解释了整个变更过程了 |
44
mark2025 8 小时 42 分钟前
@freesun165
话虽如此,但我这个业务场景经常一个分支测一个月以上,一百多个提交,每次 master 更新,我挨个 rebase 下,成本太高了 ======= 可以不用频繁 rebase ,而是功能分支要合并、发布之前集中一次 rebase 。 |
45
mark2025 8 小时 41 分钟前
@freesun165 俺这规范就是上线前 featB 需要合并 master 推到远端,远端 master 再合并 featB ,然后就出现了这么不符合知觉的事
====== 这个是你们架构师、技术总监等等没设计好流程规范的问题。 |