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

Dav*_*rks 1 git merge

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

我检查了 master,它“落后于 43 个提交并且可以快进”。

git merge --ff-only
Run Code Online (Sandbox Code Playgroud)

现在 master 是最新的,我再次检查了我的分支。git status显示它仍然是最新的。

我现在想变基来掌握:

git pull --rebase
Run Code Online (Sandbox Code Playgroud)

但是我最终在一个只有我更改的文件上遇到了一系列合并冲突。我尝试修复合并冲突,但每个人git rebase --continue都提出了另一个冲突,似乎回应了我对它所做的每一个更改。

事实上,有问题的文件被重命名,我得到的合并冲突是旧文件名。在过去的几天里,我执行了多次提交,对文件进行了更新并重命名了几次。一切都在我的分公司完成。每次我进行更新时,我都会提交并将其推送到我的分支。

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

tor*_*rek 5

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

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

事实上,有问题的文件被重命名,我得到的合并冲突是旧文件名。在过去的几天里,我执行了多次提交,对文件进行了更新并重命名了几次。一切都在我的分公司完成。每次我进行更新时,我都会提交并将其推送到我的分支。

这是有关git merge的,但它适用于git rebase以及

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

...--o--B--o--o--...--H   <-- branchA (HEAD)
         \
          o--o--...--T   <-- branchB
Run Code Online (Sandbox Code Playgroud)

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

git diff --find-renames B H
git diff --find-renames B T
Run Code Online (Sandbox Code Playgroud)

任何文件的Git标识在提交“相同”BH,或犯“相同”BT,被下旨是“同一个文件”,无论其实际路径名的提交BHT

无论改变Git把之间BH是“我们的”改变(--ours)。无论改变Git把之间BT是“自己”的变化(--theirs)。Git 现在尝试结合这些更改。在它们重叠的地方,Git 会抱怨合并冲突。

如果Git是误识别了一些路径B具有相同或不同的路径HT,即(GIT认为)的文件版本之间的差异是在BVS同一文件(可能不同的路径名)的版本是H或者T不会是“好的差异”,并且会与其他差异发生严重冲突。这将产生您看到的合并冲突。

可能的解决方案是:

  • 在添加到H和/或T这样的新提交中再次重命名文件,以便 Git 不会错误识别文件。
  • 使用-X find-renames=<value>扩展选项来git merge更改相似性索引,以影响 Git 认为“相同”的文件。
  • 允许合并冲突发生,然后通过消除 Git 合并文件的尝试来构建正确的结果,代之以​​正确的手动合并文件。

请注意,如果在某些文件B与存在同名HT,Git会(目前)总是配对这两个文件了,即使他们没有关系。例如,假设 commitB有一个名为 的文件polish,指的是波兰国家,并且都提交H并将其T重命名为,polish-as-in-the-country一个提交,或者HT创建一个文件,该文件polish指的是鞋油。Git 会用新的(鞋子)来标识B's polish(国家),polish因为它们是相同的名称git diff可以告诉平原打破这种联系,但git merge不能。

重命名鞋抛光文件以polish-as-in-the-shoes将导致不GIT中看到一个polish在两者B和所述一个尖端提交。现在,Git 将搜索“最佳匹配”,然后polish-as-in-the-country每次都找到,并且知道在两个分支中都执行了相同的重命名。

rebase 是(像)重复的cherry-picks,每个cherry-pick 都是一个合并

运行git rebase <upstream>git rebase --onto <target> <upstream>告诉 Git 复制一系列提交。对复制的提交(基本上)正如文档所说的那样git log <upstream>..HEAD1 复制后的提交是由 给出的提交<target>,或者<upstream>如果您未指定<target>.

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

令人讨厌的是,虽然您可以通过git merge(通过添加新提交或提供-X选项)控制合并结果,但在变基期间您几乎无法控制每个樱桃选择。任何cherry-pick 的合并基础是被选择的提交的父提交。该--theirs承诺是在提交被精挑细选,且--oursHEAD提交在提交被内置在。如果 rebase 刚刚开始,那就是<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 rebase - 在 fork-point 模式下提交选择