这是一个多部分的答案,因为这里有两个单独的问题现在纠缠在一起。以下是我们将要介绍的内容的摘要:
-
main 与 master
error: src refspec main does not match any
- 协调单独的
main 和 master 分支
每一个都在自己的部分中。
main 与 master
Git 本身没有特殊的分支名称。1 您可以使用main、master、trunk 或任何其他名称作为您的第一个分支的名称。 Git 传统上在这里使用名称 master,但有一个项目可以使此名称可配置,因此如果您是法语或西班牙语,则可以使用名称 principal 或 première 或 primero,或者如果您愿意毛利人,你可以使用matua 或tuatahi。目前,您可以在 git init,2 期间或之后手动执行此操作,但该项目使 Git 自动执行此操作,无需第二步:如果出于 任何 原因你想要任何默认的名字,你可以配置它。
与此同时,GitHub 已经选择了跨越式发展,将其默认的初始分支名称设为 main 而不是 master。但这会使 你的 Git 和 GitHub 的 Git 不同步,就像原来一样。有关 GitHub 转换的更多信息,请参阅
Difference Between Main Branch and Master Branch in Github?
1这种说法存在一些技术缺陷。众所周知,technically correct is the best kind of correct,所以让我在这个脚注中添加一些注意事项:
-
当您在分支 Y 上并运行 git merge <em>X</em> 时,合并会自动生成格式为 merge branch X into Y 的消息。但是,当您使用 master 时,Git 传统上只会生成 merge branch X 形式的消息。
-
git init 创建的新的空存储库没有提交,因此也没有分支(因为分支只有通过提交才能存在)。但是,您必须在这个新的空存储库中的某个分支上。因此 Git 将一些名称存储在名为 HEAD 的符号引用中。这是您所在的分支名称,即使该分支名称不存在(尚不存在)。很长一段时间以来,Git 已经硬编码了一些代码来将分支名称 master 粘贴在其中。 (实际上,这就是 GitHub 改变的地方。)
-
在源代码和文档中还有很多其他的字符串字面量为master;它们正在转换为使用配置设置,但这都需要时间。
2如果您有 Git 2.28 或更高版本,请运行 git init --initial-branch=<em>name</em>,和/或在您的系统或全局配置中使用 git config 设置 init.defaultBranch。如果您安装了早期版本的 Git,或者已经运行 git init,只需使用 git branch -m 将 master 重命名为您喜欢的任何名称。
error: src refspec main does not match any
这个来自 Git 的错误消息对于新手来说非常神秘,但实际上非常简单。问题在于它使用了行话(webster;wikipedia),并将“source”缩写为“src”。
Git 是关于提交的。当我们克隆一个存储库时,我们的 Git 可以访问其他 Git。另一个 Git 查找一个存储库,而另一个存储库充满了提交。然后我们让我们的 Git 在本地创建一个新的存储库,将他们所有的提交转移到其中所有,并将他们所有的分支名称变成远程跟踪名称时间>。然后,我们的 Git 在这个新的存储库中创建 一个 分支名称,基于它们的一个分支名称。至少,这是正常的过程。 (而且,如果您知道所有这些术语的含义,那就太好了!如果不知道,现在不要太担心它们。这里要记住的一点是,我们得到了 他们所有的提交 和 没有他们的分支,然后我们通常让我们的 Git 创建一个分支来匹配他们的一个。)
由于 Git 是关于提交的,因此我们只需要这个过程——复制所有提交,但只将其中一个分支名称复制到我们自己的存储库中拼写相同的名称中。我们的 Git 重命名它们所有的分支名称这一事实——所以除了一个例外,我们根本没有任何分支——通常并不是很重要。我们自己的 Git 会在必要时自动处理这个问题。
当我们使用git push 时,我们要求正在读取我们自己的 Git 存储库的 Git 程序连接到其他 Git 程序(通常在服务器机器上运行),然后该程序可以写入其他 Git 存储库。我们希望我们的 Git 向他们的 Git 发送我们的一些提交。特别是,我们希望向他们发送我们的新 提交: 我们刚刚提交的那些。毕竟,这些是我们放置所有好新东西的地方。 (Git 是关于提交的,所以这是我们唯一可以放任何东西的地方。)
不过,一旦我们发送了这些提交,我们需要在他们的 Git 中设置一个他们的分支名称以记住我们的新提交。那是因为 Git finds 提交的方式是使用分支名称。3 每个提交的真实名称都是大而难看的哈希 ID 号,没有人想记住或查看;所以我们让 Git 使用分支名称记住这些数字。这样,我们只需要查看分支名称,这些名称对我们来说可能是有意义的:trunk,或feature/tall,或tuatahi,等等。
默认情况下,我们使用git push 执行此操作的方式非常简单:
git push origin main
例如。 git push 部分是表示发送提交并要求他们设置名称的命令。 origin 部分是 Git 所说的 remote: 一个短名称,主要包含一个 URL。末尾的main 部分在这里是我们的 分支名称。这就是 我们的Git 用来查找我们的提交的那个。我们会让我们的 Git 发送我们的提交,然后让他们的 Git 也设置 他们的 main。
最后一部分——我们在这里输入main——就是Git 所说的refspec。 Refspecs 实际上让我们输入 两个 名称,用冒号分隔,或其他几种形式。例如,我们可以像在Arka's answer 中一样使用HEAD:main(尽管出于技术原因,我们可能希望在许多情况下使用HEAD:refs/heads/main)。但在简单的情况下,我们可以只使用一个分支名称:git push origin main。简单分支名称是 refspec 的一种简单形式。
为此,源名称必须是我们自己的 Git 存储库中现有分支的名称。这就是问题所在。
(另见Message 'src refspec master does not match any' when pushing commits in Git)
3Git 可以使用任何名称,而不仅仅是分支名称。例如,标签名称可以正常工作。但是这个答案是关于分支名称的,因为问题是关于分支名称的,而分支名称是这里最常用的。
如果 我们的 Git 只创建了 master 怎么办?
假设我们正在使用 GitHub,并且我们已经要求 GitHub 为我们创建一个新的存储库。他们运行git init 的形式,提供名称main 作为新存储库的初始分支名称。他们也可能会也可能不会创建一个提交。假设我们确实让他们创建了这个提交。根据我们使用 Web 界面选择的内容,该一次提交将保存 README 和/或 LICENSE 文件。创建初始提交实际上会创建分支名称main。
如果我们现在克隆他们的存储库,我们将获得他们的一个提交,它将在他们的分支名称main 下。我们的 Git 会将他们的 main 重命名为 origin/main,然后创建一个新的分支名称 main 以匹配他们的名称。所以一切都会好起来的。
但是,如果我们创建自己的 empty Git 存储库,使用 git init 我们自己,我们的 Git 可能会设置我们,以便我们的第一次提交将创建名称 master。我们不会有main 分支:我们将有一个master 分支。
或者,如果我们没有让 GitHub 创建初始 commit,那么 GitHub 存储库将完全是空的。因为它没有提交,所以它没有分支:分支名称只有在指定某个提交时才允许存在。因此,如果我们克隆这个空存储库,我们也将没有分支,并且我们的 Git 不会知道使用 main:我们的 Git 可能会使用 master。我们又回到了同样的情况,我们的 Git 认为要创建的第一个名字应该是 master。
因此,在这些不同的情况下,我们进行了第一次提交,它们都在一个名为 master 的分支上进行。如果我们现在运行:
git push -u origin main
(有或没有-u;我不会在这里详细介绍-u)我们的Git 在我们的Git 存储库中寻找一个名为main 的分支。一个都没有!所以我们的 Git 只是给了我们:
error: src refspec main does not match any
错误信息。
要解决这个问题,我们可以git push origin master——它发送我们的提交,然后要求 GitHub 在 GitHub 存储库中创建一个新分支,该分支名称为 master——或者将我们的 master 重命名为任何名称我们想要,然后使用该名称:
git branch -m master xyzzy
git push -u origin xyzzy
将使我们都使用的(单个)分支名称为xyzzy。如果您想在此处使用main,请将您的master 重命名为main。
如果你不小心做了两个分支怎么办?
假设我们使用 GitHub 创建了一个新的存储库,其新的默认分支名称为 main,其中包括一个带有常用 README 和 LICENSE 文件的初始提交。然后,不假思索地,我们在自己的机器上使用git init 创建了我们自己的新存储库,其默认分支名称为master,并在master 上进行了一两次提交。
如果我们现在将master 重命名为main:
git branch -m master main
然后尝试推送:
git push -u origin main
我们得到一个不同的错误:
! [rejected] main -> main (non-fast-forward)
原因很简单:他们有一个提交,他们使用他们的名main发现,而我们没有。如果他们更改名称main 以查找我们发送给他们的最后一次提交,他们将丢失他们所做的初始提交以及 README 和 LICENSE 文件.
这里有很多选择:
-
您可以忽略他们所做的初始提交。毕竟,这只是一个样板提交。你可以告诉他们把它完全扔掉。使用许多现有 StackOverflow 答案中所述的git push --force。
-
您可以获得他们的初始提交并rebase您在这些提交上的提交。这可能有点棘手,因为您的第一个提交是 root 提交。如果您的第一次提交包含 README 和/或 LICENSE 文件,您将在此处遇到添加/添加冲突。在这种情况下,强制推送可能更简单。
-
您可以获得他们的初始提交并合并您的提交。在现代 Git 中,这需要使用 --allow-unrelated-histories 选项。与 rebase 方法一样,如果您的提交包含 README 和/或 LICENSE 文件,您将遇到添加/添加冲突。生成的存储库还将有两个根提交。这些都不是严重的问题,但它们可能会有点烦人。
要获得他们的提交,只需运行git fetch origin。这将获得 GitHub 的第一次提交,并在您自己的 Git 存储库中使用名称 origin/main 来记住它。然后你可以:
git rebase origin/main
或:
git merge --allow-unrelated-histories origin/main
实现rebase或merge。您可以选择是否将您的分支重命名为 main,如果您还没有这样做,在执行所有这些操作之前或之后的任何时间。