[英]Why does this GIT merge not result in conflicts?
我们今天在工作中发现了 GIT 的一个严重问题,我想知道这是一个错误还是设计使然,以及如何解决这个问题。
考虑以下事件序列:
结果:即使文件已在两个分支中进行编辑,也不会报告合并冲突,更糟糕的是,即使是最近的提交,步骤 3.2 中的还原也没有保留。
这是一个巨大的问题,从以下最近的示例中可以看出:我的一位同事对不同的分支进行了类似的更改,注意到这些更改的一部分是恶意的,因此在其中一个分支上手动还原了其中的一部分. 合并分支后,他惊讶地发现他的恢复没有通过合并。
我上传了一个最小的例子到谷歌驱动器来演示这个问题。 您可以将 Master 合并到 test1 中,反之亦然,以自己查看。
https://drive.google.com/drive/folders/19a-QPwOQKsn9PywUPd2DRnvUOml03nZ-?usp=sharing
如果有任何问题,我将 TortoiseGIT 2.12.0.0 与 Git for Windows 2.32.0.2 一起使用。
你得到那个结果,因为那是正确的结果。
好吧,让我们修改该语句:这是 Git merge 规则的正确结果。 (据我所知,根据大多数其他合并程序的规则,这也是正确的,但有些算法至少会将此标记为注意。Git 不使用这样的算法。)
如果 Git 的合并结果不是你想要的结果,你有补救措施:见下文。
当 Git 进行合并时,Git 会注意三个快照:
一个快照是当前快照,即,如果您运行git rev-parse HEAD
您将获得其哈希 ID 的提交。 如果HEAD
附加到分支名称(通常是这样),那就是给定分支的提示提交。
一个快照是您在命令行中命名的快照: git merge foo
查找foo
以获取提交哈希 ID。
第三个,在许多方面也是最重要的,快照是合并基础。 (Git 将这个编号为“#1”, HEAD
/ --ours
为 #2,另一个 / --theirs
为 #3,在内部,即使我们必须先定位其他两个输入才能定位此合并基础输入.) 合并库的提交哈希 ID 通过提交图定位。 在您的情况下,它是您正在调用 commit X 的提交之前的提交。
让我们像这样绘制这些提交,将更新的提交放在右侧,单个大写字母代表每个实际提交哈希 ID:
X <-- master
/
...--G--H <-- here's where both branches start diverging
\
X'-X" <-- test1
在这里, commit X
包含您在master
所做的更改; 提交X'
具有相同的更改,而X"
撤消这些更改,以便X"
中的快照与H
中的快照完全匹配。
Git 的合并算法包括执行以下操作,假设您在master
(因此提交X
是当前/ HEAD
提交)并且正在合并test1
(提交X"
):
将提交H
提取为“阶段 1”。
将提交X
提取为“阶段 2”。
将提交X"
提取为“阶段 3”。(请注意,我们刚刚确定X"
的内容与H
的内容匹配。)
对于索引/阶段中的每个文件,都存在于所有三个阶段插槽中:
对于不存在于所有三个插槽中的文件(例如,可能发生了重命名或复制的位置),事情变得更加复杂。 这些可能会导致高级别的树冲突,这些冲突被视为冲突,但不会显示为冲突的工作树副本。 但是这种情况在这里并不适用,所以我们可以忽略它。
git merge
已经完成,但现在有清理步骤:
正确合并的文件被放到槽 0(如果/根据需要写出到工作树)。
有合并冲突的文件留在所有三个插槽中; 工作树在合并时得到 Git 的最大努力,包括冲突标记。
在发生冲突的情况下,合并现在在中间停止; 用户必须完成它。 每个文件的工作树和索引副本都存在于此处供用户使用。
否则,除非被告知停止而不提交,否则git merge
自行完成合并,通常是通过创建一个新的合并提交。
如果此合并结果不是您想要的,您的补救措施包括但不限于以下内容:
更改输入(例如,通过向一个或两个分支添加更多提交)。
使用git merge -n
以便git merge
在提交合并结果之前停止。 使用索引和工作树文件——现在都暂存提交,索引槽 #0 中每个文件只有一个版本——来产生你想要的结果。 然后,提交结果。 请注意,这称为邪恶合并。 它没有什么问题,但是如果你让 Git 重复合并——例如,使用花哨的新git rebase --rebase-merges
代码git rebase --rebase-merges
不会知道让新合并成为一个邪恶的合并,所以明智的做法是用提交消息或其他东西清楚地标记这一点。
进行合并,让它成为“错误的”,提交它(并且可能标记它,特别是对于稍后的skip-during-bisect),然后添加一个提交来修复问题。
在您描述的情况下:这是git merge
操作的预期结果——torek 的回答详细说明了原因。
在 git 方面:
您提到的工作流程的一个问题是git cherry-pick
没有注册原始提交的链接。 您必须知道,不知何故,管理cherry-pick
有点像管理代码中的“复制/粘贴”:如果在一侧修复了错误,则应手动将其移植到另一侧。
顺便说一下, git rebase
也是如此:如果您应用了一个 rebase 操作,该操作以某种方式保留了原始分支,并且还创建了一个带有一些复制提交的新分支,那么这也是复制/粘贴提交。
一个更通用的原则是:
“ git merge
没有冲突的情况下成功”并不意味着“生成的代码没有错误”。
您应该始终验证生成的代码是否符合您的期望:
对于您的发布分支尤其如此; 有时,例如您描述的那个(开发人员合并到一个分支),这可能更多是关于您的团队意识到潜在的陷阱——并检查差异。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.