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

Ven*_*emo 3 git version-control merge github pull-request

我为 GitHub 上的一个开源项目做出了贡献。创建了一个拉取请求,维护者对其进行了审查,他要求我返工一些东西。与此同时,其他好人也做出了贡献。因此,首先我习惯于git rebase master在最新分支之上对拉取请求进行变基master,然后使用git rebase -i HEAD~5交互式变基功能来修复我的一些提交,然后再git push --force对我自己的远程分支进行变基。

然而,此后,GitHub 认为master由于冲突,我的分支无法再合并到该分支中。事实上,它认为我的分支中添加了一些其他提交(不是我的),这显然与master.

我做错了什么,我该如何纠正?

更多细节

假设该master分支有以下历史

(master) A -> B -> C -> D
Run Code Online (Sandbox Code Playgroud)

然后我提交XYZ,所以我的分支历史记录是:

(my-branch) A -> B -> C -> D -> X -> Y -> Z
Run Code Online (Sandbox Code Playgroud)

我们还假设有人同时承诺并E推动:master

(master) A -> B -> C -> D -> E
Run Code Online (Sandbox Code Playgroud)

所以首先我要做git rebase master的就是my-branch. 之后我的分支历史是:

(my-branch) A -> B -> C -> D -> E -> X -> Y -> Z
Run Code Online (Sandbox Code Playgroud)

然后我就git rebase -i HEAD~5喜欢这样:

pick D
pick E
pick X
sqash Y
edit Z
Run Code Online (Sandbox Code Playgroud)

请注意,我不会更改DE
之后我的历史看起来像:

(my-branch) A -> B -> C -> D -> E -> X -> Z
Run Code Online (Sandbox Code Playgroud)

那我就这么做了git push myremote my-branch --force

此后,GitHub 声称我的拉取请求无法再合并,因为我的分支上的提交CD、 和与、、上的E冲突。请注意,我没有编辑这些提交,只是我自己的提交(甚至不是交互式变基的一部分)。CDEmasterC

为什么会有git这样的行为?
如何避免 git 交互式 rebase 搞乱拉取请求?

tor*_*rek 5

您必须对交互式变基(或任何变基,实际上,但它往往更频繁地出现在交互式变基中,因为它非常灵活)小心一点。

\n\n

更详细地考虑提交图可能会有所帮助。具体来说,这种画法本质上是有些错误的:

\n\n
\n
(master) A -> B -> C -> D\n
Run Code Online (Sandbox Code Playgroud)\n
\n\n

最好将其绘制为:

\n\n
A  <-B  <-C  <-D   <--master\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里的关键是将分支名称放在右侧,并带有一个箭头。其余的箭头可以转换为直连杆:

\n\n
A--B--C--D   <-- master\n
Run Code Online (Sandbox Code Playgroud)\n\n

因为所有提交都是只读的。绘制内部箭头很困难,因此知道它们实际上无法改变,我们可以将它们绘制为连接线。绘制它们的原因(至少在最初)是箭头连接到子,而不是父级:子级知道其父级提交是谁\xe2\x80\x94D知道返回到C,例如\xe2\ x80\x94但是父母不知道他们的孩子是谁。没有办法从AB; 我们只能从Bback 到A,然后意识到,嘿,我们是从 到这里的,所以毕竟B一定有一条从A到 的路径。B

\n\n

这是使用 Git 时的一个关键认识:它会向后执行所有操作。

\n\n

所有这些都很重要的原因都与并行开发过程中发生的情况有关。正如你所说,你做了XY、 和Z

\n\n
A--B--C--D   <-- master\n          \\\n           X--Y--Z   <-- my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

X了解D,但D与其他几个 Git 存储库共享的 \xe2\x80\x94 对您的X.

\n\n

同时,其他人也提出了E,这也指向D。如果我们想象某种神一样的超级存储库,它以某种方式同时了解E 所有 X-Y-Z信息,我们可以这样画它:

\n\n
           E   <-- third-person\n          /\nA--B--C--D   <-- master\n          \\\n           X--Y--Z   <-- my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后他们\xe2\x80\x94“他们”在这里是GitHub\xe2\x80\x94上的开源小组采用了这个第三E人称master

\n\n
           E   <-- master, third-person\n          /\nA--B--C--D\n
