为什么执行'git pull origin mybranch'命令后,本地的mybranch分支会比远程的origin分支多N个提交?

91
我刚刚观察到了一些关于git pull的奇怪现象,但我不理解。

上周五,我在本地分支上工作。我们称之为mybranch。 在离开办公室之前,我将其推送到原始位置(也就是我的github存储库):git push origin mybranch

昨天在家里,我pull了mybranch到我的笔记本电脑上,做了更多的编码,然后将更改推回到github(原点)。

现在我又回到了办公室,想从昨天的更改中拉取内容到我的工作机器上(在周末期间,我没有更改我工作场所的本地存储库):

git pull origin mybranch

我进行了一次快进合并,这是可以的。然后我运行了git status 命令,它显示:

# On branch mybranch
# Your branch is ahead of 'origin/mybranch' by 6 commits.
#
nothing to commit (working directory clean)

嗯?我周末根本没碰它,而且刚从远程仓库拉了代码,怎么会比远程仓库多6个提交?所以我运行了git diff origin/mybranch,发现差异正好是我刚刚从远程仓库拉下来的6个修改。

我只能通过运行git fetch origin才“修复”了这个问题:

From git@github.com:me/project
af8be00..88b0738  mybranch -> origin/mybranch

显然,我的本地仓库缺少一些引用对象,但这是怎么回事呢?我是说,拉取操作已经包含了fetch的过程,而且我除了那个分支以外没有做任何其他工作,所以 git fetch origingit fetch origin mybranch 应该得到相同的结果,不是吗?

我是否应该总是使用git pull origin而不是git pull origin branchname? 我很困惑。


我也注意到了这一点;git push 似乎也可以解决它(报告“全部更新”)。 - Ben James
4
git config --get-regexp br.* 可以告诉你,如果你的配置有一个本地分支正在跟踪另一个分支。 - VonC
3
你能在你的工作仓库中输入 git config branch.master.remote yourGitHubRepo.git 并检查(在下一次 git pull origin)状态是否仍然显示 'ahead' 警告吗? - VonC
1
仅使用 git remote 命令(显示 GitHub 存储库的正确地址)是不够的。为了避免在 git pull 后出现 "Your branch is ahead" 警告消息,您需要首先定义分支的远程名称。因此,我的建议是:输入 git config branch.master.remote yourGitHubRepo.git,然后尝试 git pullgit status,看看问题是否仍然存在。 - VonC
@VonC 谢谢啊,朋友,你刚刚帮我解决了分支超前的错误!+1 - Drewdin
显示剩余2条评论
3个回答

116

git pull 会在将显式获取的分支(如果没有,则获取配置为合并的远程分支)合并到当前分支之前,使用适当的参数调用 git fetch

语法: git fetch <repository> <ref> 其中<ref> 只是一个不带冒号的分支名称,是一次“一次性”获取,并不执行特定远程所有已跟踪分支的标准获取,而是仅获取命名分支到 FETCH_HEAD 中。

更新:自Git 1.8.4以后的版本中,如果存在跟踪您要求获取的引用的远程跟踪分支,则远程跟踪分支将通过 fetch 更新。这个更改是专门为了避免以前的行为引起的混淆。

当您执行 git pull <repository> <ref> 时,如上所述更新了 FETCH_HEAD,然后合并到检出的 HEAD,但不会更新远程库的任何标准跟踪分支(Git <1.8.4)。这意味着在本地看来,您领先于远程分支,但实际上您已经与其保持一致。

个人而言,我始终执行 git fetch,然后执行 git merge <remote>/<branch>,因为这样我可以在合并之前看到任何关于强制更新的警告,并且可以预览我要合并的内容。如果我使用 git pull 的频率更高一些,大多数情况下我会执行不带参数的简单 git pull,依靠 branch.<branch>.remotebranch.<branch>.merge 来“做正确的事”。


4
+1 这真的是一个很好的解释!我知道解释就藏在 'git help fetch' 中,但却无法理解... - Stefan Näwe
1
+1。好的帖子,采用了类似于http://gitster.livejournal.com/28309.html的方法。 - VonC
1
那么,在 git pull <repository> <ref> 之后进行 git fetch 是否可以解决问题,因为 fetch 将更新标准跟踪分支?另外,感谢您的回答,开始有点明白了 :) - Bart Jedrocha
1
我也遇到了这个问题,你需要执行 git fetch 然后执行 git merge origin/master master - user1027169

3

当运行git remote -v show命令时,它返回什么关于origin的信息?

如果origin指向GitHub,则状态应该是最新的,并且不会超前于任何远程存储库。至少,在我使用Git1.6.5进行快速测试时是这样的。

无论如何,为了避免这种情况,应明确定义主分支的远程存储库:

$ git config branch.master.remote yourGitHubRepo.git

然后执行 git pull origin master,接着执行 git status 应该会返回一个干净的状态(没有 ahead)。为什么?因为 git fetch origin master(包含在 git pull origin master 中)不仅会更新 FETCH_HEAD(正如 Charles Bailey他的回答 中所解释的那样),还会更新你本地 Git 仓库中的“远程主分支”。这种情况下,你本地的主分支就不再“领先”于远程主分支了。

我可以使用 git1.6.5 进行测试:

首先创建一个工作仓库:

PS D:\git\tests> cd pullahead
PS D:\git\tests\pullahead> git init workrepo
Initialized empty Git repository in D:/git/tests/pullahead/workrepo/.git/
PS D:\git\tests\pullahead> cd workrepo
PS D:\git\tests\pullahead\workrepo> echo firstContent > afile.txt
PS D:\git\tests\pullahead\workrepo> git add -A 
PS D:\git\tests\pullahead\workrepo> git commit -m "first commit"

