【问题标题】:GitHub PR squash causes erroneous merge conflictsGitHub PR squash 导致错误的合并冲突
【发布时间】:2019-07-05 06:51:47
【问题描述】:

我想知道这是否是一个 GitHub 错误,并想看看是否有其他人看到过这个问题。 我的 GitHub 存储库中有 masterdevfeature 分支。

流程通常如下所示:

master : A
dev    : A 
feature: A - B - C

然后我将feature 合并到dev 并删除feature 分支,但我不会压缩以保留所有提交历史:

master : A
dev    : A - B - C 

当我合并到 master 时,我会压缩合并以使其看起来更干净:

master : A - BC
               \
dev    : A - B - C 

我知道此时 BC 更像是一个单独的提交,但 master 的代码 BCdev 的代码 C 是相同的。

当我在上面的阶段比较masterdev 时,我注意到一个奇怪的问题;当我尝试再次将dev 合并到master 时,我看到了很多冲突,反之亦然。

无论我是将dev 合并到master 还是将master 合并到dev,冲突都不会改变;例如,如果 dev -> master 代码差异为 -a and +b,当我尝试合并 master -> dev 时,我会看到相同的差异,而不是 -b and +a

我们希望继续在 dev 分支上构建功能并保留其历史记录,因此无法删除它并基于 master 创建新分支。

我正在使用 GitHub 压缩和合并,因为其他成员需要批准更改才能合并到 master

谁能解释我为什么会看到这个问题以及如何解决它?

【问题讨论】:

标签: git github merge


【解决方案1】:

这幅画:

master : A - BC
               \
dev    : A - B - C 

暗示CBC 之间存在某种联系。

没有这样的链接,但是在AB 之间有一个链接(特别是从B 回到A)。所以更准确的绘图是:

A--D   <-- master
 \
  B--C   <-- dev

其中DC 具有相同的快照,但不相关,因此命名为D

这是整个问题的根源:

当我尝试再次将 dev 合并到 master 时,我发现很多冲突,反之亦然。

就Git而言,devmaster对应的点是commit A。因此,Git 必须获取从 AC 的差异,并将其再次应用于 master,在 Git 看来,它还没有 BC

我们希望继续在 dev 分支之上构建功能并保留其历史记录,因此无法删除它并基于 master 创建新分支。

在这种情况下,停止做 squash,因为 squash 有效地“杀死”了一个分支:你不应该对 B-C 提交做更多的事情。做一个真正的合并。在 GitHub Web UI 中,选择“合并”将执行此操作;从命令行,使用git merge --no-ff。然后,您将获得一个合并提交(我仍将其称为 D),而不是一个新的完全独立的提交 D

A------D   <-- master
 \    /
  B--C   <-- dev

当您在dev 上做更多工作时,您会得到:

A------D   <-- master
 \    /
  B--C--E--F   <-- dev

您现在可以进行另一个合并。这次不需要--no-ff,尽管它仍然可以正常工作;在 GitHub Web UI 上也没有什么可改变的。你会得到一个新的合并提交G:

A------D-----G   <-- master
 \    /     /
  B--C--E--F   <-- dev

第二次合并的输入是提交C作为合并基础,提交D作为--ours,提交F作为--theirs,所以合并将顺利进行。和以前一样,CD 中的快照将匹配,FG 中的快照将匹配。

如果您想严格从master 的角度来看发生了什么,请使用:

git log --first-parent master

这指示git log 只查看每个合并提交的 first 父级。也就是说,Git 将首先向您显示提交 Gmaster 指向它。然后它将从G 移动到它的第一个父D,并显示该提交。然后它将从D 移动到它的第一个父A 并显示该提交。由于根本没有更早的提交,git log 现在停止,其工作已完成。

(当然如果A之前有commit,git log会继续显示,但是这里所有的图都没有A之前的commit。)

【讨论】:

  • 感谢您的解释。快速提问,如果我们不关心保留每个提交历史记录,将feature 压缩到dev 然后简单地将dev 合并到master 可以帮助我们避免这个合并冲突问题吗?
  • 或者我们应该在合并到dev之前对feature执行git rebase -i?我们对master 分支有限制,它不能在没有同行评审的情况下合并,所以考虑避免使用 CLI 的方法。
  • 另外,从你的上图中,我知道 git 将 AC 进行比较,但它仍然感觉很有趣。当AD 链接并且它们使用相同的代码库时,为什么地球上无法将DC 进行比较?
  • 有很多可能的工作流程。我们现在使用的那个非常依赖 rebase 并且已经成为一个问题,但我们最近通过让我们的 Jenkins 测试花费 2 分钟而不是 40 分钟(使用 Jenkins v2 和预烘焙的 docker 图像)来解决这个问题。
  • 要解决比较问题,请参阅我关于 Git 实际执行方式的许多文章中的任何一篇 git merge。请注意,git merge --squashgit merge --no-ff 之间的主要区别在于前者总是在最后进行非合并提交,而后者总是在最后进行合并提交——即,记录的历史记录不同,即使对于每种合并,存储的快照都是相同的。
【解决方案2】:

如果您已经将基础分支压缩为 main/master,则解决方案是交互式地重新设置基础分支中的所有提交。

假设这是当前状态

master : A - BC
               \
dev    : A - B - C - D

在开发人员上执行rebase -i HEAD~3,将C 压缩为B。然后,D 的父提交将与 master 不同但相似。当您执行git rebase master 时,您不会遇到合并冲突。

重要
这种做法重写了 git 历史。这意味着您将不得不强制推送更改。永远不要在共享分支上这样做,因为它会破坏其他用户的 git 状态。考虑在 git-scm 中咨询 Rewriting History 以了解更多详细信息。这种做法对于单个开发人员正在处理的拉取/合并请求是安全的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-03
    相关资源
    最近更新 更多