【问题标题】:How rebase result may differ from result of a merge?变基结果与合并结果有何不同?
【发布时间】:2017-06-14 13:59:45
【问题描述】:

在 GitHub 的一篇文章中,我阅读了以下内容:

在以下情况下,您无法在 GitHub 上自动变基和合并:变基提交被认为是“不安全的”,例如当变基可能没有合并冲突但会产生与合并不同的结果时。

我不清楚变基如何产生与合并不同的结果。

谁能解释这怎么可能?


原文链接: https://help.github.com/articles/about-pull-request-merges/

【问题讨论】:

  • 合并通常会结合 2 个分支的工作,而 rebase 会回滚分支中的提交,切换头,然后在新头上重放这些提交。 Rebase 将重写历史,合并追加历史。
  • @castis 谢谢你的回答。不过,我了解这两个操作工作的基本细节。我问的是最终结果;代码将处于的状态。它如何取决于我执行的操作?
  • @castis 如果我们像您一样考虑结果,这两个操作将始终产生不同的结果。但是,我引用的这句话指出它们仅在某些情况下有所不同,并且在那些“某些”情况下,rebase 被认为是“不安全的”。
  • 好的,我看到他们已经分离出合并冲突的情况,并且他们正在讨论具有已定义合并基础的提交,因此只剩下一种可能性:重新设置包含他们自己的合并的提交链他们自己的决议。 (或者可能是两个:当合并基础是虚拟合并基础时重新设置基础。我认为这种情况没有无冲突的不同结果,但内部冲突是“隐藏的”,所以这里可能有一些路径。)
  • 截至今天,似乎至少有两种根本不相关的方式会导致这种情况发生——此时您必须开始怀疑是否还有其他方式。所以也许要问的最佳问题是:“GitHub 如何决定给定的 rebase-and-merge 操作是否受此条件约束?” (我猜只有 GitHub 可以回答。)除非他们实际执行这两种操作并比较结果,否则我怀疑他们能否始终如一地应用此规则

标签: git github merge rebase


【解决方案1】:

这里是 rebase 和 merge 产生不同结果的情况的构造证明。我想这就是他们正在谈论的情况。 编辑:还有另一种情况可能发生,当合并分支时,要变基的侧分支包含一个在变基期间将被跳过的提交(由于补丁 ID 匹配) ,然后是该提交的恢复(不会被跳过)。请参阅Changes to a file are not retained by merge, why? 如果以后有时间,我也会尝试为该示例添加构造证明。

诀窍在于,由于 rebase 复制提交但忽略了合并,我们需要删除一个合并,其解决方案不是其前任的简单组合。为了让这个合并没有冲突,我认为它必须是"evil merge",所以这就是我在脚本中输入的内容。

我们构建的图表如下所示:

  B   <-- master
 /
A--C--E   <-- branch
 \   /
  \ /
   D   <-- br2

如果你在master(你的提示提交是B)并且你git merge branch,这结合了来自差异A-vs-B的变化与来自差异A-vs-的变化E。结果图表是:

  B-----F   <-- master
 /     /
A--C--E   <-- branch
 \   /
  \ /
   D   <-- br2

而提交F的内容由ABE的内容决定。

如果你在branch(你的提示提交是E)并且你git rebase master,这个副本提交CD,以某种顺序(不清楚哪个)。它完全省略了提交E。结果图表是:

  B   <-- master
 / \
A   C'-D'   <-- branch
 \
  D   <-- br2

(原来的 CE 只能通过 reflogs 和 ORIG_HEAD 获得)。以快进方式移动mastermaster 的尖端变为提交D'。提交D' 的内容是通过将提取自CD 的更改添加到B 来确定的。

由于我们使用"evil merge"E 进行更改,而这些更改既未出现在C 中,也未出现在D 中,因此这些更改将消失。

这是产生问题的脚本(注意,它会在当前目录中创建一个临时目录tt)。

#! /bin/sh

fatal() {
    echo fatal: "$@" 1>&2; exit 1
}

[ -e tt ] && fatal tt already exists

mkdir tt && cd tt && git init -q || fatal failed to create tt repo

echo README > README && git add README && git commit -q -m A || fatal A
git branch branch || fatal unable to make branch
echo for master > bfile && git add bfile && git commit -q -m B || fatal B

git checkout -q -b br2 branch || fatal checkout -b br2 branch
echo file for C > cfile && git add cfile && git commit -q -m C || fatal C
git checkout -q branch || fatal checkout branch
echo file for D > dfile && git add dfile && git commit -q -m D || fatal D
git merge -q --no-commit br2 && git rm -q -f cfile && git commit -q -m E ||
    fatal E
git branch -D br2
git checkout -q master || fatal checkout master

echo merging branch
git merge --no-edit branch || fatal merge failed
echo result is: *

echo removing merge, replacing with rebase of branch onto master
git reset -q --hard HEAD^ || fatal reset failed
git checkout -q branch || fatal switch back to master failed
git rebase master || fatal rebase failed
echo result is: *

echo removing rebase as well so you can poke around
git reset --hard ORIG_HEAD

【讨论】:

  • 感谢您的解释!这很清楚。但是你能澄清一下“邪恶合并”是什么意思吗?据我了解,从您的回答来看,这是一个有冲突的合并,它是手动解决的,在解决冲突期间,用户向合并提交添加了一些代码。因此,当我们通过添加不属于合并分支之一的代码来解决冲突时,我们会进行邪恶的合并。对吗?
  • @VictorDombrovsky:是的;上面的“邪恶合并”短语也是一个链接,如果你点击它,就会带你到一个关于它的 StackOverflow 问题。 :-)(我只看到该字符串的第一次出现是一个链接;我会让它们都链接到它)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-06
  • 2021-12-19
  • 2015-01-19
  • 1970-01-01
相关资源
最近更新 更多