为什么要使用git branch --unset-upstream来修复问题?

232

我在git高级操作方面更像是个新手。我使用博客框架Octopress来维护我的blog。虽然Octopress自2011年以来没有进行任何开发,但它能够很好地满足我的需求,所以我一直没有考虑更改任何东西。

顺便提一下,我的博客托管在Github Pages上。

今天,在撰写一篇新文章时,git status显示了以下消息:

On branch source
Your branch is based on 'origin/master', but the upstream is gone.
  (use "git branch --unset-upstream" to fixup)

相同的消息将重复出现在所有后续命令中,例如git add .git commit -m 'message'git push origin source
  • 这个消息是什么意思?
  • 有东西坏了吗?
  • 如果有,是什么?
  • 我需要修复它吗?

如果可能,请指向一个PDF/网络文章,让我可以阅读并理解它以备将来使用。

更多细节:

bash-3.2$ git branch -a
* source
  remotes/octopress/2.1
  remotes/octopress/HEAD -> octopress/master
  remotes/octopress/gh-pages
  remotes/octopress/linklog
  remotes/octopress/master
  remotes/octopress/refactor_with_tests
  remotes/octopress/rubygemcli
  remotes/octopress/site
  remotes/origin/source

如果需要更多信息,请告知我。谢谢。

7个回答

253
TL;DR版本:远程跟踪分支origin/master曾经存在,但现在不存在了,因此本地分支source正在跟踪一个不存在的东西,这至少是可疑的——这意味着不同的Git功能无法为您做任何事情——Git正在警告您。 没有必要让"上游跟踪"功能按预期工作,您可以自行决定是否更改任何内容。
关于上游设置的另一种方法,请参见为什么我需要“git push --set-upstream origin ”?
这个警告是 Git 中的新功能,首次出现在 Git 1.8.5 中。发布说明仅包含一个短点项:

  • "git branch -v -v"(以及 "git status")没有区分一个不基于任何其他分支的分支、与其上游分支同步的分支和配置了不存在的上游分支的分支。

要描述它的意思,您首先需要了解 "远程"、"远程跟踪分支" 以及 Git 如何处理 "跟踪上游"。("远程跟踪分支" 是一个非常有缺陷的术语——我已经开始使用 "远程跟踪名称" 来代替,我认为这是一个略微的改进。然而,在下面,我将使用 "远程跟踪分支" 来保持与 Git 文档的一致性。)

每个“远程”只是一个名称,比如在这种情况下的originoctopress。它们的目的是记录像从中获取git fetchgit pull更新的完整URL这样的事情。当您使用git fetch remote,1 Git会使用保存的URL到达该远程并带来适当的更新集。它还使用“远程跟踪分支”记录更新。

“远程跟踪分支”(或远程跟踪名称)只是一条记录某个“远程”上最后看到的分支名称。每个远程本身都是一个Git存储库,因此它具有分支。在本地存储库中,记录了远程“origin”的分支为remotes/origin/。您显示的文本说明在origin上有一个名为source的分支,在octopress上有名为2.1linklog等的分支。

(当然,“正常”或“本地”分支只是您在自己的存储库中创建的分支名称。)

最后,你可以设置一个(本地)分支来“追踪”一个“远程跟踪分支”。一旦本地分支L被设置为追踪远程跟踪分支R,Git将称R为其“上游”,并告诉你当前分支相对于上游(提交方面)是“超前”还是“落后”。通常情况下(甚至推荐),本地分支和远程跟踪分支使用相同的名称(除了远程前缀部分),例如sourceorigin/source,但这并非必需。 在这种情况下,这种情况并没有发生。你有一个本地分支source跟踪着一个远程跟踪分支origin/master

你不需要知道Git如何设置本地分支来跟踪远程分支的具体机制,但在下面的内容中它们是相关的,所以我会展示一下它是如何工作的。我们从你的本地分支名称source开始。使用这个名称有两个配置条目,拼写为branch.source.remotebranch.source.merge。从你展示的输出中可以清楚地看到,这两个都已经设置好了,因此如果你运行给定的命令,你将会看到以下内容:

$ git config --get branch.source.remote
origin
$ git config --get branch.source.merge
refs/heads/master

综合起来,这告诉Git你的分支source跟踪你的“远程跟踪分支”origin/master

