Sea*_*nJA 355 git git-commit
这实际上是怎么产生的?
我现在正在自己的一个回购工作,所以这是我的工作流程:
然后,当我这样做时,git status它告诉我我的分支是由X提交提前(可能与我提交的提交数量相同).是因为当您推送代码时它实际上并不更新本地缓存的文件(在.git文件夹中)?git pull似乎'修复'这个奇怪的消息,但我仍然好奇它为什么会发生,也许我使用git错了?
包括在消息中打印的分支
我当地的分公司领先于大师
你在哪里推/拉当前分支
我正在推送GitHub并拉到我当时正在处理的任何一台计算机上,我的本地副本总是完全是最新的,因为我是唯一一个正在处理它的人.
它实际上并没有检查远程仓库
这就是我的想法,我想我会确保我对它的理解是正确的.
你传递一些额外的论据吗?
不是我能看到的,也许我的结尾有一些有趣的配置?
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
nothing to commit (working directory clean)
Run Code Online (Sandbox Code Playgroud)
Ric*_*ich 487
如果您在执行此操作后收到此消息git pull remote branch,请尝试使用git fetch.(可选,运行git fetch -p以从repo中删除已删除的分支)
Fetch似乎更新了远程分支的本地表示,这在您执行时不一定会发生git pull remote branch.
Mar*_*lea 123
使用
git pull --rebase
Run Code Online (Sandbox Code Playgroud)
--rebase选项意味着git会将您的本地提交移到一边,与远程同步,然后尝试从新状态应用您的提交.
Abh*_*oel 68
第1步:git checkout <branch_name>
第2步 :git pull -s recursive -X theirs
第3步 :git reset --hard origin/<branch_name>
更多细节:https://stackoverflow.com/a/39698570/2439715
请享用.
Jos*_*Lee 50
我认为你误读了这个消息 - 你的分支并没有超前master,它是 master.它是领先的origin/master,这是一个远程跟踪分支,它记录从上次远程仓库的状态push,pull或fetch.它告诉你你到底做了什么; 你领先遥控器,它提醒你推动.
小智 34
这对我有用
git reset --hard origin/master
输出必须看起来像
On branch dev
HEAD is now at ae1xc41z Last commit message
Fak*_*hid 26
有人说你可能误读了你的信息,你不是.这个问题实际上与您的<project>/.git/config文件有关.在它中将是类似于这样的部分:
[remote "origin"]
url = <url>
fetch = +refs/heads/*:refs/remotes/origin/*
Run Code Online (Sandbox Code Playgroud)
如果从项目的.git/config文件中删除fetch行,则会通过N提交停止"你的分支超过'origin/master' ." 发生的烦恼.
或者我希望如此.:)
Ana*_*hyn 11
我在我的舞台服务器上遇到过这个问题.硬复位帮我清理HEAD和遥控器一样.
git reset --hard origin/master
Run Code Online (Sandbox Code Playgroud)
所以现在我又来了:
On branch master
Your branch is up-to-date with 'origin/master'.
Run Code Online (Sandbox Code Playgroud)
在我的情况下,这是因为我切换到掌握使用
git checkout -B master
Run Code Online (Sandbox Code Playgroud)
只是拉它的新版本而不是
git checkout master
Run Code Online (Sandbox Code Playgroud)
第一个命令将master的头重置为我最近的提交
我用了
git reset --hard origin/master
Run Code Online (Sandbox Code Playgroud)
解决这个问题
我在这个页面上完成了每个解决方案,幸运的是@ anatolii-pazhyn评论说,因为他的解决方案是有效的.不幸的是,我没有足够的声誉来支持他,但我建议先尝试他的解决方案:
git reset --hard origin/master
Run Code Online (Sandbox Code Playgroud)
哪个给了我:
HEAD is now at 900000b Comment from my last git commit here
Run Code Online (Sandbox Code Playgroud)
我还建议:
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
Run Code Online (Sandbox Code Playgroud)
您还可以使用:
git rev-list --count --left-right origin/master...HEAD
# if you have numbers for both, then the two repositories have diverged
Run Code Online (Sandbox Code Playgroud)
祝你好运
注意:这个特定的问题在我写这个答案的时候已经很老了。它是在第一个版本的 Git 发布前三年发布的,该版本修复了许多这些问题。 不过,似乎值得在解释器的同时添加现代答案。 现有接受的答案建议运行git fetch -p, 1这是一个好主意,尽管这些天很少需要。在 Git 版本 1.8.2 出来之前,这是非常必要的;Git 是在最初的问题三年后发布的。
1本-p或--prune选项不是必需的,建议只在括号中的链接的答案。请参阅下面较长的部分,了解它的作用。
原问题问:
这实际上是如何发生的?
有问题的this是这样一个事实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-to和git branch --unset-upstream用于设置或清除上游。
这些--set-upstream-to并--unset-upstream影响当前分支。当前分支是您所在的分支on,无论何时或无论它git status说on branch xyzzy什么。你选择这个分支,git checkout或者——从 Git 2.23 开始—— git switch。2 无论您签出哪个分支,那都是您所在的分支。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打印的前面、后面、最新或发散的消息来自运行有点神奇的命令:
git rev-list --count --left-right $branch...$upstream
Run Code Online (Sandbox Code Playgroud)
其中$branch是当前分支名称,$upstream是来自其上游设置(从git branch --set-upstream-to上面)的字符串。这里的两个名字之间有三个点,而--count、--left-right、 和三个点都是git rev-list吐出这两个数字所必需的。
2如果您有 Git 2.23 或更高版本,最好改用 Git,git switch因为它可以避免一些棘手的git checkout行为,这些行为历来会导致初学者陷入困境(有时甚至会绊倒 Git 专家)。但是,如果您习惯了git checkout,则可以根据需要继续使用它,因为它仍然受支持。真正的问题基本上git checkout是过于强大,可能会意外破坏工作。新git switch的故意不那么强大,不会这样做;“故意破坏我的工作”行动被转移到git restore.
3在 Git 称为分离 HEAD模式下,可能没有分支。如果你使用它可以让你突然进入这种模式(虽然它打印了一个很大的可怕警告,所以如果你没有看到可怕的警告,它没有这样做),但是如果你使用,你必须允许detached-HEAD模式与. 这种模式没有任何问题,你只需要在进入后小心,不要丢失你所做的任何新提交。如果您不小心,很容易丢失它们。在正常模式下,Git 不会像这样丢失新的提交。git checkoutgit switchgit switch --detach
如果您处于分离的 HEAD 模式,则您没有上游(根据定义,因为您没有分支)并且此问题中的任何内容都不适用。
这部分有点技术性,我会将大部分内容外包给一个网站Think Like (a) Git。我将在这里总结如下:分支名称(如main或xyzzy)和远程跟踪名称(origin/main, origin/xyzzy)是 Git查找提交的方式。Git 是关于提交的。您的分支名称仅对查找您的提交很重要。当然,如果你找不到它们,你就有麻烦了,所以你的分支名称很重要。但关键是可达性,这是一个技术术语。
Git 存储库中的每个提交都有编号,带有一个由字母和数字组成的大而丑陋的十六进制字符串。这是提交的哈希 ID,也是 Git真正找到提交的方式。
每个提交包含两件事:每个源文件的完整快照(以特殊的、压缩的、Git 化的和重复数据删除的形式),以及关于提交本身的一些信息:元数据,告诉谁提交、何时提交以及为什么提交(他们的日志消息),例如。在元数据中,每个提交都保存了一些较早提交的提交编号。这意味着一个提交可以找到另一个更早的提交。
正如Think Like (a) Git指出的那样,这有点像火车。一旦你上了火车,那就太好了,在这种情况下,它会自动带你倒退到所有较早的火车停靠站。但首先你必须找到去火车站的路。Git 分支名称将执行此操作:它保存分支上最新提交的哈希 ID 。
我们可以这样画:
... <-F <-G <-H <--branch
Run Code Online (Sandbox Code Playgroud)
分支名称branch包含最新提交的哈希 ID 。我们说名称指向提交。无论真正的丑陋哈希 ID 是什么,我们只是H在这里用这个字母来代替它。
H是一个实际的提交,所以它有一个保存的快照——你的文件——和一些元数据。在元数据中,Git 保存了较早提交的哈希 ID 。我们将调用较早的 commit G。我们说,H 指向 G。Git 可以H通过分支名称指针找到,这使 Git 可以访问提交,包括元数据,因此现在 Git 具有较早提交的哈希 ID G。
G当然也是一个实际的提交:它有一个保存的快照和一些元数据。在 的元数据中G,Git 保存了较早提交的哈希 ID F。我们说G指向F,现在 Git 可以F使用这个保存的哈希 ID找到。
这将永远重复,或者更确切地说,直到我们进行有史以来的第一次提交。那个提交(大概我们A在这里称之为)不会向后指向更早的提交,因为没有更早的提交。
可达性的概念基本上是对如果我们从 commit 开始(H通过分支名称找到)branch然后向后工作会发生什么的总结。我们到达 commit H,向后到达 commit G,返回到F,依此类推。
正如我们刚刚提到的,分支名称保存了某个提交的原始哈希 ID。这让 Git找到那个提交。不过,还有一个关于分支名称的特殊功能。
当您使用git checkout或git switch获得的一个分支,然后做出新的承诺,Git会自动更新分支名称的存储的哈希ID。也就是说,假设我们有一系列这样的提交:
...--F--G--H <-- xyzzy (HEAD)
Run Code Online (Sandbox Code Playgroud)
我们在“on” branch xyzzy,我喜欢通过附加特殊名称HEAD来表示它。当图表中有多个分支名称时,这很有用。请注意,H目前是最新的提交。但是现在我们将按照通常的方式制作另一个。
这个新提交获得一个新的、唯一的、又大又丑的十六进制哈希 ID,就像任何提交一样。Git 确保新提交向后指向 commit H,因为这是我们用来进行新提交的提交。我们将使用这个字母I来代表这个新的提交。让我们把它画进去:
...--F--G--H <-- xyzzy (HEAD)
\
I
Run Code Online (Sandbox Code Playgroud)
这张图片实际上是在提交中:Git 已经制作了I,但尚未完成git commit操作。问问自己这个问题:我们以后如何找到提交I?我们将需要它的哈希 ID。我们可以在哪里存储哈希 ID?
如果你说:在一个分支名称中,你是对的。事实上,正确的分支名称——无论如何,就 Git 而言——就是你现在“在”的那个。这就是我们HEAD在这张图中附加的那个。所以现在,作为 的最后一部分git commit,Git 将I的哈希 ID 写入 name xyzzy。这使它指向 commit I,如下所示:
...--F--G--H
\
I <-- xyzzy (HEAD)
Run Code Online (Sandbox Code Playgroud)
现在绘图中没有任何扭结的原因,所以我们可以理顺它:
...--F--G--H--I <-- xyzzy (HEAD)
Run Code Online (Sandbox Code Playgroud)
这就是分支名称的工作方式。最后,这真的很简单:它只需要同时解决几件事。该名称查找提交。它找到最新的提交。从那里开始,Git向后工作,因为每次提交都会找到较早的提交。
远程跟踪名称怎么样?嗯,这里的技巧是你的 Git 与其他一些 Git 对话。 每个 Git 都有自己的分支名称。 你有你的 master或main; 他们有他们的。你有你的 xyzzy分行,他们也可以有他们的分行。
你的 Git可以随时、每次都调用他们的 Git,并询问他们的分支名称。但是,这不是很有效,并且如果您与 Internet 断开连接,则不起作用。4 无论如何,Git 不会这样做。相反,当您的 Git 调用他们的 Git 并从他们那里获取所有分支名称和散列 ID 的列表时,您的Git 会获取这些名称和散列 ID 并将它们存储在您的存储库中。运行时会发生这种情况git fetch。5
不过有个问题。他们main或master,或者他们xyzzy如果他们有一个,并不一定意味着在相同的承诺为您 main或master或xyzzy。不过,解决方案很简单:Git 只需将其分支名称转换为您的远程跟踪名称即可。
如果origin'smain或master或xyzzy已经移动,您只需运行git fetchor git fetch origin,也许使用--prune。你的 Git 调用他们的 Git。他们列出了他们的分支名称和提交哈希 ID。如有必要,您的 Git 会从他们那里获得任何新的提交:他们有的提交,而您没有的。然后你的Git变成自己的分支机构名称到您的远程跟踪名称和创建或更新您的远程跟踪名称来记住他们的分支名称指出,在你运行此时刻git fetch。
如果您使用--prune,这将处理他们删除某些分支名称的情况。假设他们有一个名为oldstuff. 你早点得到它,所以你有origin/oldstuff你的远程跟踪名称。然后他们删除了 oldstuff,所以这次他们......只是不再拥有它了。如果没有--prune,您的 Git 会忽略这一点。origin/oldstuff即使它现在已经死了,你仍然保持旧的。 使用 --prune,您的 Git 会说:哦,嗯,这现在看起来已经死了,并将其修剪掉:您的Git 中的远程跟踪名称与其分支名称之一不对应,只会被删除。
prune 选项可能应该一直是默认值,但它不是,因此现在不能是。6 你可以,但是,配置fetch.prune到true和使它的默认值。
4与 2010 年相比,现在 2021 年这种情况不太常见。在 2005 年 Git 首次发布时,这种情况要普遍得多。它曾经是,比如说,在航班上为Linux的会议,你无法得到的情况下任何接入互联网,任何代价。
5选择取哪些名字以及何时取它们实际上是这里答案的一部分。随着时间的推移,它在 Git 中发生了变化——并且仍然在发生一些变化——尽管仍然存在各种限制。不过,我们不会详细介绍所有细节。
6 Git 通常非常重视向后兼容性。花了1.x版本2.0的转换来改变push.default从默认设置matching到simple,例如。
git rev-list得到两个数字早些时候,我注意到git status打印的提前、落后、最新或发散的消息来自运行:
git rev-list --count --left-right $branch...$upstream
Run Code Online (Sandbox Code Playgroud)
是什么git rev-list在这里所做的是数可达提交。gitrevisions 文档中描述的三点语法产生了集合论中所谓的对称差异。但是,在非数学术语中,我们可以将其视为进行两次提交可达性测试,我们可以这样绘制:
I--J <-- xyzzy (HEAD)
/
...--G--H
\
K <-- origin/xyzzy
Run Code Online (Sandbox Code Playgroud)
在这里,J可以从您的分支名称访问commit xyzzy,因为名称指向那里。CommitI可以从 commit 到达J,所以它也很重要。这导致提交H——正如您从图中看到的那样,这有点特别。
同时,commitK可以从您的远程跟踪名称访问origin/xyzzy。提交H可从K. 从提交H回来,提交G和F等等皆是也可到达。但是两条“铁路轨道”在 commit 时连接起来H:commitH和所有较早的提交都可以从这两个名称访问。
这使得提交变得I-J特别,因为它们 * 只能从 name 到达xyzzy,而K特殊之处在于它 * 只能从 name 到达origin/xyzzy。三点符号查找这些提交:只能从一个名称或只能从另一个名称访问的提交。
如果我们将分支名称放在左侧,将其上游放在右侧,并使用三点表示法,我们将在这种情况下找到所有这三个提交。使用--count使git rev-list打印这个数字: 3. 使用--left-right告诉git rev-list更聪明:它应该计算由于左侧名称(当前分支名称)而计数的提交数量,以及由于右侧名称而计数的提交数量,即上游的。因此,通过这两个选项和三个点,我们得到:
2 1
Run Code Online (Sandbox Code Playgroud)
作为输出,告诉我们有两个提交xyzzy不在 on 上origin/xyzzy,一个在上提交origin/xyzzy不在 on 上xyzzy。这些分别是提交J- 和 - I(on xyzzy) 和K(on origin/xyzzy)。
如果没有该--count选项,git rev-list将列出哈希 ID,以<(左)或>(右)符号为前缀。使用git log代替git rev-list,如:
git log --left-right xyzzy...origin/xyzzy
Run Code Online (Sandbox Code Playgroud)
(再次注意三个点:参见gitrevisions并搜索 Symmetric Difference)我们将得到显示的三个提交,再次以<或>为前缀。
这是查看哪些提交在您的分支上以及哪些提交在上游的简单方法。 它通常与--decorate, --oneline, and 一起使用--graph(并且--boundary在某些情况下您可能还想添加)。
所以,假设我们已经运行:
git rev-list --count --left-right $branch...$upstream
Run Code Online (Sandbox Code Playgroud)
(或者——再次查看gitrevisions——$branch@{upstream}在右边使用)并得到我们的两个计数。这些可以是:
0and 0:我们的分支名称和我们的远程跟踪名称(或上游的任何名称)指向同一个提交。没有人领先或落后。该git status命令会说Your branch is up to date with '<upstream>'。
非零,零:当前分支上有不在上游的提交。上游没有不在当前分支上的提交。所以我们的分支领先于上游。
零,非零:当前分支上没有不在上游的提交,但上游有一些不在当前分支上的提交。这意味着我们的分支在上游后面。
非零,非零:这就像我上面画的图。当前分支及其上游同时相互超前。该git status命令将使用单词diverged。
我们现在要跳回到最初的问题。让我们假设当前分支的 upstrema 是一个远程跟踪名称。 请注意,获得的计数git rev-list基于我们的远程跟踪分支名称中的内容。
在 OP 的场景中,只有一个人在进行新的提交并使用git push. 如果我是一个人,我可能会git clone从 GitHub 中获取一些东西,然后进行一两个新的提交,然后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 文件git push。他们接受这些提交并将它们放在某个地方。7 然后你的 Git 询问他们的 Git——默认情况下礼貌地询问——如果他们愿意,请,如果可以的话,设置他们的 分支名称以引用最新的提交,通过其哈希 ID,如你的分支名称所示。这里没有远程跟踪的东西。您只是要求他们设置您正在使用的相同名称。
作为一般规则,如果您只是向他们的存储库添加新提交,这种礼貌的请求会成功。如果你要求他们“丢失”一些提交,它就会失败:你会收到关于这是“非快进”的抱怨。如果您是唯一一个向他们发送新提交的人,那么他们永远不会以这种方式丢失任何东西,因此这应该始终有效。8
如果推送失败,则您的 Git 保持远程跟踪名称不变是合适的。你的 Git 从来没有从他们的 Git 那里得到过让你的 Git 更新它的信息。但是如果推送成功......好吧,他们只是将他们的分支名称设置为您的 Git 要求他们使用的哈希 ID。所以现在你的 Git 知道他们的分支名称指向哪里。您的 Git 应该更新您的远程跟踪名称。
在旧的 Git 版本中,您的 Git 只是懒得这样做。在 Git 版本 1.8.2 中,Git 作者终于解决了这个问题:git push基于他们的 Git 同意您的 Git 提供的更新这一事实,成功地让您的 Git 更新了您的远程跟踪名称。所以这样的事情不会再发生了。
7在过去的坏日子里,他们直接把它们放进自己的仓库。在现代 Git 中,他们将它们放在隔离区,并且只有在它们真正接受新提交时才将它们迁移到它们的存储库中。
8当然,像 GitHub 这样的地方也提供了诸如protected 分支之类的功能,例如,它对每次推送都说不。我们还可以发明更有趣的场景,例如当您有多台计算机并忘记您通过计算机 A 进行和推送新提交时,现在尝试从计算机 B 推送。
git push假设 Alice 和 Bob 都克隆了一些 GitHub 存储库。此存储库中的开发发生在分支上dev(用于开发)。所以爱丽丝dev从她那里创造了她自己的origin/dev:
...--G--H <-- dev (HEAD), origin/dev [Alice's computer]
Run Code Online (Sandbox Code Playgroud)
鲍勃,同样,让他自己dev:
...--G--H <-- dev (HEAD), origin/dev [Bob's computer]
Run Code Online (Sandbox Code Playgroud)
GitHub 存储库也dev以 结束H。(他们没有origin/dev:GitHub 存储库不关心远程跟踪名称。)
Alice 进行一个新的提交,我们称之为I,并在 Alice 的计算机上绘制如下:
I <-- dev (HEAD)
/
...--G--H <-- origin/dev
Run Code Online (Sandbox Code Playgroud)
同时,Bob 进行了一次新的提交,我们称之为J:
...--G--H <-- origin/dev
\
J <-- dev (HEAD)
Run Code Online (Sandbox Code Playgroud)
现在 Alice 和 Bob 都尝试git push origin dev。其中一个首先到达那里——也许是爱丽丝:
...--G--H--I <-- dev [GitHub's systems]
Run Code Online (Sandbox Code Playgroud)
Bob 向JGitHub发送 commit ,如下所示:
I <-- dev
/
...--G--H
\
J ... polite request to set dev to J
Run Code Online (Sandbox Code Playgroud)
如果 GitHub 这样做,这将“丢失” Alice 的 commit I,因为 Git通过从名称开始向后工作来查找提交。因此,他们以“不是快进”的抱怨拒绝了这一推动。
Bob 现在需要将提交I从 GitHub 拉到 Bob 自己的存储库中,以便Bob看到:
I <-- origin/dev
/
...--G--H
\
J <-- dev (HEAD) [Bob's computer]
Run Code Online (Sandbox Code Playgroud)
Bob 应该使用git fetchor git fetch origin,也许使用--prune(或使用fetch.pruneset to true)来做到这一点。 现在,当 Bob 运行时git status,他将收到“发散”消息。
现在由 Bob 作为推动竞赛的输家决定如何将他的工作 (commit J) 与 Alice 的工作 (commit )结合起来I。有不同的方式来结合工作。两个主要的是git merge和git rebase。我们不会在这里讨论谁应该做什么、什么时候做以及为什么做,只是事实是当您认为自己完全领先于其他某个 Git 时,您可能会遇到“分歧”状态的另一种方式。
| 归档时间: |
|
| 查看次数: |
311771 次 |
| 最近记录: |