推送到远程失败,因为“当前分支的尖端在其远程对应的后面”

时间:2020-06-28 19:35:53

标签: git github rebase

My git terminal which doesn't make sense

我的朋友正在尝试帮助我学习解决请求请求中的冲突。他创建了一个存储库。我克隆了它,并创建了一个名为“ EditReadMe2”的分支。我将“ EditReadMe2”推到存储库中,他造成了冲突。

我做到了

git checkout master
git pull
git checkout EditReadMe2
git rebase master

它警告我解决了冲突,但是当我尝试推送EditReadMe2时,出现了错误。

我再次输入命令以在所附的图像中显示我的终端,因为我不知道当我第二次拉动并重新设置基准时分支是否有可能落后,并且它告诉我一切都在进行日期,但仍然失败。

强行解决了这个问题,但是我想知道如何在不使用--force的情况下做到这一点。

谢谢!

2 个答案:

答案 0 :(得分:1)

命令顺序不完整。
git checkout EditReadMe2之后,您需要执行另一个git pull

git pull更新当前分支的工作索引,而不更新您拥有的所有 local 分支的工作索引。
发出rebase命令时,将把更新后的master重新建立到“旧” EditReadMe2分支中。

无论如何,git rebase都可以以这种方式使用。


提示:
如果您出于结底的目的而切换到mastergit pull,切换到EditReadMe2 ,则可以使用以下顺序并保存几个命令:

假设您在EditReadMe2

git pull
git rebase origin/master

答案 1 :(得分:1)

重定基通常会产生此结果-这需要使用--force-因为用新的和改进的 1 重定 replaces 一些现有的提交提交。要真正了解其工作原理,您需要了解Git如何使用和查找提交,以及git push和其他命令如何工作。有点棘手!首先,看看my long answerHow to delete all unpushed commits without deleting local changes,以了解类似的图形:

...--G--H   <-- master
         \
          I   <-- feature (HEAD)

可能是卑鄙的。特别是,您需要记住这些字母如何代表原始哈希ID,每个提交如何向后指向其父提交,以及分支名称如何指向/包含在其中的 latest 提交。那个分支。


1 至少,我们希望得到改进。 ?


设置

现在,让我们假设我们有一系列的提交本身并没有有缺陷的 –我们实际上不需要修复其中的任何内容–但是这些提交是较早完成的,例如:

...--G--H   <-- master
         \
          I--J   <-- feature

(无附件HEAD表示我们不在乎在此之前签出了哪一个)。我们先运行git checkout mastergit switch母版,然后运行git pull或类似名称,并获取新的master提交,然后执行以下操作:

...--G--H--K   <-- master (HEAD), origin/master
         \
          I--J   <-- feature, origin/feature

我们还将添加或更新这些远程跟踪名称origin/masterorigin/feature。它们是我们Git对某些 other Git的分支名称的记忆。我们的名称origin/master标识提交K,现在我们自己的分支名称master也是如此;我们的名字origin/feature告诉我们,在origin上,他们也有我们的分支名feature的副本,它也标识了提交J,就像我们的feature 。 (也许是他们在更早的时候运行git push origin feature时得到的。)

这下一部分很重要:提交哈希ID(这两个大写字母代表的丑陋的字母和数字字符串)在两个存储库中都是相同的。 >分支名称不必是,尽管在这种情况下,它们现在也是如此。

Rebase通过复制提交来工作

在此设置中,我们确定功能的缺陷在于它基于提交H,而最新的提交现在是提交K。我们希望基于提交feature创建K分支。为此,我们运行:

git switch feature       # or git checkout feature

给予我们

...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature (HEAD), origin/feature

其次:

git rebase master

rebase命令列出了分支feature上的那些提交的原始哈希ID,而master上的不是的那些提交。在这种情况下,这就是提交IJ的哈希ID。 (请注意,提交H和更早的版本都在两个分支上。)然后,Git使用其特殊的“分离式HEAD”模式来开始处理提交K,位于技巧的顶端。 master

...--G--H--K   <-- HEAD, master, origin/master
         \
          I--J   <-- feature, origin/feature

Git应用我们在提交I中所做的工作,并从中进行新的提交。此新提交具有不同的新哈希ID,但是重新使用了I中的作者姓名和日期和时间戳,并重新使用了I中的commit消息,因此commit看起来非常像commit I。换句话说,它是提交I副本 2 我们将这个新副本称为I'

             I'  <-- HEAD
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature, origin/feature

成功将I复制到I'之后,Git现在以相同的方式复制J,结果:

             I'-J'  <-- HEAD
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- feature, origin/feature