但是现在看一下git branch -a的输出,它显示了存储库中所有本地和远程跟踪分支的名称。远程跟踪名称列在remotes/下面...而且没有remotes/origin/master。可能曾经有过,但现在已经消失了。

Git告诉你可以使用--unset-upstream删除跟踪信息。这将清除branch.source.originbranch.source.merge,并停止警告。

不过,你想要的很可能是从跟踪origin/master切换到跟踪其他东西:可能是origin/source,但也可能是octopress/之一。

你可以使用git branch --set-upstream-to来实现,例如:

$ git branch --set-upstream-to=origin/source

假设您仍在“source”分支上,并且origin/source是您想要的上游 - 如果有任何一个,我无法告诉您实际想要哪个。请参见如何使现有的Git分支跟踪远程分支?
我觉得您到达这里的方式是,当您第一次执行git clone时,克隆源有一个master分支。您也有一个master分支,它被设置为跟踪origin/master(这是Git的正常标准设置)。这意味着您已经设置了branch.master.remotebranch.master.merge,分别为originrefs/heads/master。但是,然后您的origin远程更改其名称从mastersource。为了匹配,我相信您还将本地名称从master更改为source。这将更改您的设置名称,从branch.master.remote更改为branch.source.remote,从branch.master.merge更改为branch.source.merge,但是保留了旧值,所以branch.source.merge现在是错误的。

在这个时候,“上游”链接断开了,但是在 Git 1.8.5 之前的版本中,Git 没有注意到这个断开的设置。现在你有了 1.8.5,它会指出这一点。


这基本上回答了大部分问题,但没有回答“我需要修复它吗”的问题。你可能已经绕过了这个问题很多年,通过执行git pull remote branch(例如,git pull origin source)。如果你一直这样做,它将继续解决问题,所以不,你不需要修复它。如果你愿意,你可以使用--unset-upstream来删除上游并停止投诉,并且不需要将本地分支source标记为具有任何上游。
设置上游的目的是使各种操作更加方便。例如,如果正确设置了上游,接下来执行git fetchgit merge通常会“做正确的事情”,并且在git fetch后执行git status将告诉您您的repo是否与上游分支匹配。
如果你想要这种便利,重新设置上游即可。

1git pull使用git fetch,从Git 1.8.4开始,这也更新了“远程跟踪分支”的信息。在旧版本的Git中,使用git pull没有将更新记录在远程跟踪分支中,只有使用git fetch才行。由于您的Git必须至少是1.8.5版本,所以这对您来说不是问题。

2好吧,还有一行配置我故意忽略了,它可以在remote.origin.fetch下找到。Git必须映射“合并”名称以确定远程分支的完整本地名称为refs/remotes/origin/master。虽然映射几乎总是像这样工作,但它预测到master会转到origin/master

3 或者,使用 git config。如果您只想将上游设置为 origin/source,那么唯一需要更改的部分是 branch.source.merge ,而 git config branch.source.merge refs/heads/source 就可以做到这一点。但是,--set-upstream-to 表示您想要完成的任务,而不是让您手动去完成,这是一种“更好的方法”。


3
如果你愿意,可以使用 "--unset-upstream" 命令将本地分支标记为没有任何上游分支。 - BeatriceThalo
我喜欢这个答案的所有内容,除了它没有解决我的问题。HEAD看起来像:<commit_id> (HEAD->master, m/master, origin/master, local-branch),我很确定m/master是虚假的,因为当列出所有分支时,它显示为remotes/m/master -> origin/local-branch。解决方法仍然有效,但实际上解决问题会更好。 - taranaki
1
@taranaki:remotes/m/master(如 git branch -a 所示)表示这是一个远程跟踪名称。它不是分支名称,因此没有上游设置。如果显示为 remotes/m/master -> origin/local-branch,那么这个特定的远程跟踪名称是一个符号引用。有人使用 git symbolic-ref 创建了它。您可以使用 git branch -r -d m/master 删除该符号引用。 - torek
@taranaki:在Git中,唯一百分之百正确工作的符号引用是名为“HEAD”的引用,因此创建一个符号引用很少是一个好主意。然而,这在多年来已经得到了很大改善。过去的情况是,如果你有一个符号引用并尝试删除它,Git会错误地删除其目标! - torek

199

