轻松进行自动合并会出错

时间:2019-07-20 14:42:16

标签: git git-merge

我合并了两个分支:

  • 分支master向文件config_dev.yml添加了行
  • 分支report没有碰到它(因此该文件没有冲突)。

我将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吗?


可能相关的其他信息:

  • 还有一个第三分支(未触及该文件),它从master上的提交3开始,被“合并”(实际上是一个空白合并,“仅我的”)到{{1 }}提交7

1 个答案:

答案 0 :(得分:0)

这并不是真正的答案(应该是评论),但是它需要格式化并且不适合评论空间。取而代之的是,这是有关如何找到答案的说明,或者至少是提出正确的输入内容以导致答案的方法。

运行时:

git checkout master
git merge report

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

可以说这次合并会出错,但是-重要的-还没有完成。如果已经完成,那么我们需要一个技巧或一个尚未完成的单独存储库:两者就足够了。诀窍是创建一个 new 分支(未命名为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 diff发送到文件,以便您可以随意和/或并行阅读它们。如果有很多差异可以正确合并,而您只想关注无法正确合并的特定文件,那么您也可以将输出限制为特定文件。

请注意,git diff发现的不一定是某人所做的更改。 git diff发现的是产生相同效果的最小指令集。通常这已经足够了。例如,假设在左侧提交(由哈希ID指定的合并基数)和右侧提交之间,有人删除了 second 行,即a的第一个单词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错误地在右花括号上同步,并产生语法无效的diff?好吧,即使通常仍然可以使用,但有时会产生不适当的冲突。在极少数情况下(尽管确实会发生,但很难说明),由于Git进行了最少的编辑,您可能 发生了应该发生的冲突在语法上是正确的,但在语义上是错误的。

无论如何,在生成了两个差异列表之后,Git现在要做的是提出合并后的源文件集。为此,Git从所有文件的 merge base 版本开始。然后:

  • 对于您更改过的每个文件,或它们都更改了...

    • 您更改文件了吗?他们没有更改文件吗?如果是这样,请获取您的文件。

    • 他们是否更改了文件,而您没有更改?如果是这样,请获取文件。

    • 否则,您都更改了文件。尝试组合更改,逐行通过差异。无论您在哪里添加了几行,都请添加几行。无论他们在哪里添加一些内容,都请添加他们的行。无论您或他们在哪里删除了几行,都请删除。如果您都对完全相同的行进行了完全相同的更改,请复制一份更改。如果您对同一行或邻接(相互触摸)或碰到文件末尾的行进行了不同的更改,请声明合并冲突。

  • 对于您或它们已被完全删除,创建为全新文件或重命名的文件,请执行适当的操作。 (恰恰是适当的意味着什么变得复杂,在这里似乎不是您的情况,所以我不再赘述。)

现在,Git已将所有内容尽力地结合在一起:

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

如果Git进行了合并提交,或者如果它停止并允许您进行合并,并且您确实进行了合并,则合并提交会将当前分支的旧提示提交作为其第一父提交,新提示提交本身就是合并提交,并合并提交具有另一个分支的技巧提交作为其第二父级。也就是说,在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进行了合并。

在任何情况下,这都是{em> git merge所做的事情:它找到一个合并基础,进行两个比较,然后组合这些差异并将组合的差异应用于合并基础合并结果。 (当然,这会忽略所有特殊情况,例如由于各种原因未进行实际合并:-s ours,快速前进,或不相关的历史记录或合并冲突等。)差异本质上是面向行的并且无法重建某人真正做到的事情:他们只会产生一组指令,这些指令将带有相同的最终文本。结合使用这些指令通常可以与大多数编程语言一起使用,但是绝对不是完美的。

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

相关问题