为什么Git不会自动快进?

时间:2016-11-22 21:20:04

标签: git

当我执行git pull,添加,提交,推送时,会发生这种情况

 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'http://foo:8080/tfsdev/foo/_git/Development.Services'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

当我添加-ff到git pull时,它会通过。 我认为fastforward是默认的,为什么会发生这种情况?

1 个答案:

答案 0 :(得分:10)

  

当我向git pull添加-ff时,它会通过。我认为fastforward是默认的,为什么会发生这种情况?

你在这里混合了一堆不同的概念......这并不奇怪,因为git pull 混合了一堆不同的概念,试图方便主要是导致混乱。 (好吧,偶尔也方便。:-))

使用git pull通常会运行git merge,这通常会执行快进而不是合并。这样,您的分支机构就可以了解从远程调用的任何git pull(通过git fetch),以便您添加的提交,也只会添加到(不会替换或删除) - 来自)他们的承诺。但git push git pull相反,它与git fetch相反:做任何事情合并。

当您执行pull +做东西(提交或其他)+ push序列时,您正在与其他也在进行拉提交推送序列的人竞争。谁赢了,就得推。这部分真的很简单。当你没有获胜时,问题就出现了!

当你是唯一一个跑步的人时,很容易赢得这场比赛。如果没有其他人在推动这个分支,你将永远赢得比赛。这里发生的事情是你不是唯一一个跑步的人,而你输了。

遗憾的是,完整的答案很复杂,而且你没有充分展示能够回答这个具体案例的情况。所以,让我们来看看究竟发生了什么。

始终跟踪当前分支

如果git statuson branch master,或git branch打印* master,则您当前的分支为master。大多数Git命令适用于您当前的分支,git pull在这里也不例外。

git pull命令只运行另外两个Git命令。第一个是始终 git fetch。第二个,你可以改变。

首先,git pull运行git fetch

第一个git pull运行的命令是git fetch。它运行有限的 git fetch,告诉git fetch仅为您的当前分支提供所需的提交和其他项目。如果您只是运行git fetch,Git会带来所有内容,这可能会花费更长的时间,但这意味着您可以与所有内容保持同步。

git fetch命令是关于使用当前分支的常用Git规则的例外。在某些情况下,它仍然会查看您当前的分支,但git fetch通常更新您的远程跟踪分支。在大多数情况下,您将只有一个名为origin的所谓远程,并且正在运行git fetch会带来origin的所有内容并更新您的origin/master },origin/develop和其他origin/分支。这些是名为origin的远程跟踪分支。

