Git冲突标记显示了不同的文件更新。

4

我们正在将主分支合并到功能分支,突然间,我们发现一些文件出现了冲突。

  1. 这些文件从未被移动到主分支
  2. 文件中的冲突标记显示头部的正确部分,但主分支标记显示来自完全不同文件的内容。

不确定第二点是如何发生的?我们现在真的很害怕。

<?xml version="1.0" encoding="UTF-8"?>
<ApexPage xmlns=http://soap.sforce.com/2006/04/metadata>
<<<<<<<< HEAD:salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml
    <apiVersion>53.0</apiVersion>
    <label>xyz</label>
========
    <apiVersion>55.0</apiVersion>
    <label>abc</label>
>>>>>>>> development:salesforce_sfdx/force-app/main/default/pages/abc.page-meta.xml
</ApexPage>

1
这听起来似乎不可能,所以您需要提供更多细节。我能建议的就是仔细检查两个分支中该文件的历史记录。如果Git显示了冲突,那么这些更改肯定在某个时间点上发生过。 - joanis
不是的,这是两个不同的文件。即使发生了更改,标记又怎么可能来自不同的文件呢? - Oxycash
1
不同的假设... Git 有智能功能,可以尝试识别文件是否被重命名,然后将更改应用于它认为已被重命名的文件。我有一个项目,其中我将存储库的一部分拼接到具有不同树结构的新存储库中,在该树中进行更改,然后挑选提交,并且Git可以找出要将这些补丁应用于哪些文件。感觉像魔法一样,但在我的情况下它起作用了,节省了我大量的时间。你手头的问题可能是这些智能功能误操作了? - joanis
我想听听@torek对这个问题的意见,他可能知道如何解决,但是我不知道如何邀请他来看看。也许悬赏会吸引比我更好的专家的注意。 - joanis
Oxycash,您能否运行Torek在他们的答案中展示的命令,并向我们展示结果? - joanis
显示剩余6条评论
2个回答

3

我在这里添加一篇答案,受到@torek的答案启发,提供一个潜在的解决方法。

很明显问题是由Git错误地推断出重命名操作并相应地执行造成的。目前还不清楚原因,但torek的建议使用git diff --find-renames进行故障排除,让我阅读了手册并发现两个选项可能会对您有帮助:

  1. You can tell Git to be strict when it's looking for renames, considering only identical files as renames:

     git merge --find-renames=100% <rest of your merge command>
    
  2. You can tell Git to turn off rename detection altogether:

     git merge --no-renames <rest of your merge command>
    
为了让这一切有意义,您需要知道Git仅在每个提交中存储整个项目树的快照。提交实际上不是作为更改集存储的,而是作为完整的新快照存储的。因此,每当您合并、挑选、查看日志、执行差异操作时,所有这些操作都是通过比较这些快照来完成的。重命名也不例外:您在提交中执行git mv的事实并没有被存储,它是通过与先前提交的差异或在有多个提交基础时可能出现的合并基础之间推断出来的。有时候推断会出错。

1
(注:目前这并不是一个真正的答案。)
合并冲突中我们拥有的信息来自于两个标记,具体如下:
HEAD:salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml
development:salesforce_sfdx/force-app/main/default/pages/abc.page-meta.xml

这告诉我们,对于这个文件,索引槽#2(--ours)的内容来自HEAD提交中的salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml,而索引槽#3(--theirs)的内容来自development提交中的salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml。因此,该文件必须存在于两个提交中,并且必须有一个合并基础版本的该文件,该版本将位于索引槽#1。
这里还有几件事情很有用。特别是,merge.conflictStyle在这里明显设置为merge。这隐藏了一些信息;如果它被设置为diff3,我们会得到更多信息。不过,我们可以重构这些信息。
首先,我们想知道哪个提交是该合并基础版本的源。要找出,请运行:
git merge-base --all HEAD MERGE_HEAD

如果一切顺利,这将输出一个单独的提交哈希 ID,或者如果情况不理想,则会输出多个哈希 ID。

其次,我们想知道索引槽#1中相应行中实际包含的内容。 很容易获得:git show :1:salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml应该提取整个文件,然后我们可以进行检查。

如果git merge-base命令显示多个提交,则可能合并基础是“虚拟提交”(由git merge-recursivegit merge-ort或此处使用的任何策略创建)。 在这种情况下,合并基础本身可能包含合并冲突。

无论如何,肯定有三个输入文件。 如果只有一个合并基础,那么我们的情况就好多了,因为我们可以使用git diff --find-renames找出所有三个提交中的这三个文件。 只需获取合并基础哈希 ID 并运行:

git diff --find-renames <hash> HEAD > /tmp/ours
git diff --find-renames <hash> MERGE_HEAD > /tmp/theirs

现在这两个/tmp文件包含了差异结果,包括任何检测到的重命名。这两个输出文件的HEADdevelopment提交名称相同(salesforce_sfdx/force-app/main/default/pages/xyz.page-meta.xml),因此真正的问题是合并基础文件的名称是什么。

有了这些额外的信息,我们可以继续查看为什么会出现合并冲突,并确定这是否代表了某种Git错误。


我已经成功创建了一个相关但不太具有代表性的示例。请参见 gist https://gist.github.com/joanise/ab529049e3859b6d3daa6ec7b0f77ab2,其中我创建了两个分支,文件名与 OP 的问题中的文件名相同。通过 cherry pick,我得到了与 OP 观察到的相同冲突。然而,这并不真正具有代表性,因为通过合并,Git 看到这两个文件都是独立添加的,没有冲突。我仍然不知道如何使用实际合并来重现此问题。 - joanis
我的意思是,如果没有在其中一个分支上使用git mv,我不知道如何重现这个问题,因为OP说文件从未移动到主分支。 - joanis
顺便说一下,我上面的要点是确定Git是否会假定从相似的文件开始意味着已经发生了重命名。实际上,在我的要点中,我创建了相同的文件,但没有共享的历史记录或git mv操作,然而Git仍然假定在cherry pick操作中一个文件是另一个文件重命名的结果。Git的这种假设恰好是不正确的,我的假设仍未得到证实,即OP的情况源于类似的情况。 - joanis
@joanis 一个文件来自主干(开发是我们的主干),另一个来自特性分支(在我的情况下是 HEAD)。我明天会分享建议命令的输出。 - Oxycash
阅读 git diffgit loggit rebasegit merge 手册时,注意到它们都有 --find-renames 选项,默认阈值为50%! 这似乎非常低。 如果我理解正确,则将删除一个文件并添加50%相同的文件的提交将在将来被检测为重命名。 这比我预期的阈值要低得多! - joanis
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接