Git帮助理解合并基础冲突

13

我在未修改的文件上遇到了冲突。合并时出现了只有一行更改的文件的冲突。我对可能发生的情况有所了解,但仍无法解决问题。

以下是分支结构。

  • 主分支
    • 当前迭代
      • mapr_autoinit

主分支很旧,很长一段时间以来没有从当前迭代进行过更改。但是,主分支已经通过将其他分支直接基于主分支进行rebase而进行了直接的更改。因此,技术上说,主分支和当前迭代已经分叉。我们始终从当前迭代进行分支和标记。我遇到的问题是,当我将current_iteration合并回我的分支时,出现的冲突比根据对current_iteration的更改应该发生的要多得多。我能够从current_iteration执行git diff / git apply,并且它会干净地执行。

当我在mapr-autoinit上运行git show-branch --merge-base时,我看到提交实际上是几个月前在主分支上完成的。但是当我检查git merge-base current_iteration mapr_autoninit时,我发现版本非常新,并且很可能不会有冲突。

根据我最近几天的观察,如果我把主分支合并到当前迭代分支、提交然后再将当前迭代分支合并回主分支,这可能会解决我的分支合并基础指向更近版本的问题。

另外,我是否可以停止跟踪主分支?我尝试使用git config --unset branch.master.merge; git config --unset branch.master.remote来停止跟踪主分支,但这没有解决我的问题。

这个问题是否是因为主分支和当前迭代分支已经分叉,而合并正在尝试通过重放整个日志来协调合并的问题?

此外,以下是更多细节。现在我正在开一个新问题,因为问题变得更加具体。我会更新两个问题,并提供有效答案或删除较旧的问题,取决于哪个更好。

Git合并导致不合理的冲突

1个回答

16

我个人从未觉得git show-branch的输出很有用,也不确定它的使用意图以及其他人如何成功地使用它。

同时,我是否可以停止跟踪master分支...

这是无关紧要的。重要的是实际提交记录。任何分支的上游都独立于提交记录。

这是一个问题吗,其中master和current_iteration分支已经分叉,并且合并正在尝试通过回放整个日志来协调合并?

不是。在找到合并基之后,重要的是仅有的内容是合并基提交,也就是两个分支端点提交加起来。中间的所有内容都是无关紧要的。

但是当我检查git merge-base current_iteration mapr_autoinit时,我发现版本非常新,很可能不会产生冲突。

这通常是开始的方法,因为git merge-base检查提交历史,并使用它计算合并基提交。请注意,默认情况下,git merge运行等效于git merge-base --all。如果这产生了多个合并基(多个提交哈希值),则您有一种特殊情况。现在假设您没有,但是检查一下是个好主意。

找到合并基提交后,您可以查看git log输出(参见最后一节),并查看合并基如何与两个分支端点相关联。Git并不关心这个问题,好吧,不完全正确:在Git找到合并基之后(这取决于此),Git停止关心它,但是您可能会发现它有帮助。

找到current_iterationmapr_autoinit的(单个)合并基(并假设您位于其中一个分支上)后,Git将执行以下操作:

git diff --find-renames <merge-base-hash> current_iteration > /tmp/1
git diff --find-renames <merge-base-hash> mapr_autoinit > /tmp/2

为了方便多次查看和并排查看,我将它们重定向到这里的/tmp文件。由于我不知道您从哪里合并——从哪个版本到哪个版本——所以我只是对这两个文件进行了编号,而不是称它们为“我们的”和“他们的”。请注意,在“合并更改”步骤中,情况基本上是对称的。对于每个文件的每个更改,有四种可能性:

  • 您在修改这些行而他们没有:Git会接受您的更改。
  • 他们在修改这些行而您没有:Git会接受他们的更改。
  • 您和他们都修改了这些行,但您做出了相同的更改:Git会采用一份更改副本。
  • 您和他们都修改了这些行,但是做出了不同的更改:Git会声明合并冲突,并将两组更改放入工作树文件中。如果您设置了merge.conflictStylediff3——我强烈建议这样做——Git还会包括合并基础版本,以显示您和他们在开始之前做出的更改。