因此,如果您自己运行git fetch,它将更新您的所有origin/*远程跟踪分支机构。如果您运行git pull,它将以基于您当前分支的方式更新仅一个的方式运行git fetch。这些更新始终是安全的:它们将新的提交带入您的存储库,这对您的存储库中的所有现有提交都没有影响。

为了让人感到困惑,git fetch将所有更新写入名为FETCH_HEAD的文件,您将在the git fetch documentation中看到该文件。但它更新你的远程跟踪分支,除非你有一个非常旧版本的Git(早于Git版本1.8.4)。对于那些使用古代Gits(1.7.x)的人来说,git fetch的所有内容origin都会对其进行更新,但由git pull运行的内容不会更新。

然后git pull运行其他东西

这里的事情有点乱。 git pull第二部分是 git mergegit rebase。默认值为git merge,即使git rebase通常更好。 您必须提前选择git pull应使用哪个命令。但您使用哪个 ?要确定的唯一方法是查看即将发生的事情。如果您选择新的提交,可以使用git log和其他Git命令查看它们。

要了解进入的内容,您必须将其引入。这是git fetch的作用。 git fetch步骤引入新提交,安全,将它们安全地置于这些远程跟踪分支之后。 现在您可以决定是合并还是改组。

git pull命令可让您在之前决定。因此,选择你想要它做哪一个 - 合并,或者rebase-and pull,然后它将获取然后执行。要使其执行rebase,请运行git pull --rebase,或者设置当前分支以使git pull默认执行此操作。

(此设置为每个分支,因此您必须为每个分支设置它。还有一个配置条目告诉Git设置" rebase模式"自动为Git创建的每个分支 - 但在我看来,只需自己运行git fetch,然后自己运行第二个命令就可以了。这让你有机会你所得到的东西,以及在出现问题时不那么容易混淆。)

当第二个命令为git merge

我们假设git pull运行的第二件事是git merge。此 可以在您当前的分支上运行。但git merge本身有点复杂。

当您合并时,您通常通过分支名称选择一些提交,并且经常通过远程跟踪分支名称(如origin/master) - 并要求Git找出几个事项。此合并的目标很容易说明:合并将您完成的工作与其他人的工作相结合,以使用这两组工作进行新的提交。

弄明白你做了什么,以及他们(无论他们是谁)做了什么,Git必须找到合并基础。此合并基础或多或少是您和他们共享的第一个提交。这意味着是时候绘制提交图的一部分了。这是一些可能性。

快进"合并"

...--o--*     <-- master
         \
          o   <-- origin/master

o代表提交,右边有更新的提交。我已使用*标记了合并基础提交。在这里,您的master指向合并基础提交,origin/master指向较新的提交。

在这种情况下,git merge能够做到这一点&#34;快进&#34;事情。也就是说,因为合并基础 master的提示,所以不需要实际的合并。 Git只需向下滑动名称主服务器,以便它指向与<{1}}相同的相同提交:

origin/master

现在我们也可以理顺这条线,并且只有:

...--o--o
         \
          o   <-- master, origin/master

如果您现在运行...--o--o--o <-- master, origin/master ,基本上没有任何反应,因为您的结尾没有任何新内容。最后一次提交是您已经从git push origin master获得的

真正的合并

但是,如果我们有这个呢?

origin

在这里,Git必须进行真正的合并。 Git将提交...--o--*--o <-- master \ `-o <-- origin/master 与您的*提交(最右边的最右边master)和o提交(底线上的origin/master)进行比较)。也就是说,Git运行o两次。然后它尝试组合两组更改,如果成功,则进行&#34; merge commit&#34;的新提交。味:

git diff

此提交已准备好...--o--*--o--o <-- master \ / `-o <-- origin/master git push,在与其他任何人同时尝试origingit push的竞赛中。

或许没有什么可做的

你甚至可以从头开始:

origin

现在没有任何东西可以合并:名称...--o--o--* <-- master, origin/master 和名称master都指向同一个提交,因此合并基础提交已经是{{1 (以及origin/master)。现在没有任何东西可以融合,在无所事事之后,也没有什么可以推动的。你甚至不必尝试比赛,更不用说赢得比赛了。

现在你也可以进行新的提交

现在您的masterorigin/master排队了,您可以进行新的提交。当你这样做时,你的 master将会超过&#34; origin/master

master

(我将底部origin/master向下移动,以便我可以指向...--o--o o--o <-- master \ / o <-- origin/master 。)

o做什么

我在上面提到origin/mastergit push不相反;它更像是push

当你运行pull时,你的Git会调用其他一些Git。在这种情况下,这就是远程fetch上的Git。然后它会移交您已经提交的任何新提交和请求:&#34;请将您的git push设置为此最新提交。&#34;

换句话说,我们要求他们的 Git更改他们的 origin - 我们调用master - 匹配我们的 master

看看上面的那些图纸。当我们执行快进时,我们将origin/master移动到我们已从master获取的提交。当我们什么也没做,我们什么也没做。在任何一种情况下,都没有新的提交:没有什么可以打扰的。无论如何我们推送,我们会对另一个Git说:&#34;这里没有什么新内容,但请设置master以便它指向对于这个特定的提交,就像我们一样。&#34;

如果我们做了做了一次新的提交 - 正如我们对合并确实做了一些事情,或者我们做了新的提交 - 我们为他们提供了新的东西。我们会将他们的新提交交给他们,然后让他们将origin/master设置为与master相同。

当然,我们也会在推动其他人的比赛。

如果没有其他人 赢得推送比赛,他们的master仍然会匹配我们的master。我们要求推动,好吧,看看上面的图纸。移动master(我们的origin/master的名称)前进以匹配我们的origin/master,为什么,这是我们之前看到的那些快速前进操作之一!

如果推送会进行快进,通常会成功。

但是如果其他人赢了怎么办?如果,在我们运行mastermaster之后,我们花了一些时间做出提交,同时还有其他人进来并推到我们面前呢?

好吧,现在我们有了这个:

git fetch

但这并不是非常正确,因为他们他们的 git merge上有新的提交。 他们有其他人推送的提交。我们需要运行...--o--o o--o <-- master \ / o <-- origin/master 并获取它们。让我们这样做,现在 - 现在master使用我们拥有的新提交 - 更新了我们的git fetch

git fetch

现在,为了推动,我们必须合并或重组。我将绘制合并案例。 (请注意,合并基础是最底层的最左侧提交。)

origin/master

如果我们进行此合并,现在我们可以推送,因为现在我们的...--o--o o--o <-- master \ / o-----o <-- origin/master 是&#34;快速前进&#34;他们的...--o--o o--o--o <-- master \ / / *-----o <-- origin/master

当然,在我们合并的同时,我们也在与其他可能试图推动的人竞争。如果我们输掉比赛,我们可能需要再次获取和合并。

(在所有这些情况下,master 可能master更好。请参阅其他StackOverflow关于rebase的答案。)