如何避免git交互式rebase搞乱拉取请求?

时间:2018-03-26 15:48:14

标签: git github merge version-control pull-request

我参与了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 XY,所以我的分支历史记录是:

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 --forceCDE冲突,{ {1}}和C D。请注意,我没有编辑这些提交,只有我自己的(并且E甚至不是交互式rebase的一部分)。

为什么master表现得像这样? 如何避免git交互式rebase搞乱拉取请求?

2 个答案:

答案 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时的一个关键实现: 它可以向后执行所有操作。

所有这些重要的原因都与并行开发过程中发生的事情有关。如您所说,您制作了BXY

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存储库。

没有交互式rebase

当您运行upstream,或者让git rebase upstream/master点提交master并运行E时,您的Git会让副本提交git rebase masterXY

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}},它们应该没有问题吸收你的提交。

使用交互式rebase

如果您希望将X'-Y'-Z'压缩到E并使用E的消息执行某些操作,您现在可以使用交互式rebase执行此操作。或者,您可以执行所有而不是初始rebase:

Y'

请注意X'这里的确意味着&#34;提交Z'&#34;在我们的图表中,其中一个$ git checkout my-branch $ git rebase -i upstream/master upstream/masterEEupstream/master

Git现在将列出Z上但未在Z'上的三个提交,即从my-branchmy-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-branchorigin/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的角度出现任何冲突。