变基结果与合并结果有何不同?
How rebase result may differ from result of a merge?
在 GitHub 的一篇文章中,我阅读了以下内容:
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.
我不清楚 rebase 如何产生与合并不同的结果。
谁能解释一下这怎么可能?
Link转原文:
https://help.github.com/articles/about-pull-request-merges/
这里是变基和合并产生不同结果的案例的构造证明。我想这就是他们正在谈论的情况。 编辑:当合并分支时,可能会发生另一种情况,其中要变基的侧分支已经或合并包含一个将被跳过的提交(由于补丁 ID 匹配)在变基期间,然后是该提交的还原(不会被跳过)。参见 如果我以后有时间,我也会尝试为该示例添加构造证明。
诀窍在于,由于 rebase 复制提交但忽略合并,我们需要删除一个合并,其解决方案不是其前身的简单组合。为了让这个合并没有冲突,我 认为 它必须是一个 "evil merge",所以这就是我在脚本中输入的内容。
我们构建的图表如下所示:
B <-- master
/
A--C--E <-- branch
\ /
\ /
D <-- br2
如果你在 master
(你的提示提交是 B
)并且你 git merge branch
,这结合了差异 A
-vs-[=15 的变化=] 与来自差异 A
-vs-E
的那些。结果 graph 是:
B-----F <-- master
/ /
A--C--E <-- branch
\ /
\ /
D <-- br2
和提交F
的内容由A
、B
和E
的内容决定。
如果你在branch
(你的提示提交是E
)并且你git rebase master
,这会复制提交C
和D
,在某些情况下顺序(不清楚是哪个)。它完全省略了提交 E
。结果 graph 是:
B <-- master
/ \
A C'-D' <-- branch
\
D <-- br2
(原来的C
和E
只能通过reflogs和ORIG_HEAD
获得)。以快进方式移动 master
,master
的提示变为提交 D'
。 commit D'
的内容是通过将从 C
和 D
提取的更改添加到 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
在 GitHub 的一篇文章中,我阅读了以下内容:
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.
我不清楚 rebase 如何产生与合并不同的结果。
谁能解释一下这怎么可能?
Link转原文: https://help.github.com/articles/about-pull-request-merges/
这里是变基和合并产生不同结果的案例的构造证明。我想这就是他们正在谈论的情况。 编辑:当合并分支时,可能会发生另一种情况,其中要变基的侧分支已经或合并包含一个将被跳过的提交(由于补丁 ID 匹配)在变基期间,然后是该提交的还原(不会被跳过)。参见
诀窍在于,由于 rebase 复制提交但忽略合并,我们需要删除一个合并,其解决方案不是其前身的简单组合。为了让这个合并没有冲突,我 认为 它必须是一个 "evil merge",所以这就是我在脚本中输入的内容。
我们构建的图表如下所示:
B <-- master
/
A--C--E <-- branch
\ /
\ /
D <-- br2
如果你在 master
(你的提示提交是 B
)并且你 git merge branch
,这结合了差异 A
-vs-[=15 的变化=] 与来自差异 A
-vs-E
的那些。结果 graph 是:
B-----F <-- master
/ /
A--C--E <-- branch
\ /
\ /
D <-- br2
和提交F
的内容由A
、B
和E
的内容决定。
如果你在branch
(你的提示提交是E
)并且你git rebase master
,这会复制提交C
和D
,在某些情况下顺序(不清楚是哪个)。它完全省略了提交 E
。结果 graph 是:
B <-- master
/ \
A C'-D' <-- branch
\
D <-- br2
(原来的C
和E
只能通过reflogs和ORIG_HEAD
获得)。以快进方式移动 master
,master
的提示变为提交 D'
。 commit D'
的内容是通过将从 C
和 D
提取的更改添加到 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