torek的答案可能非常完美,但为了记录,我想提及另一种情况,这种情况不同于原始问题描述的情况,但是同样会出现相同的错误(因为它可能帮助其他遇到类似问题的人):

我在其中一个服务器上使用git init --bare创建了一个空(新)存储库。然后我将其克隆到我的个人计算机上的本地工作区。

在本地存储库提交了单个版本后,在调用git status后出现了该错误。

按照torek的答案,我理解发生的情况是本地工作目录存储库上的第一次提交创建了"master"分支。但在远程存储库(位于服务器上)中从未有过任何内容,因此甚至没有"master"(remotes/origin/master)分支。

从本地存储库运行git push origin master后,远程存储库最终拥有了一个主分支。这使得错误不再出现。

因此,总结一下-对于一个全新的远程存储库,由于它没有包括“主”在内的分支,因此可能会出现这种错误。


9
这是目前此错误信息的排名第一搜索结果:On branch source Your branch is based on 'origin/master', but the upstream is gone. (use "git branch --unset-upstream" to fixup),虽然有些主观性,但更可能是由于克隆了一个空仓库导致的,因此在这里提供另一种解答是很好的。 - Bella
7
我认为这就是为什么始终将新存储库初始化为README.md文件是个好主意,即使它是一个空文件。 - Ahmed Hussein
当我使用Azure DevOps并从客户端使用“在VS Code中克隆”开始开发时,我看到的就是这个。服务器上还没有任何内容,因此git push origin main允许将初始提交推送到服务器。 - Shiraz
关于git init --bare的信息不多,作为一个Git新手,我发现git push --tags并没有创建主分支,而git push origin master --tags却可以。谢谢。 - lexc

16

这可能解决你的问题。

修改后,您可以提交并保存更改。

git remote add origin https://(address of your repo) it can be https or ssh
then
git push -u origin master

希望这对你有用。

谢谢。


1
这确实会有所帮助 - 原因在我的答案中有详细说明(简而言之 - 这将在远程仓库中创建缺失的主分支)。 - ElazarR

13

对我来说,.git/refs/origin/master已经损坏了。

我按照以下步骤操作,解决了我的问题。

rm .git/refs/remotes/origin/master
git fetch
git branch --set-upstream-to=origin/master

0

实际上,torek已经告诉你如何更好地使用工具了,比我能做的好。然而,在这种情况下,如果你遵循http://octopress.org/docs/deploying/github/的指南,我认为有一些特别的事情需要指出。也就是说,你的设置中将会有多个github存储库。首先是包含你网站所有源代码的目录$WEBSITE,然后是只包含静态生成文件的目录$WEBSITE/_deploy。这个设置的有趣之处在于,在$WEBSITE目录中有一个.gitignore文件,所以这个设置实际上是有效的。

介绍到此结束。在这种情况下,错误也可能来自于_deploy存储库。

cd _deploy

git branch -a
* master
remotes/origin/master
remotes/origin/source

.git/config 文件中,通常需要找到类似以下的内容:
[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = git@github.com:yourname/yourname.github.io.git
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
    remote = origin
    merge = refs/heads/master

但在你的情况下,主分支没有远程。

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
[remote "origin"]
    url = git@github.com:yourname/yourname.github.io.git
    fetch = +refs/heads/*:refs/remotes/origin/*

你可以通过以下方式解决:

cd _deploy
git branch --set-upstream-to=origin/master

所以,一切都像torek告诉你的那样,但是指出这可能涉及_deploy目录而不是您网站的根目录可能很重要。

PS:值得使用像zsh这样的shell和git插件,以免将来受到此问题的影响。它将立即显示_deploy涉及不同的存储库。


0

我曾经遇到过这个问题两次,而且都是由于本地分支的 git 缓存文件损坏所致。我通过将缺失的提交哈希写入该文件来解决了这个问题。我从服务器获取了正确的提交哈希,并在本地运行了以下命令:

cat .git/refs/remotes/origin/feature/mybranch \
echo 1edf9668426de67ab764af138a98342787dc87fe \
>> .git/refs/remotes/origin/feature/mybranch

-6
问题:你的分支基于“origin/master”,但上游已经消失。
解决方案:git branch --unset-upstream

12
欢迎来到stackoverflow。这个问题不是关于如何修复错误,而是想知道为什么出现了这个错误以及是否需要采取措施。请在回答中解决这个问题。 - cronoik

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