复制过程现已完成-不再有要复制的提交,因此rebase执行其最后一步,即从用于命名的提交中删除名称feature并使其指向最后一个复制的提交,在这种情况下为J'

             I'-J'  <-- feature (HEAD)
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   <-- origin/feature

如图所示,在最后一步中,Git重新附加了HEAD,以便我们回到正常的工作模式,即在分支上使用附加的HEAD

请注意,使用名称feature不再可以找到此处的两个原始提交。如果我们没有名字origin/feature来记住 other Git的feature,那么我们将完全放弃这两个提交。但是我们的Git记得他们的 Git记得使用他们的名称J的提交feature

无论哪种情况,请注意我们所做的。 我们抛弃或至少尝试抛弃了旧提交,以支持这些新的和改进的提交。我们仍然可以通过origin/feature名称访问旧提交,因为我们记得origin上的Git over正在记住通过分支名称J的提交feature


2 您可以根据需要使用git cherry-pick复制任何提交。 rebase的作用是分离HEAD,然后进行一组自动的樱桃突击,然后执行此分支名称动作,类似于git resetgit branch -f。在旧版本的Git中,rebase可以默认为一种替代策略,该策略实际上不会运行git cherry-pick,但是这些细节通常并不重要。


git push的工作方式

git push命令的工作原理是让您的Git调用其他Git。此其他Git也具有提交和分支名称。他们的分支名称不必与您的分支名称匹配,但是如果不匹配,事情就会变得很混乱,因此大多数人在这里都将它们的分支名称设为相同。

他们的Git为您的Git列出了他们的分支名称和提交哈希ID。 3 这可以让您的Git找出哪些提交没有它们,他们将需要。然后,您的Git通过其哈希ID将这些提交发送到其Git。除了这些提交,您的Git还会发送Git所需的其他任何内部对象。

发送正确的对象后,您的Git现在会发送一个或多个礼貌的请求或命令。礼貌的请求具有以下格式:请,如果可以,请将您的姓名______(填写分支或标签名称)设置为______(填写哈希ID)。这些命令具有以下两种格式之一:我认为您的姓名______(填写分支或标签名称)设置为______(填写哈希ID)。如果是这样,请将其设置为______!或:将您的姓名______设置为______!

有礼貌的请求表单会询问,要求他们将其feature设置为指向提交J',这是我们用作新的J的副本,并改进了J的版本。但是, 他们不知道这是一个经过改进的新副本,他们只能告诉我们我们要他们扔掉提交IJ,并使它们的名称feature记住提交J'。他们说不! 他们说如果我这样做,我会失去一些承诺。

这就是我们希望他们做的:丢失提交IJ,用新的和改进的代替。为了使他们能够做到这一点,我们必须向他们发送命令。

如果我们使用git push --force-with-lease,我们将向他们发送条件命令:我认为您的feature标识提交J;如果是这样,则改为标识J'如果他们接受并执行了此命令,我们将拥有I'-J'的提交,我们现在可以像这样绘制存储库:

             I'-J'  <-- feature (HEAD), origin/feature
            /
...--G--H--K   <-- master, origin/master
         \
          I--J   [abandoned]

如果完全允许这样做,则此--force-with-lease选项通常是正确的方法。这样做会迫使其他正在使用feature分支,位于另一个Git存储库中,以使用新的和改进的提交来更新分支。通常,在开始以这种方式重新设置基础之前,让每个人都同意feature可以通过这种方式重新设置是一个好主意。您需要做的是弄清楚谁是“每个人”。如果那只是您自己,您只需要同意自己。如果是您和六个同事,请先征得同事的同意。

使用git push --force而不是--force-with-lease,省略了安全检查:它只是向命令发送另一个 set your feature 的命令,而没有任何条件的“我认为”第一部分。如果您的Git与他们的Git是最新的,那么您的origin/feature和他们的feature都可以识别提交J,就可以了。但是,如果在您完成工作并要推送之后,其他人在{{1}上的Git中向L添加了新的提交feature }?您的推力将告诉Git也放弃那个提交。

“强制租赁”选项更好,因为您的Git会告诉另一个Git,您认为他们的origin标识的是提交feature,而不是提交的J。他们会说:糟糕,现在我的L了,您的L将失败。现在,您可以git push --force-with-lease,看到有一个新的提交git fetch,并修复您的基准,也可以复制提交L,然后在您的{{ 1}}表示提交L


3 此处的确切机制已针对Git智能协议v2进行了重写,该协议最初是在Git 2.26中默认启用的。我不会详细介绍,但是在早期v2协议中存在一个小但令人讨厌的小错误,您的Git有时会推入太多对象。此错误已在Git 2.27中修复。如果您使用的是2.26,而推送的时间太长,则可以使用git push --force-with-lease解决此问题,或者只是升级。