变基结果与合并结果有何不同? [英] How rebase result may differ from result of a merge?

查看:23
本文介绍了变基结果与合并结果有何不同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

<块引用>

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

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

谁能解释一下这怎么可能?

<小时>

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

解决方案

这里有一个构造证明,其中 rebase 和 merge 产生不同的结果.我认为这就是他们正在谈论的情况.编辑:当合并分支时,可能会发生另一种情况,其中要重新定位的侧分支已经或合并包含一个在重新定位期间将被跳过的提交(由于补丁 ID 匹配),然后是该提交的还原(不会被跳过).请参阅合并不保留对文件的更改,为什么?如果以后有时间我会尝试添加构造证明对于那个例子也是如此.

诀窍在于,由于 rebase 副本提交但省略了合并,我们需要删除其解析不是其前辈的简单组合的合并.为了让这次合并没有冲突,我认为它必须是一个邪恶合并",所以这就是我放入脚本中的内容.

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

 B <-- 大师/A--C--E <-- 分支//D <-- br2

如果你在master(你的tip commit是B)并且你git merge branch,这结合了diffing A-vs-B 与那些来自不同的 A-vs-E.结果是:

 B-----F <-- 大师//A--C--E <-- 分支//D <-- br2

和commit F的内容由ABE决定.

如果你在 branch(你的提示提交是 E)并且你 git rebase master,这将复制提交 CD,按某种顺序排列(不清楚是哪个).它完全省略了提交 E.结果是:

 B <-- 大师/A C'-D' <-- 分支D <-- br2

(原始的 CE 只能通过 reflogs 和 ORIG_HEAD 获得).以快进的方式移动 mastermaster 的尖端变成 commit D'.提交D'的内容是通过将从CD中提取的变化添加到B中来确定的.>

由于我们使用了 "evil merge"E 中出现的更改进行了更改无论是 C 还是 D,这些变化都消失了.

这是产生问题的脚本(注意,它创建了一个临时目录 tt 并将其保留在当前目录中).

#!/bin/sh致命的() {回声致命:$@"1>&2;出口 1}[ -e tt ] &&致命 tt 已经存在mkdir tt&&cd tt & &git init -q ||致命无法创建 tt 存储库回声自述文件 >自述文件&&git add README &&git commit -q -m A ||致命Agit 分支||致命无法进行分支主人的回声>bfile &&git add bfile &&git commit -q -m B ||致命Bgit checkout -q -b br2 分支 ||致命结帐 -b br2 分支C > 的回声文件cfile &&git add cfile &&git commit -q -m C ||致命Cgit checkout -q 分支 ||致命的结帐分支D > 的回声文件dfile &&git add dfile &&git commit -q -m D ||致命Dgit merge -q --no-commit br2 &&git rm -q -f cfile &&git commit -q -m E ||致命Egit 分支 -D br2git checkout -q master ||致命的结账大师回声合并分支git merge --no-edit 分支 ||致命合并失败回声结果是:*echo 删除合并,替换为分支的 rebase 到 mastergit reset -q --hard HEAD^ ||致命重置失败git checkout -q 分支 ||致命切换回主服务器失败git rebase master ||致命变基失败回声结果是:*echo 也删除了 rebase,这样你就可以四处看看git reset --hard ORIG_HEAD

In one of GitHub's articles I read the following:

You aren't able to automatically rebase and merge on GitHub when: Rebasing the commits is considered "unsafe", such as when a rebase is possible without merge conflicts but would produce a different result than a merge would.

It isn't clear for me how a rebase may produce a different result than a merge.

Can anyone explain how is it possible?


Link to the original article: https://help.github.com/articles/about-pull-request-merges/

解决方案

Here's a construction proof of a case where rebase and merge produce different results. I assume this is the case they are talking about. Edit: There is another case that can occur, when merging branches where the side branch to be rebased has-or-merged contains one commit that will be skipped (due to patch-ID matching) during a rebase, followed by a reversion of that commit (that will not be skipped). See Changes to a file are not retained by merge, why? If I have time later I will try to add a construction proof for that example as well.

The trick is that since rebase copies commits but omits merges, we need to drop a merge whose resolution is not simple composition of its predecessors. For this merge to have had no conflicts, I think it must be an "evil merge", so this is what I put into the script.

The graph we build looks like this:

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

If you are on master (your tip commit is B) and you git merge branch, this combines the changes from diffing A-vs-B with those from diffing A-vs-E. The resulting graph is:

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

and the contents of commit F are determined by those of A, B, and E.

If you are on branch (your tip commit is E) and you git rebase master, this copies commits C and D, in some order (it's not clear which). It completely omits commit E. The resulting graph is:

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

(the original C and E are only available through reflogs and ORIG_HEAD). Moving master in a fast-forward fashion, the tip of master becomes commit D'. The contents of commit D' are determined by adding the changes extracted from C and D to B.

Since we used an "evil merge" to make changes in E that appear in neither C nor D, those changes vanish.

Here is the script that creates the problem (note, it makes a temporary directory tt that it leaves in the current directory).

#! /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

这篇关于变基结果与合并结果有何不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