git - 合并时 Git 似乎使用不正确的祖先作为基础
问题描述
我正在尝试将我们的发布分支合并到我们的功能分支中,但 git 似乎对共同祖先是什么感到困惑。
我正在使用 SourceTree v3.1.2 和 git v2.21.0.windows.1
在 2 月 7 日之前
,开发分支与发布分支并行运行,对开发进行了更改,并对发布进行了热修复。
2 月 7 日
发布分支到功能。
5 月 9 日
Development 分支并入 Release 分支。
5 月 15 日(今天)
尝试将 Release 分支合并到 Feature 分支。
我最终有 78 次冲突。
- 在合并之前将一些文件添加到开发分支中 - 不明白为什么这些文件会发生冲突。
- 一些文件存在,但在功能分支中没有更改 - 看不出为什么会发生冲突?
三路合并(在 Beyond Compare 4 中)表明在 Feature 分支中删除了很多代码,而实际上它是在 Development 分支中添加的。 - 两个分支中的某些文件都已更改,但在三向合并中存在相同的问题。
在查看冲突时,我可以看到三向合并是多么混乱,所以我不确定非冲突文件是否发生了同样的问题。乍一看,它们看起来还不错,但是它们很多,所以我不能确定。
我运行了以下 git 命令,该命令从 4 月 24 日开始在 Development 分支上返回看似随机的签入。
git merge-base (最新功能分支提交) (最新发布分支提交)
我原以为我会在 2 月 7 日之前的 Development 和 Release 分歧之前获得最后一次提交。
我试过了:
- 将发布合并到功能中
- 将功能合并到版本中
- 将功能重新定位到发布(我不认为这是推荐的,但我想我还是会尝试)我似乎丢失了一堆文件这样做。
- 将发布重新定位到功能
- 重置到 2 月 7 日分支,重新分支,然后合并。
- 在合并到发布时合并开发分支。
- 我一直在尝试确定我是否可以强制使用一个共同的祖先来进行合并,但我不确定我是否可以从我所读到的内容中做到这一点。
值得注意的是,几周前我尝试将 Development 分支(这次不是 Release 分支)合并到 Feature 分支中,一切似乎都很顺利。从那时起,我将 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
是 commit *
。
许多图表都是可怕的和纠结的,而且像这样在视觉上并不容易解析。即使是一些更简单的一开始也有点棘手。重复合并会发生一个非常常见的情况:
...--o--o--o--o--o--o--*--o--L <-- branch1
\ \ \
o--o--A--o--o--B--o--R <-- branch2
再一次, 和 的合并基础L
是R
commit *
。有两个现有的合并,A
和B
. 合并A
是不相关的,因为合并B
创建了从两个分支提示可到达的第一个提交:我们从 开始L
并返回两个提交到*
,我们从 开始R
,返回两个提交到B
,走到它的父节点*
,并且已经达到共享提交。
变基
我试过[变基]
请注意,使用git rebase
副本提交,通常会丢弃合并。假设您有上面的图表,并合并到branch2
,并且您决定git rebase branch2 branch1
。这将枚举所有可以从branch2
但不能从的提交branch1
,不包括合并。虽然我们可以*
从branch2
和 branch1
到达提交,但我们无法从 到达任何底行提交branch1
。所以要复制的提交都是底行的所有提交。让我们给它们一个字母的名称,以便我们可以讨论它们:
...--o--o--o--o--o--o--*--o--L <-- branch1
\ \ \
C--D--A--E--F--B--G--R <-- branch2
(我保留了我们已经为 、 和 这里 使用的单字母A
名称B
)R
。
变基操作——<code>git checkout branch2;git rebase branch1——选择底行的所有非合并提交并复制它们。副本在 commit 之后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
由于R
、G
和B
不容易找到,我们可以完全停止绘制它们:
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
(commit I
) 开始并向后工作来实现。
如果您现在选择branch3
与branch1
orbranch2
合并,则通过从两个分支提示开始并像往常一样向后工作来找到合并基础。在这种情况下,合并基础是我们通过从提交向上和向左到达的提交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 合并两个,提交结果,与下一个合并,提交结果,依此类推。
推荐阅读
- ios - How to read a text file while developing an ios app using xcode
- scala - 使用 Java api 使用 Spark 从 parquet 读取/访问原始双数组
- angular - 模板驱动形式的 Angular Material Mat-Chip 验证
- python - 如何在tensorflow中获取矩阵中每一行的最大值和次最大值?
- python - 如何仅绑定按键而不是在 Tkinter 中按住键时
- python - Python - 在不停止运行 tkinter mainloop 的情况下中止当前函数
- python - 在原子中运行python
- android - android.provider.Telephony.SMS_RECEIVED 无法在 OREO 中接收。我该如何解决这个问题?
- python - TypeError: __init__() 得到了一个意外的关键字参数“missing_vaules”
- android - 为什么 Flutter 只能以超级用户身份运行?