Run Code Online (Sandbox Code Playgroud)\n\n

(也许他们还不知道你的情况X-Y-Z,所以我们就把他们从图纸上去掉)。现在,您运行git fetch upstream到本地计算机来获取 GitHub 存储库版本,并且在您的存储库中(仍然有 )X-Y-Z,您有:

\n\n
           E   <-- upstream/master\n          /\nA--B--C--D   <-- master, origin/master\n          \\\n           X--Y--Z   <-- my-branch (HEAD), origin/my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

(我在这里假设 yourorigin代表你的GitHub 分支,而 yourupstream代表你创建分支的原始 GitHub 存储库)。

\n\n

没有交互式变基

\n\n

当您运行git rebase upstream/master或表明master要提交E并运行时git rebase master,您的 Git 会复制提交XYZ

\n\n
             X\'-Y\'-Z\'  <-- my-branch (HEAD)\n            /\n           E   <-- upstream/master\n          /\nA--B--C--D   <-- master, origin/master\n          \\\n           X--Y--Z   <-- origin/my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

(此图假设您没有更新您的master;如果您更新了,我们可以稍微不同地绘制它)。

\n\n

这些副本\xe2\x80\x94(标有刻度线的副本X\'-Y\'-Z\'\xe2\x80\x94)已准备好供您强制推送到叉子。假设您此时这样做,您的分叉将拥有它们,并且原始X-Y-Z链将被放弃:

\n\n
             X\'-Y\'-Z\'  <-- my-branch (HEAD), origin/my-branch\n            /\n           E   <-- upstream/master\n          /\nA--B--C--D   <-- master, origin/master\n          \\\n           X--Y--Z   [abandoned]\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我们也更新您自己masterorigin/master,我们将能够以更简单的方式重新绘制所有这些:

\n\n
A--B--C--D--E   <-- master, origin/master, upstream/master\n             \\\n              X\'-Y\'-Z\'   <-- my-branch (HEAD), origin/my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

(所有这些都位于计算机上的存储库\xe2\x80\x94on GitHub 上,在的 fork 中,这些只是命名mastermy-branch没有部分,而且根本origin/没有部分)。upstream/

\n\n

您的拉取请求将自动更新以使用新的X\'-Y\'-Z\'提交,只需添加到提交E\xe2\x80\x94 即可,因此如果它们的存储库仍然以 commit 结尾E,那么它们吸收您的提交应该不会有问题。

\n\n

通过交互式变基

\n\n

如果你想挤进Y\'\的消息X\'并做一些事情,你现在可以使用交互式变基来做到这一点。Z\'或者,您可以执行所有这些操作,而不是最初的变基:

\n\n
$ git checkout my-branch\n$ git rebase -i upstream/master\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,upstream/master这里在我们的图中实际上意味着“提交”,这是具有as和 或或作为您自己的提示的E那些之一。Eupstream/masterZZ\'my-branch

\n\n

my-branch现在,Git 将列出已在但未在 上的三个提交的列表upstream/master,即,从Z或开始Z\'并向后工作到A,列出这些提交,然后从该列表中删除所有从 开始E并向后工作的提交。这三个提交的哈希 ID 将进入三个pick命令。

\n\n

现在,您可以像以前一样将一项更改为压缩,一项更改为编辑:因为您处理自己的提交,而不处理其他人的提交,所以您不会将其他人的提交复制到新的和不同的提交。完成编辑后,您将再次获得新的提交\xe2\x80\x94如果您已经复制XX\'YtoY\'Zto Z\',现在您拥有X"的是X\'+ Y\'(压缩),以及Z"的编辑版本Z\'但连接到X"

\n\n
              X"-Z"   <-- my-branch (HEAD)\n             /\nA--B--C--D--E   <-- master, origin/master, upstream/master\n             \\\n              X\'-Y\'-Z\'   <-- origin/my-branch\n
Run Code Online (Sandbox Code Playgroud)\n\n

您现在可以将它们强制推送到您的 fork,以便它们my-branch(您在此处的 Git 存储库中调用origin/my-branch)更改为指向 commit Z",现在您的 fork 再次更新,并且您的拉取请求会自动更新,您\' re(仍然,或最终真正)准备好让他们查看X"Z"

\n