有可能告诉Github我的分支被合并到上游主人吗?

时间:2017-04-21 15:00:40

标签: git github git-workflow

我使用我的本地分支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的信息。因此,它没有意识到最近的feature2master的共同基础非常接近:它只是feature。相反,rebase一直追溯到featuremaster的共同基础,就好像它们从未被合并一样。结果,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将不会与上游回购同步,所以这将毫无意义。

3 个答案:

答案 0 :(得分:6)

Github现在支持3 techniques合并拉取请求:

  • 合并:创建合并提交(无快进)+从PR分支获取所有原始提交
  • 壁球并合并:创建一次提交
  • 重新启动和合并:创建与PR分支一样多的提交,但它们已重新定位到主分区

只有常规的合并才能保留我的本地提交是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}}。以下是显示mastermaster拉请求完成后feature2feature的外观图表:

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