我通过创建一个裸仓库(可以从任何地方接收推送)来模拟 GitHub 仓库。
PS D:\git\tests\pullahead\workrepo> cd ..
PS D:\git\tests\pullahead> git clone --bare workrepo github

我在工作的代码库中添加了修改,并将其推送到GitHub代码库(作为远程仓库)

PS D:\git\tests\pullahead> cd workrepo
PS D:\git\tests\pullahead\workrepo> echo aModif >> afile.txt
PS D:\git\tests\pullahead\workrepo> git ci -a -m "a modif to send to github"
PS D:\git\tests\pullahead\workrepo> git remote add github d:/git/tests/pullahead/github
PS D:\git\tests\pullahead\workrepo> git push github

我创建了一个主页仓库,并从GitHub上克隆它,在其中进行了一些修改,然后上传到GitHub。
PS D:\git\tests\pullahead\workrepo> cd ..
PS D:\git\tests\pullahead> git clone github homerepo
PS D:\git\tests\pullahead> cd homerepo
PS D:\git\tests\pullahead\homerepo> type afile.txt
firstContent
aModif

PS D:\git\tests\pullahead\homerepo> echo aHomeModif1  >> afile.txt
PS D:\git\tests\pullahead\homerepo> git ci -a -m "a first home modif"
PS D:\git\tests\pullahead\homerepo> echo aHomeModif2  >> afile.txt
PS D:\git\tests\pullahead\homerepo> git ci -a -m "a second home modif"
PS D:\git\tests\pullahead\homerepo> git push github

我随后复制了 workrepo 以进行第一次实验。
PS D:\git\tests\pullahead\workrepo4> cd ..
PS D:\git\tests\pullahead> git clone workrepo workrepo2
Initialized empty Git repository in D:/git/tests/pullahead/workrepo2/.git/
PS D:\git\tests\pullahead> cd workrepo2
PS D:\git\tests\pullahead\workrepo2> git remote add github d:/git/tests/pullahead/github
PS D:\git\tests\pullahead\workrepo2> git pull github master
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From d:/git/tests/pullahead/github
 * branch            master     -> FETCH_HEAD
Updating c2763f2..75ad279
Fast forward
 afile.txt |  Bin 46 -> 98 bytes
 1 files changed, 0 insertions(+), 0 deletions(-)

在那个代码库中,git状态确实提到了主分支“master”领先于“origin”的情况:
PS D:\git\tests\pullahead\workrepo5> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
nothing to commit (working directory clean)

但这只是origin不是github:

PS D:\git\tests\pullahead\workrepo2> git remote -v show
github  d:/git/tests/pullahead/github (fetch)
github  d:/git/tests/pullahead/github (push)
origin  D:/git/tests/pullahead/workrepo (fetch)
origin  D:/git/tests/pullahead/workrepo (push)

但是,如果我在一个具有指向 GitHub 的 origin(或者根本没有 origin,只定义了一个远程的“github”)的 repo 中重复该序列,则状态干净:

PS D:\git\tests\pullahead\workrepo2> cd ..
PS D:\git\tests\pullahead> git clone workrepo workrepo4
PS D:\git\tests\pullahead> cd workrepo4
PS D:\git\tests\pullahead\workrepo4> git remote rm origin
PS D:\git\tests\pullahead\workrepo4> git remote add github d:/git/tests/pullahead/github
PS D:\git\tests\pullahead\workrepo4> git pull github master
remote: Counting objects: 8, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From d:/git/tests/pullahead/github
 * branch            master     -> FETCH_HEAD
Updating c2763f2..75ad279
Fast forward
 afile.txt |  Bin 46 -> 98 bytes
 1 files changed, 0 insertions(+), 0 deletions(-)
PS D:\git\tests\pullahead\workrepo4> git status
# On branch master
nothing to commit (working directory clean)

如果我只有origin指向github,那么对于git1.6.5来说,status应该是干净的。
对于早期版本的Git,可能会有“ahead”警告,但是无论如何,明确定义git config branch.master.remote yourGitHubRepo.git应该能够处理这个问题。


感谢您抽出时间查看此内容。源远程已经指向我的GitHub存储库。我从GitHub URL克隆了该项目,我的本地主分支正在跟踪origin/master。至于mybranch,我很确定是从origin/mybranch分支创建的,应该会自动跟踪它。但是,也许这就是问题所在?本地mybranch实际上并没有跟踪origin/mybranch吗?PS:我正在使用git 1.6.1(通过MacPorts)。 - mxk
有没有一个git命令可以让我查看本地分支是否跟踪另一个分支?我在手册页中找不到它。 - mxk
您可以使用 git remote show origin 命令查看哪些远程分支被跟踪。 - T Percival

2

您在添加所有远程仓库(除了原始克隆中自带的origin)时,是否小心使用git remote add NAME URL?我曾看到过这种错误,当它们刚被添加到git配置中。


我克隆repo时这样做的。但我没有针对每个分支都这样做。例如,对于mybranch,我会先从origin获取,然后使用git checkout -b mybranch origin/mybranch。根据git-branch的手册页面,origin/mybranch是起点,并且进一步说明了--track: "...如果您总是将同一个上游分支拉入新分支,并且不想明确使用"git pull <repository> <refspec>",请使用此选项。当起点是远程分支时,此行为是默认值。" - mxk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接