git:你的分支领先 X 次提交

475

这是我的工作流程:我目前独自在一个仓库中工作:

  1. 更改文件
  2. 提交(commit)
  3. 重复1-2直到满意
  4. 推送(push)到主分支(master)

然后当我运行git status时,它告诉我我的分支比远端的主分支(assumed master)超前了X个commit(大概是我所做的提交数量)。这是因为当你推送(push)代码时,它实际上并没有更新你本地缓存的文件(在.git文件夹里)?git pull似乎能够“修复”这个奇怪的消息,但我仍然很好奇为什么会发生这种情况。也许我在使用git时有问题?


包括打印消息所在的分支

我的本地分支比主分支(a.k.a. master)超前

你推送/拉取当前分支的位置在哪里

我正在推送到GitHub并拉取到我当前正在工作的任何计算机上,我的本地副本始终是最新的,因为只有我在工作。

它实际上没有检查远程仓库

这就是我想的,我想确保我对它的理解是正确的。

你有没有传递一些额外的参数(arguments)给它?

没有我能看到的,也许在我的配置(config)上发生了一些有趣的事情?

$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
nothing to commit (working directory clean)

你是如何进行push操作的?你的远程和分支配置设置是什么? - CB Bailey
2
它实际上并不检查远程仓库,你需要在执行推送后进行git fetch以获取远程仓库的最新信息,这将更新本地用于跟踪的“remote”分支。 - Sekhat
2
@Sekhat: 虽然 git status 不会检查远程仓库,但 git pull 会。如果您有一个用于推送的跟踪分支的存储库,git push 将在推送成功后更新您的本地跟踪分支以反映远程分支的新状态。这就是为什么我问问者的配置情况,因为如果没有正确地进行操作,那么可能存在配置错误。 - CB Bailey
git status?真的吗?我的 git status 从来没有告诉我我的分支超前了多少...你有传递额外的参数吗? - hasen
4
git status 不会去远程仓库检查远程分支是否有更新,它会告诉你本地分支相对于你 本地存储的 远程跟踪分支落后了多少。问题在于正常的 git push(以及 fetch 和 pull)应该会更新远程跟踪分支,但是对于提问者来说,这似乎没有起作用。要查看原因,我们需要查看正在使用的 git push 的确切形式和本地存储库的配置,但由于提问者已经接受了答案,现在我无法看到这种情况发生。 - CB Bailey
21个回答

537
如果在执行git pull remote branch后出现此消息,请尝试跟进一个git fetch。(可选地,运行git fetch -p来从存储库中修剪已删除的分支)
看起来Fetch会更新远程分支的本地表示,这并不一定发生在执行git pull remote branch时。

3
好的。那真的是问题所在。我开始在Google Code上创建存储库。然后我在我的笔记本电脑上克隆了这个存储库,在那里工作并推送更改,笔记本电脑=> code.google。我曾经在我的服务器上创建了code.google代码存储库的克隆,并在那里拉取更改,我认为需要使用fetch来更新本地数据库。 - rjha94
2
我们这里也遇到了同样的问题,因为另一个分支(A)指向了相同的master commitid。先拉取A分支,再拉取master分支会导致出现相同的情况。 当git拉取A分支时,commitid被更新为最新的,所以拉取master分支时实际上没有什么可以拉取的内容,因此git没有更新master分支的最新commitid,并警告“领先于master”。 - Uberto
9
谢谢!不过我注意到了一件奇怪的事情。"git fetch origin master" 没有帮助,但是 "git fetch origin" 却可以。我在主分支上,所以不确定在这种情况下 "git fetch origin" 会做出什么不同的操作。 - Parag
3
请参考以下网址 https://dev59.com/4ITba4cB1Zd3GeqP3D3V ,了解这两个命令之间的区别,以及如何修改配置文件来改变行为,使得 git fetch 远程分支也更新远程跟踪分支引用,这样 git_status 就不会报告 'ahead by'。 - Anatortoise House
2
@Parag,此外https://stackoverflow.com/questions/7365415/pull-only-repos-git-status-saying-the-branch-is-ahead-of-origin-master-why/7365922#comment43028967_7365922的回答讨论了ORIG_HEAD和FETCH_HEAD不同步导致状态警告以及可能需要进行配置文件更正的详细信息。 - Anatortoise House
显示剩余4条评论