通常情况下,通过将合并冲突标记和基础版本包含在内,使用merge.conflictStyle设置为diff3即可满足需求。(特别是在某些情况下,Git会错误地将更改配对,并认为它们存在冲突,而实际上这些更改是针对不冲突的其他区域做出的。)如果不行,可以查看提交历史。

有关提交历史的更多信息

git log --graph的输出结果通常会起作用,但很难理解。1建议使用--decorate --oneline选项,并列出当前分支2和您打算合并的分支的名称。例如,如果您已经检出了mapr_autoinit并打算运行git merge master,则可以运行:

git log --graph --decorate --oneline mapr_autoinit master

Git会尽其所能地绘制 带有可选颜色的ASCII艺术风格的提交图。当然,结果在很大程度上取决于图形中的内容。这是从Linux内核的Git仓库中提取的片段:

* 1ffaddd029c8 (HEAD -> master, tag: v4.18-rc8, origin/master, origin/HEAD) Linux 4.18-rc8
*   a8c199208cd6 Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
|\  
| * 1b3a62643660 x86/boot/compressed/64: Validate trampoline placement against E820
* |   2f3672cbf9da Merge branch 'timers-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
|\ \  
| * | 0a0e0829f990 nohz: Fix missing tick reprogram when interrupting an inline softirq
| * | 80d20d35af1e nohz: Fix local_timer_softirq_pending()
* | |   0cdf6d4607df Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

可以使用这个来追踪自合并基准以来发生了什么。还有更多的工具:

git log --graph --decorate --oneline --boundary mapr_autoinit...master

例如,这将向您展示什么没有合并(标记为*的提交),以及由于--boundary而合并的边界提交(标记为o),但不会比此更深入地进入图形。

对于非常简单的情况——没有内部分支和重新合并的图形——在三点语法中添加--left-right有时也很有帮助。 对于复杂的情况,每个腿上都有许多内部分支和合并操作,--first-parent有时很有用,--simplify-by-decoration有时很有用。


1尽管如此,它仍然值得一读。 如果您愿意,可以使用绘制图形更漂亮的GUI或类似工具,但请注意,至少某些Git GUI似乎会绘制不正确的图形(咳咳各种网络服务咳咳)。

2您始终可以使用大写字母的名称HEAD代替当前分支名称。 在Windows和MacOS等不区分大小写的文件系统上,全小写字母也起作用,但养成这种习惯并不好。 在任何现代Git中,单独的at符号@都表示HEAD,如果您愿意,请使用它代替拼写出HEAD。 请注意,在使用两个或三个点语法(A..BA...B)时,完全省略一个名称也意味着HEADHEAD..B@..B..B是同一事物的三种拼写方式。


我似乎在一个人的合并方面遇到了问题。看起来他的合并导致了我的问题,但我不明白它是如何发生的。分支 origin/feature/predict-proxy-plot-endpoints 似乎创建了一个疯狂的合并高原。我正在努力理解这是如何发生的。 - Chris Hinshaw
当我们从某个源重复合并时,就会得到这种构造,而不是仔细地关注图形(至少在这方面不关心),重新基于(即复制)提交以使它们线性化并且易于理解给未来的代码考古学家。有些人喜欢这种东西 - 例如,Git本身的Git存储库,但在我工作的地方,在过去的8年中,我一直试图保持事情更可管理... - torek
请注意,谁合并代码就有(尽可能多的)控制权来决定最终提交的树。有些人会做出所谓的“恶意合并”,因为最终合并提交中包含了两个输入“侧面”都没有的内容(与合并基线相比)。这可能是意外发生的,也可能是故意为之,有时候尽管被称为“恶意合并”,但实际上是一件好事。 - torek
你还可以尝试重复合并操作,以了解它的原因。由于合并取决于合并基础和两个特定提交,您可以检出(作为分离的HEAD)其中一个提交(现有合并的两个父提交之一),然后运行git merge <hash-of-other-commit>,看看Git如何合并它们。如果结果与您感兴趣的合并不匹配,则执行合并的人必须使用了额外的选项或进行了邪恶合并等操作。 - torek

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