我使用我的本地分支feature
为github repo创建一个PR(我没有写入权限)。后来我决定把它的最后一次提交分成一个独立的PR,所以我把feature
提交了一个提交:
git checkout feature
git branch feature2
git reset --hard @~
git push -f
第一个PR合并到上游,所以现在我要创建第二个PR:
git checkout master
git pull upstream master
git push origin
git checkout feature2
git rebase master
不幸的是,git
缺少feature
合并到master
的信息。因此,它没有意识到最近的feature2
和master
的共同基础非常接近:它只是feature
。相反,rebase
一直追溯到feature
和master
的共同基础,就好像它们从未被合并一样。结果,git rebase master
变得不必要地混乱。
为什么Github
会丢失feature
通过上游PR合并到master
的信息?有没有办法向Github
提供这些信息?
最后,我不得不诉诸:
git checkout master
git checkout -b feature2_new
git cherry-pick feature2
幸运的是,我只需要处理一次提交。即使只有一次提交,我认为与真实基础的合并(如果git知道它)将比cherry-pick
更好,因为git
将能够使用其历史知识来解决更多冲突自动。
请注意,如果我在本地将feature
合并到master
而不是执行github PR,则不会丢失任何信息。当然,那么我的master
将不会与上游回购同步,所以这将毫无意义。
答案 0 :(得分:6)
Github现在支持3 techniques合并拉取请求:
只有常规的合并才能保留我的本地提交是PR合并为master的一部分的知识。如果使用它,我就不会遇到我在问题中描述的问题。
另外两种技术失去了这些知识 - 我没有办法追溯地创建它(不修改上游主机)。这是为更简单的历史付出的代价。
直观地,为了让git
知道上游主提交U
与我的本地提交L
相关,需要有一个指向U
的箭头到L
。
从概念上讲,有两种方法可以实现这一目标。
首先,U
可以有两个父项:一个将其连接到L
,另一个将它连接到上游master
上的所有先前提交。这正是Github 合并技术的作用。
其次,如果U
已经指向上游L
上的所有先前提交,则L
可以master
作为其唯一父级。 Github可以通过允许快速使用 merge 技术来支持这一点,但它选择不这样做。
如果Github PR与压缩和合并或 rebase and merge 合并,则在上游主服务器上创建的所有提交只有一个父项;它们和我当地的提交之间没有箭头。
编辑:
此外,我现在相信,我所询问的历史遗失首先并不是什么大不了的事。 IIUC,如果git cherry-pick
通过常规合并提交连接到git rebase
,我与master
会遇到的冲突实际上与feature2
的冲突相同。如果我将超过1次提交拆分为独立公关,那么也可以选择would handle that easily。
答案 1 :(得分:2)
你的困境的根本原因是当feature
的拉取请求(回滚一次提交的功能分支)完成时,它会导致合并提交进入{ {1}}。以下是显示master
拉master
拉请求完成后feature2
和feature
的外观图表:
master
在这里,我们可以看到master: ... A -- B -- C -- M
\
feature: D
\
feature2: E
在提交feature
时从master
分支出来,C
只是feature2
的延续,只有一次额外的提交。合并提交feature
位于M
的顶部,它代表所有在master
中完成的额外工作。请注意,这是合并提交,因此与feature
的历史记录无关。
接下来,您在feature2
上运行了以下feature2
的rebase:
master
此次变基后,git checkout feature2
git rebase master
将如下所示:
feature2
请注意,合并提交仍然是历史记录的一部分。尽管从功能上讲它似乎没必要,因为提交feature2: ... A -- B -- C -- M -- D' -- E'
包含进行合并提交所需的所有内容,但仍会出现此提交。
如果您想知道如何避免这种情况,可以选择将D
的历史保留为线性。缺陷是pull请求,它以合并提交结束。如果您已经master
直接在feature
之上播放了提交,那么您就不会遇到此问题。请考虑以下命令:
master
然后,快速合并git checkout feature
git rebase master
与master
:
feature
这会让git checkout master
git merge feature
和master
看起来像这样:
feature2
现在,如果您要将master: ... A -- B -- C -- D
feature2: ... A -- B -- C -- D -- E
合并到feature2
,Git只会播放master
提交,而不是回到E
和{master
的原始点。 {1}}分歧。
答案 2 :(得分:1)
通常是的。但是git通过查看历史来回答这个问题,而不是改变。如果你使用Github的壁球(即核武历史),那么你就丧失了利用这段历史的能力;即git能够检测该历史的一部分是否已存在于上游。
以Tim Biegeleisen创建的图表为基础:
master: ... A -- B -- C -- M
\ /
feature: D
\
feature2: E
当您将feature2重新设置为master时,您应实际查看此历史记录:
master: ... A -- B -- C -- M
\ / \
feature: D \
\
feature2: E'
因为rebase永远不会重新创建目标中已存在的提交。它会知道D已经在主人的历史中了。
当您在master上重新设置feature2时,您会看到在master中已经存在逻辑上的提交。这只能在以下情况中发生。
在合并之前,您将重写功能中的一些提交:
D' feature
/
... A -- B -- C
\
D
\
E feature2
执行合并:
D' feature
/ \
... A -- B -- C --- M master
\
D
\
E feature2
然后尝试在master上重新定位feature2:
D' feature
/ \
... A -- B -- C --- M master
\
D''
\
E' feature2