Git 在合并时似乎使用不正确的祖先作为基础



我正在尝试将我们的发布分支合并到我们的功能分支中,但 git 似乎对共同祖先是什么感到困惑。

我正在使用源代码树 v3.1.2 和 git v2.21.0.windows.1

在 2 月 7
日之前
,开发分支与发布分支并行运行,对开发进行了更改,并对发布进行了热修复。

2 月 7
发布分支到功能。

5月9
开发分公司并入发布分公司。

5 月 15 日(今天)
尝试将发布分支合并到功能分支中。
我最终遇到了 78 次冲突。

  • 一些文件在合并之前已添加到开发分支 - 看不出为什么这些文件会是冲突。
  • 某些文件存在,但在功能分支中未更改 - 不明白为什么这些文件会是冲突?
    三向合并(在 Beyond Compare 4 中)表明在功能分支中删除了许多代码,而实际上它是在开发分支中添加的。
  • 某些文件在两个分支中都已更改,但在三向合并中存在相同的问题。

在查看冲突时,我可以看到三向合并是多么混乱,所以我不确定非冲突文件是否发生了同样的问题。 乍一看,它们看起来还不错,但是它们很多,所以我不能确定。

我运行了以下 git 命令,该命令从 4 月 24 日开始在开发分支上返回了一个看似随机的签入。

git 合并库(最新功能分支提交)(最新发布分支提交)

我本以为我会在 2 月 7 日之前在开发和发布分道扬镳之前收到最后一次提交。

我试过:

  • 将发布合并到功能
  • 将功能合并到版本中
  • 将功能重新定位到发布(我认为不建议这样做,但我想无论如何我都会尝试)我似乎丢失了一堆这样做的文件。
  • 将版本重定为特征的基准
  • 重置为 2 月 7 日分支,重新分支,然后合并。
  • 合并
  • 开发分支,因为它被合并到发布中。
  • 我一直在尝试确定是否可以强制一个共同的祖先进行合并,但我不确定我是否可以从我所阅读的内容中做到这一点。

值得注意的是,几周前我将开发分支(这次不是发布分支)尝试合并到功能分支中,一切似乎都进展顺利。 从那以后,我将SourceTree从1.7版本升级到3.1.2,这也需要git升级。

我对这个 git 的东西不是很好,所以任何帮助将不胜感激。

我一直在努力弄清楚我是否可以强制一个共同的祖先进行合并,但我不确定我是否可以从我所阅读的内容中做到这一点。

不。 Git 会为您计算合并基数。 它们基于提交图。

值得坐下来画图表(或者让git log --graph为你画)。 尝试使用git log --all --decorate --oneline --graph(请记住,这是从 A DOG 获得帮助),也许可以添加--simplify-by-decoration以帮助减少很多视觉混乱。

通过眼球查找合并基地

使用简单的图表,很容易直观地看到哪个提交是另外两个提交的合并基础。 例如,给定一个可以这样显示的图形,稍后向右提交:

o--o--L   <-- branch1 (HEAD)
/
...--o--*

o--o--R   <-- branch2

。提交L(您当前签出的提交,HEAD,在branch1的尖端)和提交R(branch2的尖端提交)的合并基础是提交*

许多图表是可怕的和纠结的,根本不容易像这样直观地解析。 即使是一些更简单的一开始也有点棘手。 重复合并会发生一种非常常见的情况:

...--o--o--o--o--o--o--*--o--L   <-- branch1
             
o--o--A--o--o--B--o--R  <-- branch2

再一次,LR的合并基础是提交*。 有两个现有的合并,AB。 合并A无关紧要,因为合并B创建可从两个分支提示访问的第一个提交:我们从L开始,然后返回两个提交到*,我们从R开始,返回两个提交到B,步行到它的父*,并到达一个共享提交。

变基

我试过[变基]

