我参与了GitHub上的一个开源项目。创建了一个pull请求,由维护者审核,他要求我重做一些东西。与此同时,其他好人也做出了贡献。首先,我使用git rebase master
在最新的master
分支上重新设置我的pull请求,然后使用git rebase -i HEAD~5
,交互式rebase功能来修复我的一些提交,然后{{1}到我自己的远程分支。
然而,在那之后,由于冲突,GitHub认为我的分支不能再合并到git push --force
分支。实际上,它认为我的分支中添加了一些其他提交(不是我的),这显然与master
中的相同提交冲突。
我做错了什么,我怎么能做到正确?
假设master
分支具有以下历史记录
master
然后我提交了(master) A -> B -> C -> D
,X
和Y
,所以我的分支历史记录是:
Z
我们还假设有人在此期间承诺并将(my-branch) A -> B -> C -> D -> X -> Y -> Z
推送到E
:
master
首先,我在(master) A -> B -> C -> D -> E
上git rebase master
。在此之后我的分支历史是:
my-branch
然后我这样做(my-branch) A -> B -> C -> D -> E -> X -> Y -> Z
:
git rebase -i HEAD~5
请注意,我不会更改pick D
pick E
pick X
sqash Y
edit Z
或D
在此之后我的历史看起来像:
E
然后我(my-branch) A -> B -> C -> D -> E -> X -> Z
。
在此之后,GitHub声称我的拉取请求无法再合并,因为我的分支上的提交git push myremote my-branch --force
,C
和D
与E
冲突,{ {1}}和C
D
。请注意,我没有编辑这些提交,只有我自己的(并且E
甚至不是交互式rebase的一部分)。
为什么master
表现得像这样?
如何避免git交互式rebase搞乱拉取请求?
答案 0 :(得分:1)
你必须对交互式rebase(或任何rebase,实际上,但它往往更多地出现交互式rebase,因为它非常灵活)有点小心。
更详细地考虑提交图可能会有所帮助。具体来说,这种绘图本身就有些错误:
(master) A -> B -> C -> D
将它画成以下内容要好得多:
A <-B <-C <-D <--master
这里的关键项目是将分支名称放在右边,并带有一个箭头。其余的箭头可以转换为直接链接:
A--B--C--D <-- master
因为所有提交都是只读的。绘制内部箭头很困难,因此知道它们确实无法改变,我们可以将它们绘制为连接线。吸引他们的原因,至少在最初,是箭头连接到孩子,而不是父母:孩子们知道他们的父母提交的是谁 - D
知道要回来例如,C
,但父母不知道他们的孩子是谁。无法从A
转到B
;我们只能从B
返回A
,然后意识到嘿,我们从B
来到这里,所以必须有一条从A
到{{1}的路径毕竟。
这是使用Git时的一个关键实现: 它可以向后执行所有操作。
所有这些重要的原因都与并行开发过程中发生的事情有关。如您所说,您制作了B
,X
和Y
:
Z
您的A--B--C--D <-- master
\
X--Y--Z <-- my-branch
了解X
,但D
- 您与其他几个Git存储库共享 - 对您的D
一无所知。
与此同时,其他人提出X
,这也指向E
。如果我们想象某种类似神圣的超级存储库,它们在某种程度上同时知道D
和 E
,我们可以这样绘制它:
X-Y-Z
然后他们 - &#34;他们&#34;这里是GitHub上的开源组织 - 将这个第三人 E <-- third-person
/
A--B--C--D <-- master
\
X--Y--Z <-- my-branch
收录到他们自己的E
中:
master
(或许,他们仍然不知道你的 E <-- master, third-person
/
A--B--C--D
,所以我们只是将它们从图纸中取出来。现在,您运行X-Y-Z
到本地计算机以获取GitHub存储库版本,并在您的存储库中运行 仍然拥有git fetch upstream
,有:
X-Y-Z
(我假设您的 E <-- upstream/master
/
A--B--C--D <-- master, origin/master
\
X--Y--Z <-- my-branch (HEAD), origin/my-branch
代表您的 GitHub分叉,而您的origin
代表您从中创建分支的原始GitHub存储库。
当您运行upstream
,或者让git rebase upstream/master
点提交master
并运行E
时,您的Git会让副本提交git rebase master
,X
和Y
:
Z
(此图假设您没有费心更新 X'-Y'-Z' <-- my-branch (HEAD)
/
E <-- upstream/master
/
A--B--C--D <-- master, origin/master
\
X--Y--Z <-- origin/my-branch
;如果有,我们可以稍微区别一点。)
这些副本 - 标有刻度线的那些副本master
- 已准备就绪,可以强制推送到你的前叉。假设你在这一点上这样做,你的分叉将拥有它们,原来的X'-Y'-Z'
链将被放弃:
X-Y-Z
如果我们同时更新您自己的 X'-Y'-Z' <-- my-branch (HEAD), origin/my-branch
/
E <-- upstream/master
/
A--B--C--D <-- master, origin/master
\
X--Y--Z [abandoned]
和master
,我们也可以将所有这些重绘为更简单的方法:
origin/master
(所有这些都在你的计算机上的你的存储库中 - 在GitHub上,在你的分支中,这些只是命名为A--B--C--D--E <-- master, origin/master, upstream/master
\
X'-Y'-Z' <-- my-branch (HEAD), origin/my-branch
和{{1}没有master
部分,根本没有my-branch
个部分。
您的拉取请求将自动更新为使用新的origin/
提交,这些提交只需添加到提交upstream/
- 所以如果他们的存储库仍然以提交{{}结束1}},它们应该没有问题吸收你的提交。
如果您希望将X'-Y'-Z'
压缩到E
并使用E
的消息执行某些操作,您现在可以使用交互式rebase执行此操作。或者,您可以执行所有而不是初始rebase:
Y'
请注意X'
这里的确意味着&#34;提交Z'
&#34;在我们的图表中,其中一个$ git checkout my-branch
$ git rebase -i upstream/master
为upstream/master
,E
或E
为upstream/master
。
Git现在将列出Z
上但未在Z'
上的三个提交,即从my-branch
或my-branch
开始并向后工作upstream/master
,列出这些提交,然后从该列表中删除,所有提交都从Z
开始并向后工作。这三个提交&#39;哈希ID将进入三个Z'
命令。
您现在可以像以前一样更改一个壁球和一个进行编辑:因为您在提交中仅 ,而不是其他任何人,您赢了&#39 ;复制任何其他人的提交到新的和不同的提交。当您完成编辑后,如果您已将A
复制到E
,将pick
复制到X
,并X'
,则会再次进行新的提交} Y
,现在您Y'
+ Z
(被压扁),Z'
X"
的编辑版本X'
但是连接到Y'
:
Z"
您现在可以强制将这些推送到您的分支,以便他们的Z'
(您在此处的Git存储库中调用X"
)更改为指向提交 X"-Z" <-- my-branch (HEAD)
/
A--B--C--D--E <-- master, origin/master, upstream/master
\
X'-Y'-Z' <-- origin/my-branch
,并且现在您的分叉会再次更新,您的拉取请求会自动更新,您仍然可以(仍然或最终真实地)让他们看到my-branch
和origin/my-branch
。
答案 1 :(得分:0)
一个想法是在命令行上指定合并策略,以便始终通过从上游存储库中进行更改来解决冲突。
git rebase -s recursive -X ours master
虽然它不直观,但是我们的#34;和#34;他们的&#34;在变革期间变得逆转。因此,这将解决使用合并的上游更改的任何冲突。要在变革期间总是接受你的改变,通过&#34;他们的#34; (更多细节:https://git-scm.com/docs/git-rebase#git-rebase---strategy-optionltstrategy-optiongt)
使用交互式rebase来清理任何历史记录,强行推送到你的分支......在PR期间不应该从GitHub的角度出现任何冲突。