当我在git pull后面添加-ff,它就会执行。我以为快进是默认的,为什么会这样?
你在混淆许多不同的概念...这并不奇怪,因为git pull也混淆了许多不同的概念,试图变得方便,但大多数情况下却让人感到困惑。(好吧,还有时候很方便 :-))
使用git pull通常会运行git merge,它通常会进行快速向前合并而不是合并。这将使您的分支与通过git fetch从远程获取的内容保持同步,以便您添加的提交也只会添加到它们的提交(而不是替换或删除)。但是,git push不是git pull的相反,而是git fetch的相反:它不进行任何合并。
当您执行pull + do stuff(提交或其他操作)+ push序列时,您正在与其他人竞争,他们也正在执行pull-commit-push序列。谁赢了,谁就可以推送。这一部分确实如此简单。问题在于当你输掉比赛时!
当您不是唯一在运行的人时,赢得这场比赛很容易。(如果没有其他人将提交推送到该分支,则您始终可以赢得比赛。)这里发生的事情是您不是唯一在运行的人,而且您输了。
完整的答案不幸的是复杂的,而且您没有展示足够的内容来回答这个具体的情况。因此,让我们看一下真正发生了什么。
始终跟踪您当前的分支
如果git状态说on branch master,或者git branch打印* master,那么您当前的分支就是master。大多数Git命令都与您当前的分支一起使用,git pull也不例外。
git pull命令只是运行两个其他的Git命令。第一个始终是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)的人,从
origin
手动
git fetch
会更新它们,但由
git pull
运行的更新则不会更新它们。
然后,
git pull
会运行其他一些东西。
这里情况有点混乱。`git pull`的第二部分是`git merge`或`git rebase`中的任意一个,即使`git rebase`通常更好,但默认情况下是`git merge`。你必须提前选择`git pull`应该使用哪个命令,但哪个命令才是最好的呢?唯一确定的方法是查看即将到来的内容,如果您拾取新的提交,则可以使用`git log`和其他Git命令查看它们。
然而,要查看即将到来的内容,您必须先把它带进来。这就是`git fetch`的作用。`git fetch`步骤将新提交带入,并安全地将它们放在远程跟踪分支的后面。现在您可以决定是否合并或变基。
`git pull`命令会让您在查看内容之前做出决策。因此,请选择您想要它执行的操作(合并或变基),然后拉取,它将获取然后执行该操作。要使其执行变基,运行`git pull --rebase`,或设置当前分支以使`git pull`默认执行变基。
(此设置是每个分支的专属设置,因此您必须为每个分支设置它。也有一个配置项告诉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
能够执行这种"快进"操作。也就是说,因为合并基础已经是master
的末端,实际上不需要进行合并。Git可以将名称 master 向下和向前滑动,以便它指向与origin/master
相同的提交:
...--o--o
\
o <-- master, origin/master
现在我们也可以将这条线条拉直,只留下如下内容:
...--o--o--o <-- master, origin/master
如果你现在运行 git push origin master
,基本上什么都不会发生,因为在你这一端没有新内容。最后一个提交是你从 origin
已经获取的。
真正的合并
但是如果我们有以下情况呢?
...--o--*--o <-- master
\
`-o <-- origin/master
在这里,Git需要进行一次真实的合并操作。Git将提交*
与您的master
提交(顶部行中最右边的o
)以及您的origin/master
提交(底部行的o
)进行比较。也就是说,Git会运行两次git diff
。然后它会尝试组合这两组更改,如果成功,将创建一个新的“合并提交”类型的提交:
...--o--*--o--o <-- master
\ /
`-o <-- origin/master
这个提交已经准备好推送到
origin
,与其他人同时试图将其推送到
origin
竞争。
或者可能根本没有要做的事情
你甚至可以从这开始:
...--o--o--* <-- master, origin/master
现在没有需要合并的内容了: master
和 origin/master
这两个名称指向同一个提交,所以合并基础提交已经是 master
分支的末端(也是 origin/master
的末端)。现在没有需要合并的内容,什么都不做后,也没有需要推送的内容。你甚至不必尝试比赛,更不用赢它。
现在你也可以创建新的提交记录
现在你的 master
和 origin/master
已经一致,你可以创建新的提交记录。当你这样做时,你的 master
将会“超过” origin/master
:
...--o--o o--o <-- master
\ /
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
,这是我们之前看到的一些快进操作中的一个!
如果推送将进行快进,则通常会成功。
但是,如果其他人赢了怎么办?在我们运行了 git fetch
和 git merge
之后,我们花了一些时间进行提交,而与此同时,其他人已经在我们之前进行了推送,会发生什么呢?
好吧,现在我们有了这个:
(我将底部的 o
向下移动,只是为了能够指向 origin/master
。)
...--o--o o--o <-- master
\ /
o <-- origin/master
但那并不完全正确,因为他们在他们的master
上有新的提交。 他们拥有其他人推送的提交。 我们需要运行git fetch
来获取它们。 让我们这样做,现在 - 现在git fetch
使用新提交更新了我们的origin/master
- 现在我们有:
...--o--o o--o <-- master
\ /
o-----o <-- origin/master
现在,为了推送,我们必须合并或变基。我将画出合并的情况。(请注意,合并基础是底部行中最左边的提交。)
...--o--o o--o--o <-- master
\ / /
*-----o <-- origin/master
如果我们进行这个合并,现在我们就可以推送了,因为现在我们的
master
已经"快进"到他们的
master
前面了。
当然,在我们合并的同时,还会与其他可能正在尝试推送的人竞争。如果我们输掉比赛,可能需要再次获取和合并。
(在所有这些情况下,
git rebase
可能比
git merge
更好。请参见其他关于 rebase 的 StackOverflow 回答。)