为什么Git不会自动快进?

use*_*558 2 git

当我执行git pull,add,commit,push时,会发生这种情况

 ! [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.
Run Code Online (Sandbox Code Playgroud)

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

tor*_*rek 12

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

你是混合了一堆在这里不同的概念......这也难怪,因为git pull 混合了一堆不同的概念,企图要方便是主要结果是混乱.(好吧,偶尔也方便.:-))

使用git pull通常会运行git merge,这通常会执行快进而不是合并.这使您的分支更新git pull了从远程(via git fetch)带来的任何内容,以便您添加的提交,也将仅添加(不替换或删除)其提交.但是,git push不是相反git pull,它是相反的git fetch:它不会做任何合并.

当你做一些pull+做东西(提交或其他)+ push序列时,你正在与其他也在做pull-commit-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,你会看到中提到git fetch文档.但它也会更新你的远程跟踪分支,除非你有一个非常旧版本的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为Git创建的每个分支自动设置"rebase模式" - 但是在我看来,运行git fetch自己要容易得多,然后自己运行第二个命令.这使您有机会查看您所获取的内容,以及在出现问题时更少混淆.)

当第二个命令是 git merge

让我们假设第二件事git pull就是git merge.这确实对你当前分支工作.但git merge它本身有点复杂.

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

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

快进的"合并"

...--o--*     <-- master
         \
          o   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

os为提交,与新向右提交.我已经标记了合并基础提交*.在这里,您master指向合并基础提交,origin/master指向更新的提交.

在这种情况下,git merge能够做到这个"快进"的事情.也就是说,因为合并基础已经提示master,所以不需要实际的合并.Git只需向下滑动名称大师,使其指向同一个提交origin/master:

...--o--o
         \
          o   <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

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

...--o--o--o  <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

如果你现在跑步git push origin master,基本上没有任何事情发生,因为的结果并没有什么新东西.最后一次提交是你已经得到origin.

真正的合并

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

...--o--*--o   <-- master
         \
          `-o    <-- origin/master
Run Code Online (Sandbox Code Playgroud)

在这里,Git必须进行真正的合并.Git将提交*与您的master提交(最右边的最右边o)和origin/master提交(o底线)进行比较.也就是说,Git运行了git diff两次.然后它尝试组合两组更改,如果成功,它会进行"合并提交"风格的新提交:

...--o--*--o--o   <-- master
         \   /
          `-o    <-- origin/master
Run Code Online (Sandbox Code Playgroud)

这个承诺是准备git pushorigin,在比赛反对任何人也试图git pushorigin.

或许也没什么可做的

你甚至可以从这开始:

...--o--o--*   <-- master, origin/master
Run Code Online (Sandbox Code Playgroud)

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

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

现在,您masterorigin/master排队,就可以使新的提交.当你这样做时,你的 master意志"领先" origin/master:

...--o--o   o--o   <-- master
         \ /
          o   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

(我把底部o向下移动,以便我可以指向origin/master它.)

是什么git push

我上面提到的push并不是相反的pull; 它更像是相反的fetch.

当你运行时git push,你的Git会调用其他一些Git.origin在这种情况下,这就是遥控器上的Git .然后它会移交您所做的任何新提交,并提出请求:"请将您设置master为最新提交."

换句话说,我们要求他们的 Git改变他们 master - 我们称之为origin/master- 匹配我们 的东西master.

看看上面那些相同的图纸.当我们进行快进时,我们将我们转移master到了已经从中获取的提交origin/master.当我们什么也没做,我们什么也没做.在任何一种情况下,都没有新的提交:没有什么可以打扰的.如果我们确实推进了,我们会对另一个Git说:"这里没有什么新东西,但请设置你的,master以便它指向这个特定的提交,就像我们的一样."

如果我们确实做了一个新的提交 - 正如我们对合并做的那样,或者当我们做了新的提交时 - 我们确实为它们提供了新的东西.我们将他们的新提交交给他们,然后让他们设置master我们的相同master.

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

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

如果推动会做快进,它通常会成功.

但如果其他人赢了怎么办?如果有什么,以后我们跑git fetchgit merge,我们花了一些时间去提交,同时其他人上了车,我们推前?

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

...--o--o   o--o   <-- master
         \ /
          o   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

但是这不是真的完全正确的,因为他们对新提交 master. 他们有其他人推动的承诺. 我们需要运行git fetch并获取它们.让我们这样做,现在 - 现在用新的提交git fetch更新我们- 现在我们有:origin/master

...--o--o   o--o   <-- master
         \ /
          o-----o   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

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

...--o--o   o--o--o   <-- master
         \ /     /
          *-----o   <-- origin/master
Run Code Online (Sandbox Code Playgroud)

如果我们进行合并,现在我们可以推动,因为现在我们master的"快速前进"了master.

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

(在所有这些情况下,git rebase可能更好的事情,而不是做的git merge.请参阅有关重订其他StackOverflow的答案.)