首页 > 解决方案 > 轻松自动合并出错

问题描述

我合并了两个分支:

我合并report进去master,结果是:线条消失了。

           1  2  3  4  5  6  7  8  9  10
master: ---O--O--O-----O--O-----O--O--O----
report: -------\----O--------O-------/ 
    changes on master__^              ^__merge that silently removes changes

为什么?我可以信任git merge吗?


可能相关的其他信息:

标签: gitgit-merge

解决方案


这不是真正的答案(应该是评论),但它需要格式化并且不适合评论空间。相反,这是关于如何找到答案的说明,或者至少想出正确的输入得出答案。

当你运行时:

git checkout master
git merge report

Git 首先找到一个合并基础提交。很少——可能不是这里的情况——Git 发现不止一个合并基础提交,但我们会确保不是这种情况。

假设这个合并会出错,但是——重要的是——<em>还没有完成。如果它已经完成,我们需要一个技巧,或者一个尚未完成的单独存储库:任何一个都足够了。诀窍是创建一个的分支,而不是命名master,它指向master在合并之前将指向的提交:

git checkout -b test-the-merge <hash-ID>

(我们可以稍后丢弃这个分支。或者,我们甚至可以根本不创建它,使用另一种技巧,但为简单起见,使用测试分支更容易。)

现在我们处于尚未完成合并的状态,我们首先运行:

git merge-base --all HEAD report

理想情况下,这会产生一个哈希 ID。如果它产生多个哈希 ID,我们会遇到一种罕见的情况,即有多个合并基,我们需要一个我将省略的过程,因为它很长而且很无聊。:-)

现在我们知道了作为合并基础的一个哈希 ID,下面是当 Git 进行合并时我们如何看到 Git 将看到的内容:

git diff --find-renames <hash> HEAD     # what does Git think *we* changed?
git diff --find-renames <hash> report   # what does Git think *they* changed?

您可能希望将这两个git diffs 发送到文件中,以便您可以在闲暇时和/或并行阅读它们。如果有很多正确合并的差异并且您只想关注未正确合并的特定文件,您还可以将输出限制为特定文件。

请注意,git diff发现的不一定是某个人所做的更改。找到的是产生相同效果git diff的最小指令集。这通常足够好。例如,假设在左侧提交(合并基础,由哈希 ID 指定)和右侧提交之间,有人删除了冗余段落的第二行第一个单词:the

Paris in
the
the
spring

而 Git 选择生成指令:删除第二the行(第三行)。

有关系吗?可能不会——但有时会。如果左右两边分别读:

some vaguely C like code {
    with redundant stuff
}
more vaguely C like code {
    with redundant stuff
}

和:

some vaguely C like code {
    with redundant stuff
}
still vaguely C like code {
    with redundant stuff
}
more vaguely C like code {
    with redundant stuff
}

和 Git 错误地在右大括号上同步并产生语法上无效的差异?好吧,即使这样通常仍然有效,但有时会产生不适当的冲突。在极少数情况下(尽管确实会发生但很难说明),您可能会错过本发生的冲突,因为 Git 提供了语法正确但语义错误的最小编辑。

在任何情况下,Git 现在所做的,已经产生了两个差异列表,就是想出合并的源文件集。为此,Git 从所有文件的合并基础版本开始。然后:

  • 对于您、他们或两者都更改的每个文件...

    • 你改过文件吗?他们没有更改文件吗?如果是这样,把你的文件。

    • 他们是否更改了文件,而您没有更改它?如果是这样,请拿走他们的档案。

    • 否则,你们俩都更改了文件。尝试通过差异逐行合并更改。无论您在哪里添加了一些线条,都可以使用您添加的线条。无论他们在哪里添加了一些,都使用他们添加的行。无论您或他们在何处删除了某些行,请进行删除。如果你们都对完全相同的进行了完全相同的更改,请复制一份更改。如果您对同一行或相邻(相互接触)或到达文件末尾的行进行了不同的更改,请声明合并冲突。

  • 对于您或他们完全删除、创建为全新或重命名的文件,请执行适当的操作。(究竟什么是合适的意思会变得复杂,在这里似乎不是你的情况,所以我不会深入探讨。)

将所有内容尽其所能地组合在一起,Git 现在:

  • 因合并冲突而停止,或
  • 停止,因为你告诉它(--no-commit例如),或者
  • 进行合并提交,因为一切似乎都很顺利。

如果 Git 进行了合并提交——或者如果它停止并让你提交并且你确实成功了——合并提交将你当前分支的旧提示提交作为它的第一个父项——新提示提交是合并提交本身——并且合并commit 将另一个分支的提示提交作为其第二个父级。也就是说,在此合并之后mastertest-the-merge(无论我们在哪个分支上),我们有:

git rev-parse <branch>^1   => hash ID of the previous branch tip
git rev-parse <branch>^2   => same hash ID as git rev-parse report

您可以查看两个输入,来自三个提交的两个差异 - 合并基础,HEAD以及他们的 / 提示 -report以查看 Git 看到的内容,这将解释 Git 进行合并的原因。

无论如何,这就是这样git merge做的:它找到一个合并基础,制作两个差异,然后组合差异并将组合的差异应用于合并基础以得出合并结果。(这当然忽略了所有特殊情况,例如由于各种原因而没有实际合并:-s ours,或快进,或不相关的历史,或合并冲突等)差异本质上是面向行的,无法重建某人的内容真的做到了:他们只产生一组指令,会产生相同的最终文本。结合这些指令往往适用于大多数编程语言,但绝对不是完美的。

如果该过程适用于您的文件,您可以信任它。如果该过程不适用于您的文件,则您不能信任它,并且您必须仔细检查 - 并在需要时更正 - 每次合并。


推荐阅读