215

使用

git pull --rebase

--rebase选项意味着git会将您本地的提交放在一旁,与远程同步,然后尝试从新状态应用您的提交。


6
防止无用的合并并使源代码树更加清晰的一个很好的方法! - hatef
2
我尝试了这个命令,但我仍然有同样的问题... `$ git pull --rebase 当前分支xyz已经是最新的。$ git status 在分支xyz上 您的分支比'origin / xyz'领先6次提交。 (使用“git push”发布本地提交) 没有要提交的内容,工作目录干净。 - bbh
13
这个答案是错误的:如果你在不了解情况的情况下使用它,可能会给将来带来麻烦(重写历史!)。如果你理解了情况,这就不是解决问题的方法。使用git时请三思而后行,切勿盲目地重写历史! - cmaster - reinstate monica
@Rich的答案和这个有什么不同? - Timo
这解决了我的问题!谢谢 :) - KLTR

163

使用以下3个简单命令

步骤1: git checkout <分支名称>

步骤2: git pull -s recursive -X theirs

步骤3: git reset --hard origin/<分支名称>

更多细节请参考: https://dev59.com/ZmQo5IYBdhLWcg3wI8jx#39698570

享受吧。


10
这是唯一一个能够真正解决我的问题的答案。上面的命令奇怪地将提交数量从12减少到7,而这个方法终于将它们全部移除了。 - Ieuan
1
我同意这是唯一对我有效的方法。我确信GIT有时会有多重人格障碍。 - ksed
42
像@leuan一样,只有git reset --hard origin/master才能解决我的问题。 - Dave Land
同样的情况,这是唯一有效的步骤。 - Shard_MW
这对我有用,特别是第三步做出了改变。谢谢! - Wadi Diaz-wong

60

这对我有用

git reset --hard origin/master

输出应如下所示

在dev分支上 当前版本已更新至ae1xc41z,最后的提交信息为:Last commit message


是的!谢谢,我使用了git reset --hard origin master但我仍然有一个准备推送的提交。 - Broski
git reset --hard origin/qa。这对我来说可以与远程同步。 - Frank Guo

54

我认为你误解了这条信息——你的分支不是落后于 master,而是与 master 相同。它超前于 origin/master,这是一个记录了你上次进行 pushpullfetch 操作后远程仓库状态的远程跟踪分支。它正在告诉你你所做的事情;你超前于远程仓库,并提醒你进行推送。


28
这实际上是在我进行推送之后发生的。我不得不拉取(或者可能是获取?)才能使它不再显示那条消息。 - SeanJA

27

我在我的舞台服务器上遇到这个问题,我只进行拉取操作。使用硬重置帮助我将HEAD清理为与远程相同的状态。

git reset --hard origin/master

所以现在我又有了:

On branch master
Your branch is up-to-date with 'origin/master'.

1
我最初尝试了没有使用--hard标志,它起作用了! - kroiz

27

有人说你可能误读了你的消息,但你并没有。这个问题实际上与你的<project>/.git/config文件有关。它会包含一个类似于以下内容的部分:

[remote "origin"]
    url = <url>
    fetch = +refs/heads/*:refs/remotes/origin/*
如果您从项目的.git/config文件中删除fetch行,则可以停止出现“Your branch is ahead of 'origin/master' by N commits.”的烦恼。 我希望是这样。 :)

下次看到“ahead by x commits”消息时,我会检查一下。我有一段时间没看到这个消息了。 - SeanJA
我有一段时间没有看到这条消息了。我想这是因为我开始在本地创建git仓库,然后将其推送到远程仓库,而不是相反的方式... - SeanJA
20
那行代码是做什么用的?如果我把它删除了,除了烦恼之外,还会错过什么? - John Mee
这也解决了我的问题,但是为什么呢?(git pull 没有解决这个问题) - aem999
1
这个方法虽然可行,但更像是抑制错误。如果再加上那一行,你就会再次收到警告。 - Krishna Pandey
显示剩余2条评论

18

注意:在我撰写此答案时,这个特定的问题已经相当老了。它是在 Git 发布修复许多这些问题的版本之前三年发布的。然而,添加现代答案和解释似乎是值得的。现有的被接受的答案建议运行git fetch -p1这是一个好主意,虽然现在不那么经常需要。Git 1.8.2 版本发布之前,它是更加必要的;那个版本发布于原始问题之后三年。


1-p--prune选项不是必需的,仅在链接的答案中建议使用。请参阅下面更长的部分以了解其作用。


这是如何发生的?

最初的问题是:

这是如何发生的?

所谓的“这”是指在执行git push origin master后,OP运行git status并看到消息On branch master,后面跟着Your branch is ahead of 'origin/master' by 1 commit.要正确回答这个问题,我们需要把它分成几个部分。

首先,每个(本地)分支都有一个上游设置

这个说法实际上有点过于强硬了。在你自己的Git仓库中,每个你自己的本地分支可以有一个Git称之为上游的设置。或者,该分支可能没有上游。旧版本的Git在称之为上游设置方面不太一致,但在现代Git中,它更加一致。我们还有git branch --set-upstream-togit branch --unset-upstream用于设置或清除上游。

这些--set-upstream-to--unset-upstream会影响当前分支。当前分支是你使用git checkout或自Git 2.23以来使用的git switch2选择的分支。无论你检出了哪个分支,那就是你所在的分支。3

如果使用--unset-upstream,这将删除当前分支的上游。没有上游,这会停止有关超前或落后或分歧的消息。但是,这条消息旨在有用,因此您可能不应该只删除上游以使其停止发生。(如果您不觉得它有用,可以随意忽略该消息,毕竟它不是一个错误。)

如果运行git branch --set-upstream-to=origin/xyzzy,则将当前分支的上游设置为origin/xyzzy。对于名为xyzzy的分支,这将是典型的正确设置。某些创建分支的操作会自动设置(通常正确的)上游,而有些则不会,因此如果您使用了自动设置正确上游的分支创建操作,则无需执行任何操作。如果您需要不同的上游,或者如果您使用了未设置任何上游的分支创建操作,则可以使用此选项更改上游。

你可以将上游设置为以下内容:

  • 自己的另一个本地分支:git branch --set-upstream-to=experiment 将你自己的本地 experiment 设置为当前分支的上游;或者
  • 你的任何一个远程跟踪名称,例如 origin/main 或者 origin/master 或者 origin/xyzzy。这些是由 git branch -r 输出的名称。Git 将它们称为 远程跟踪分支名称(我喜欢在这里省略“分支”一词),我们稍后会详细讨论。

git status 打印的 ahead、behind、up-to-date 或 diverged 消息是从运行下面这个看起来有点神奇的命令中得出的:

git rev-list --count --left-right $branch...$upstream

这里的$branch是当前分支名称,$upstream是从其上游设置中获取的字符串(来自上面的git branch --set-upstream-to)。两个名称之间有三个点,--count--left-right和三个点都是必需的,以便让git rev-list输出这两个数字。


如果您的Git版本为2.23或更高版本,则建议使用git switch,因为它避免了一些棘手的git checkout行为,这些行为通常会让初学者陷入麻烦(甚至会让Git专家出错)。但是,如果您习惯于使用git checkout,则可以继续使用,因为它仍然受支持。实际问题基本上是git checkout过于强大,可能会意外破坏工作。新的git switch有意减少了功能,不会这样做;“故意破坏我的工作”操作已移至git restore
在Git中,可能处于无分支状态,即Git称之为“游离HEAD”模式。如果使用git checkout,它可能会突然将您置于此模式下(尽管会打印一个大而可怕的警告,因此如果您没有看到可怕的警告,则没有这样做),但如果使用git switch,则必须使用git switch --detach来允许游离HEAD模式。这种模式没有任何问题,只需要小心,一旦进入该模式,不要丢失任何新提交。如果不小心就容易丢失它们。在正常模式下,Git不会像这样丢失新提交。
如果您处于游离HEAD模式,则没有上游分支——根据定义,因为您没有分支——并且此问题中的任何内容都不适用。

可达性

这部分内容比较技术性,我将大部分转交给网站Think Like (a) Git。但是我会在这里概括一下:像

或者这样的分支名称origin/mainorigin/xyzzy这样的远程跟踪名称是Git用来找到提交的方式。Git的核心在于提交。你的分支名称仅在查找你的提交时起作用。当然,如果你无法找到它们,那就有麻烦了,所以你的分支名称很重要。但关键是可达性,这是一个技术术语。

Git存储库中的每个提交都有一个编号,由一串大而且丑陋的十六进制字母和数字组成。这是提交的哈希ID,也是Git实际查找提交的方式。

每个提交包含两个内容:每个源文件的完整快照(以特殊压缩、Git格式化和去重的形式)和一些关于提交本身的信息:例如,元数据告诉我们谁在何时以及为什么(他们的日志信息)。在元数据中,每个提交都保存了先前某些提交的提交编号。这意味着一个提交可以找到另一个早期的提交。

Think Like (a) Git所述,这有点像一辆火车。当你在火车上时,它会向后自动带你到所有先前停靠站。但首先你要找到去火车站的路。Git分支名称也将完成这个任务:它保持了你的分支上最新提交的哈希ID。

我们可以像这样画出来:

... <-F <-G <-H   <--branch

分支名称branch保存着最新提交的哈希ID。我们称这个名称指向该提交。无论实际上是什么样的大而丑陋的哈希ID,我们在此处使用字母H来代替它。 H是一个实际的提交,因此它有一个保存的快照——您的文件和一些元数据。在元数据中,Git保存了一个早期提交的哈希ID。我们将那个早期提交称为G。我们说H指向G。Git可以通过分支名称指针找到H,这给了Git访问提交的权限,包括元数据,因此现在Git具有早期提交G的哈希ID。
当然,G也是一个实际的提交:它有一个保存的快照和一些元数据。在G的元数据中,Git保存了早期提交F的哈希ID。我们说G指向F,现在Git可以使用这个保存的哈希ID找到F
这将一直重复下去,或者说,直到我们到达有史以来的第一个提交。该提交(在这里我们可能称其为A)不向后指向早期提交,因为没有早期提交。
这个可达性的概念基本上是从我们从分支名称branch找到提交H开始,向后工作所发生的事情的总结。我们到达提交H,它向后指向提交G,后者又指向F,依此类推。
分支名称和远程跟踪名称
正如我们刚才提到的,一个分支名称保存了某个提交的原始哈希ID。这使Git能够找到该提交。然而,关于分支名称还有另一个特殊功能。
当您使用git checkoutgit switch进入一个分支,并进行新的提交时,Git会自动更新分支名称存储的哈希ID。也就是说,假设我们有一系列提交,就像这样:
...--F--G--H   <-- xyzzy (HEAD)

我们正在分支{{xyzzy}}上,我喜欢将特殊名称{{HEAD}}附加到它上面表示。当图表中有多个分支名称时,这很有用。请注意,{{H}}目前是最新的提交。但现在我们会按照通常的方式再做一个提交。
这个新的提交会得到一个新的、唯一的、又大又丑的十六进制哈希值,就像任何提交一样。Git确保新的提交向后指向提交{{H}},因为那是我们用来创建新提交的提交。我们将使用字母{{I}}表示这个新提交。让我们把它画出来:
...--F--G--H   <-- xyzzy (HEAD)
            \
             I

这张图片实际上是中间提交状态:Git已经完成了I,但还没有完成git commit操作。现在要问自己一个问题:我们如何才能以后找到提交I?我们需要它的哈希ID。那么我们可以把哈希ID存储在哪里呢?
如果你说:分支名称中,那么你就是对的。实际上,就Git而言,正确的分支名称是你当前所在的分支。在这个图示中,我们已经将HEAD附加到该分支上。因此,作为git commit的最后一部分,Git会将I的哈希ID写入名称xyzzy中。这使得它指向提交I,如下所示:
...--F--G--H
            \
             I   <-- xyzzy (HEAD)

现在图中没有弯曲的原因,所以我们可以将其拉直:

...--F--G--H--I   <-- xyzzy (HEAD)

这就是分支名称的工作原理。最终它非常简单:只需要同时理解几个概念。名称找到提交。它找到最新提交。从那里开始,Git向后工作,因为每个提交都会找到一个更早的提交。
那么远程跟踪名称呢?这里的诀窍在于你的Git与其他Git进行通信。每个Git都有自己的分支名称。你有你的master或main;他们有他们的。你有你的xyzzy分支,他们也可以有自己的分支。
你的Git可能一直打电话给他们的Git,并询问他们的分支名称。然而,这样并不高效,如果你断网了就行不通。无论如何,Git不会这样做。相反,当你的Git调用他们的Git并从他们那里获取所有分支名称和哈希ID的列表时,你的 Git会将这些名称和哈希ID存储在你的存储库中。当你运行git fetch时,就会发生这种情况。
但是存在一个问题。他们的main或master,或者如果他们有一个xyzzy,并不一定意味着与你的main或master或xyzzy相同的提交。然而,解决方案很简单:Git只需将他们的分支名称转换为你的远程跟踪名称。
如果origin的main或master或xyzzy已移动,则只需运行git fetch或git fetch origin,可能还要使用--prune。你的Git调用他们的Git。他们列出他们的分支名称和提交哈希ID。如有必要,您的Git从他们那里获取任何新的提交:他们拥有的,你没有的提交。然后,你的Git将他们的分支名称转换为你的远程跟踪名称,并创建或更新你的远程跟踪名称,以记住在你运行此git fetch时他们的分支名称指向的位置。
如果你使用--prune,这将处理他们删除一些分支名称的情况。假设他们有一个名为oldstuff的分支。您之前获取了它,因此在您的远程跟踪名称中有origin/oldstuff。然后他们删除oldstuff,所以这一次他们...就没有了。如果没有--prune,您的Git会忽略它。即使它现在已经死了,您仍将保留您的旧origin/oldstuff使用--prune,您的Git会说:哦,嗯,这看起来已经失效了并将其修剪掉:在您的 Git中的远程跟踪名称不对应于其中一个他们的分支名称,就会被删除。 < p >修剪选项可能应该始终是默认值,但它不是,因此现在无法成为默认值。6 但是,您可以配置fetch.prunetrue使它成为您的默认值。


4在2021年,这种情况比2010年时更少见。2005年Git首次发布时,这种情况普遍存在。例如,在前往Linux会议的航班上,你以任何价格都无法获得互联网访问。

5选择采用哪些名称,以及何时采用它们,实际上是答案的一部分。在Git中,这已经随着时间的推移而发生了变化,并且仍在略微变化中,尽管仍存在各种限制。我们不会详细介绍所有细节。

6Git通常非常重视向后兼容性。例如,它花费了1.x到2.0的转换来将push.default默认设置从matching更改为simple


如何使用git rev-list获取这两个数字

之前我提到过,git status打印的ahead、behind、up-to-date或diverged信息是通过运行以下命令得到的:

git rev-list --count --left-right $branch...$upstream

这里git rev-list的作用是计算可访问提交的数量。在gitrevisions文档中描述的三点语法产生了集合论中所谓的对称差异。虽然不是数学术语,但我们可以将其视为进行两次提交可达性测试,可以像这样绘制:
          I--J   <-- xyzzy (HEAD)
         /
...--G--H
         \
          K   <-- origin/xyzzy

在这里,提交J可以从你的分支名称xyzzy到达,因为该名称指向那里。提交I可以从提交J到达,因此也计入其中。这将导致回到提交H——正如您从图表中看到的那样,这有点特殊。
同时,提交K可以从您的远程跟踪名称origin/xyzzy到达。提交H可以从K到达。从提交H开始,提交GF等等都是可达的。但是两个“铁路轨道”在提交H处汇合:提交H和所有更早的提交都可以从两个名称到达。
这使得提交I-J很特殊,因为它们仅从名称xyzzy可达,而提交K很特殊,因为它仅从名称origin/xyzzy可达。三个点符号找到这些提交:仅从一个名称或仅从另一个名称可达的提交。
如果我们将分支名称放在左侧,将其上游放在右侧,并使用三个点符号,我们将找到此情况下的所有三个提交。使用--count使git rev-list打印此数字:3。使用--left-right告诉git rev-list变得更加智能:它应该计算由左侧名称——当前分支名称——计算了多少提交,以及由右侧名称——上游计算了多少提交。因此,在两个选项和三个点的情况下,我们得到:
2       1

作为输出,告诉我们在xyzzy上有两个提交不在origin/xyzzy上,而在origin/xyzzy上有一个提交不在xyzzy上。这些是分别在xyzzy上的JI提交以及在origin/xyzzy上的K提交。

如果没有--count选项,git rev-list将列出哈希ID,并加上左侧的<或右侧的>符号。使用git log代替git rev-list,如下所示:

git log --left-right xyzzy...origin/xyzzy

(请注意三个点:查看gitrevisions并搜索Symmetric Difference) 我们将看到显示的三个提交,再次根据需要加上<>前缀。

这是一种简单的方法,可以看到哪些提交在您的分支上,哪些提交在上游分支上。 通常与--decorate--oneline--graph一起使用更有用(在某些情况下,您可能还想添加--boundary)。

提前、落后、分叉或最新

因此,假设我们运行了:

git rev-list --count --left-right $branch...$upstream

(或者-再次查看gitrevisions-在这里使用$branch@{upstream})并获得我们的两个计数。它们可以是:

  • 00:我们的分支名称和我们的远程跟踪名称(或上游中的任何内容)指向相同的提交。没有人领先或落后。 git status命令将显示Your branch is up to date with '<upstream>'

  • 非零,零:当前分支上有未在上游分支上的提交。上游分支上没有未在当前分支上的提交。因此,我们的分支领先于上游。

  • 零,非零:当前分支上没有未在上游分支上的提交,但上游分支上有一些未在当前分支上的提交。这意味着我们的分支落后于上游。

  • 非零,非零:这就像我上面画的图表。当前分支及其上游同时领先和落后于彼此。 git status命令将使用单词diverged

现在我们将回到最初的问题。假设当前分支的上游是一个远程跟踪名称。请注意,git rev-list得到的计数是基于我们远程跟踪分支名称中的内容。

这实际上是如何发生的?

在OP的情况下,只有一个人正在创建新提交并使用git push将其发送。如果我是唯一的人,我可能会从GitHub git clone一些东西,然后进行一两个新的提交和git push origin master

在现代Git中,git status会告诉我我已经更新了。在非常旧的Git版本中,git status现在会告诉我我的master领先于origin/master原因很简单:在旧时代,git push未能更新origin/master。运行git fetch origin或者只是git fetch让你自己的Git调用GitHub上的Git,读取他们的信息,并意识到你的git push已经起作用了。

当你运行git push时,你的Git会调用另一个Git。然后,你的Git向其他Git提供他们没有但需要完成git push的任何新提交。他们接收这些提交并将它们放在某个地方7。然后,你的Git会礼貌地询问他们的Git(默认情况下)是否可以将分支名称设置为最新提交的哈希ID,就像在你的分支名称中看到的那样。这里没有远程跟踪的内容。你只是要求他们设置与你使用的相同的名称

一般来说,如果你只是向他们的存储库添加新的提交,则此类礼貌请求将成功。如果你要求他们“删除”某些提交,则会出现“非快进”的错误。如果你是唯一一个向他们发送新提交的人,则他们永远不会有任何损失,因此这应该始终有效8

如果推送失败,则你的Git保持远程跟踪名称不变是适当的。你的Git从他们的Git那里没有得到更新它所需的信息。但是,如果推送成功...他们只是将他们的分支名称设置为你的Git要求使用的哈希ID。因此,现在你的Git知道他们的分支名称指向哪里。你的Git应该更新你的远程跟踪名称。

在旧的Git版本中,你的Git只是没有费心做这件事。在Git 1.8.2版本中,Git作者终于解决了这个问题:成功的git push将使你的Git根据他们的Git同意的更新来更新远程跟踪名称。因此,这种情况不再经常发生。


在过去,他们直接将它们放入存储库中。在现代Git中,他们将其放入隔离区,仅在实际接受新提交时才将其迁移到存储库中。
当然,像GitHub这样的地方也提供了诸如“受保护的分支”之类的功能,例如拒绝每个推送。我们还可以发明更复杂的场景,比如当您有多台计算机并忘记通过计算机A创建和推送新提交,现在尝试从计算机B推送时。

如果你不是唯一一个进行git push的人怎么办

假设Alice和Bob都克隆了某个GitHub存储库。开发在这个存储库中发生在分支dev(代表develop)上。因此,Alice从她的origin/dev创建了自己的dev分支:

...--G--H   <-- dev (HEAD), origin/dev  [Alice's computer]

鲍勃同样也自己制作{{他的}}dev

...--G--H   <-- dev (HEAD), origin/dev  [Bob's computer]

GitHub仓库dev也以H结尾(它们没有origin/dev:GitHub仓库不需要远程跟踪名称)。
Alice进行了新的提交,我们称之为I,并像这样在Alice的计算机上绘制:
          I   <-- dev (HEAD)
         /
...--G--H   <-- origin/dev

同时,Bob进行了一次新的提交,我们称之为J

...--G--H   <-- origin/dev
         \
          J   <-- dev (HEAD)

现在Alice和Bob都尝试执行git push origin dev。其中一个人先到达了目的地,可能是Alice。
...--G--H--I   <-- dev  [GitHub's systems]

Bob发送提交J,在GitHub上看起来像这样:

          I   <-- dev
         /
...--G--H
         \
          J   ... polite request to set dev to J

如果GitHub这样做,就会“丢失”Alice的提交I,因为Git通过从名称开始向后工作来查找提交。因此,他们拒绝了带有“不是快进”的投诉的推送。
Bob现在需要将提交I从GitHub中拉入自己的存储库,以便Bob看到:
          I   <-- origin/dev
         /
...--G--H
         \
          J   <-- dev (HEAD)  [Bob's computer]

Bob应该使用git fetchgit fetch origin来完成此操作,也许使用--prune(或将fetch.prune设置为true)。现在,当Bob运行git status时,他将收到“diverged”消息。
现在由Bob作为推送竞赛的输家,需要想办法将他的工作(提交J)与Alice的工作(提交I)结合起来。有不同的方法来合并工作。两种主要的方法是git mergegit rebase。我们不会在这里讨论谁应该在何时以及为什么做什么,只是提到这是另一种可能遇到“diverged”状态的方式,即当您认为自己严格领先于其他Git时。

12

我的情况是因为我使用了 master 分支:

 git checkout -B master
只需拉取其新版本而不是原来的版本。
 git checkout master

第一个命令将主分支的头指针重置为我最新的提交

我使用了

git reset --hard origin/master

为了解决这个问题


12

我浏览了这个页面上的每一个解决方案,幸运的是,@anatolii-pazhyn发表了评论,因为他的解决方案是有效的。不幸的是,我没有足够的声望来给他点赞,但我建议首先尝试他的解决方案:

git reset --hard origin/master

这给了我:

HEAD is now at 900000b Comment from my last git commit here

我还推荐:

git rev-list origin..HEAD
# to see if the local repository is ahead, push needed

git rev-list HEAD..origin
# to see if the local repository is behind, pull needed
你还可以使用:

git rev-list --count --left-right origin/master...HEAD
# if you have numbers for both, then the two repositories have diverged

祝你好运


1
注意!reset --hard origin/master会销毁你本地的提交(那些你还没有推送的)! - neu242

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