git pull --rebase 在我自己的分支内对未在我的分支之外编辑的文件存在合并冲突



我有一个我一直在做的分支。git status表明它是最新的。

我检查了master,它"落后于43个提交,可以快进"。

git merge --ff-only

现在主人是最新的,我再次检查了我的分支。git status表明它仍然是最新的。

我现在想变基为主:

git pull --rebase

但是我最终在一个只有我更改过的文件上出现合并冲突流。我尝试修复合并冲突,但每个git rebase --continue都提出了另一个冲突,似乎可以回复我对它所做的每个更改。

实际上,有问题的文件已重命名,我得到的合并冲突是在旧文件名上。在过去的几天里,我执行了许多提交,对文件进行了更新并重命名了几次。所有这些都是在我的分支机构完成的。每次我进行更新时,我都会提交并将其推送到我的分支。

我不明白合并冲突的原因。该文件在我的分支中看起来是最新的。什么可能导致这种情况发生?

我不明白合并冲突的原因。

问题似乎是 Git 将重命名的文件错误配对。

实际上,有问题的文件已重命名,我得到的合并冲突是在旧文件名上。在过去的几天里,我执行了许多提交,对文件进行了更新并重命名了几次。所有这些都是在我的分支机构完成的。每次我进行更新时,我都会提交并将其推送到我的分支。

这是关于git merge,但它也适用于git rebase

当 Git 被要求合并两个分支提示 — 或者实际上是任何一对提交(HEAD、当前分支,以及其他一些分支提示提交或任何其他提交)时,Git 将首先使用git merge-base --all来识别这两个提示提交之间的合并库:

...--o--B--o--o--...--H   <-- branchA (HEAD)

o--o--...--T   <-- branchB

文件被重命名了多少次,在哪个分支上都无关紧要。 重要的是 Git 在运行时看到什么:

git diff --find-renames B H
git diff --find-renames B T

任何 Git 在提交中标识为"相同"的文件BH,或在提交BT中标识为"相同"的文件,无论它在提交BHT中的实际路径名如何,都被判定为"同一文件"。

Git 在BH之间看到的任何变化都是"我们的">变化(--ours)。 Git 在BT之间看到的任何变化都是"他们的">变化(--theirs)。 Git 现在尝试组合这些更改。 在它们重叠的地方,Git 抱怨合并冲突。

如果 Git 错误地将B中的某些路径与HT中的相同或不同路径识别,则(Git 认为)在B中的文件版本与同一文件的版本(可能具有不同的路径名)之间的差异在HT将不是一个"好的差异", 并且会与其他差异发生严重冲突。 这将产生您看到的合并冲突。

可能的解决方案是:

  • 在添加到H和/或T的新提交中再次重命名文件,以便 Git 不会错误地识别文件。
  • 使用-X find-renames=<value>扩展选项git merge更改相似性索引,以影响 Git 认为哪些文件"相同"。
  • 允许合并冲突发生,但随后只需通过清除 Git 合并文件的尝试来构建正确的结果,而是替换为正确的手动合并文件。

请注意,如果B中的某个文件在HT中存在同名,Git 将(当前)始终将这两个文件配对,即使它们不相关。 例如,假设 commitB有一个名为polish的文件,指的是波兰国家,并且两个提交HT将其重命名为polish-as-in-the-country,而一个提交(HT)会创建一个名为polish的新文件,该文件引用鞋油。 Git 会用新的polish(鞋)来标识Bpolish(国家),因为它们是同名的。 普通git diff可以被告知打破这种联系,但git merge不能。

将鞋油文件重命名为polish-as-in-the-shoes将导致 Git 在B和单提示提交中都看不到polish。 现在 Git 将搜索"最佳匹配",每次都找到polish-as-in-the-country,并知道在两个分支中执行了相同的重命名。

变基是(像)重复的樱桃采摘,每个樱桃采摘都是一个合并

运行git rebase <upstream>git rebase --onto <target> <upstream>会告诉 Git 复制一系列提交。 要复制的提交(基本上)如文档所述git log <upstream>..HEAD所示。1它们被复制的提交是<target>给出的提交,或者如果您不指定<target>,则<upstream>

无论如何,一旦 Git 有了要复制的提交列表,它就会复制它们,就像在每个提交上运行git cherry-pick一样。 (根据您的特定git rebase命令,这可能实际运行git cherry-pick,或者可以通过git format-patch ... | git am -3模拟它。 我还没有构建一个很好的示例来说明它们何时产生不同的结果 - 但是如果您确实遇到合并冲突,那是因为git am -3回退到三向合并,这与使用git cherry-pick具有相同的效果。

令人讨厌的是,虽然您可以使用git merge控制合并结果(通过添加新提交或提供-X选项),但在变基期间,您几乎无法控制每个樱桃选择。 任何 cherry-pick 的合并基础都是正在选取的提交的父提交。--theirs提交是精心挑选的提交,--oursHEAD提交是正在构建的提交。 如果变基刚刚开始,那就是您给出的<target>,否则就是最近成功复制的提交。

无论如何,此时您只能选择一种选择:手动正确合并文件。 (好吧,那或完全终止变基尝试,git rebase --abort. 然后,您可以自己重复使用git cherry-pick,并在适当时添加-X参数。


1文档在这里有一个善意的谎言。Git复制的几乎是那些提交;它实际上是git rev-list --cherry-pick --right-only --no-merges <upstream>...HEAD列出的那些。 这是相同的列表,除了它:

  • 省略任何合并提交,以及
  • 省略上游目标中具有相同git patch-id的任何提交。

此外,正如实际记录的那样,在某些情况下,--fork-point会更改给定<upstream>参数的起点,从而省略更多的提交。 请参阅 Git 变基 - 在分叉点模式下提交选择。

最新更新