请注意,使用git rebase副本提交,通常会丢弃合并。 假设你有上图,合并到branch2,你决定git rebase branch2 branch1。 这将枚举所有可以从branch2访问但不能从branch1访问的提交,不包括合并。 虽然我们可以从branch2branch1达到提交*,但我们不能从branch1获得任何底行提交。 因此,要复制的提交都是底部行的提交。 让我们给它们一个字母的名字,以便我们可以谈论它们:

...--o--o--o--o--o--o--*--o--L   <-- branch1
             
C--D--A--E--F--B--G--R  <-- branch2

(我保留了我们已经拥有的ABR的单字母名称)。

变基操作(git checkout branch2; git rebase branch1)选择底部行的所有非合并提交并复制它们。 副本在提交L之后,在branch1的尖端,因此结果是:

C'-D'-E'-F'-G'-R'   <-- branch2
/
...--o--o--o--o--o--o--o--o--L   <-- branch1
             
C--D--A--E--F--B--G--R  <-- ???

虽然此时名称branch2已被移动 - 它现在指向R'R的副本 - 原始提交仍然存在。 只是无法再从名称branch2访问它们。

如果有另一个分支(例如branch3)来自部分或所有这些提交,我们可以将其绘制为:

C'-D'-E'-F'-G'-R'   <-- branch2
/
...--o--o--o--o--o--o--o--o--L   <-- branch1
             
C--D--A--E--F--B--G--R  <-- ???

H--I   <-- branch3

由于RGB不容易找到,我们可以完全停止绘制它们:

C'-D'-E'-F'-G'-R'   <-- branch2
/
...--o--o--o--o--o--o--o--o--L   <-- branch1
     
C--D--A--E--F

H--I   <-- branch3

提交C-D-A-E-F仍然有效,但是,可以通过从branch3(提交I)开始并向后工作来实现。

如果现在选择将branch3branch1branch2合并,则可以通过从两个分支尖端开始并像往常一样向后工作来找到合并基础。 在这种情况下,合并基础是我们从提交A向上和向左到达的提交(合并仍然可以从branch3的尖端到达),因为该提交可以从两个分支提示访问,并且是"最接近结束"的,就像它一样。

由于这种多名称到达旧提交的事情,因此变基支持其他分支的分支通常是不明智的。 重定包含合并的分支的基数也很棘手。 现代 (2.18+) Git 有一个--rebase-merges选项,允许 Git 重新创建合并;较旧的 Git 有尝试做同样事情的--preserve-merges,但比现代方法更脆弱。 但是,在所有情况下,重要的是要知道,当 rebase 必须复制合并提交时,它实际上只是再次运行git merge,以便计算新的合并基,并从头开始重做合并。

其他说明

请注意,提交的日期和时间戳在这里完全无关紧要。只有图表很重要。 提交及其节点哈希 ID 和父边缘哈希 ID 构成提交图。 每个提交中的内容是历史记录;合并基础中的内容和两个分支提示是git merge的重要内容。

要让 Git 告诉您它用于合并基础的内容,请执行您所做的操作,但在有多个合并基础的情况下添加--all

git merge-base --all branch1 branch2

Git 将执行合并基础查找:从名称指向的最后一个提交开始,沿着所有路径向后走,并找到最佳提交。 然后,它将打印出所有合并库的哈希 ID。 然后,合并将通过比较(与git diff一样)(单个)合并基础与两个分支提示提交来进行合并。

如果有多个合并库,默认的-s recursive策略将首先在合并基本身上运行git merge,1执行递归合并,直到 Git 可以提出单个提交作为合并过程的"虚拟合并库"。 你可以让 Git 通过使用-s resolve来(明显)随机选择其中一个。 这通常没有任何改进,但在复杂的情况下需要了解。


1除非你真的很倒霉/对你早期的图表制作非常疯狂,否则最多有两个合并基础。 如果有更多,Git 会合并两个,提交结果,将其与下一个合并,提交结果,依此类推。

最新更新