Git - 新的 repo 允许我 "pull origin main" 但不能提交 "push origin main" 并给出错误

Git - New repo allows me to "pull origin main" but cannot commit with "push origin main" and gives an error

一分钟没用git/github,好像有点变化

我在 GitHub 上设置了一个新的存储库(使用 readme),然后在我的计算机上创建了一个匹配的目录,我将在其中工作。

在该匹配目录中打开了终端...

git init
git remote add origin https://github.com/(username)/(reponame).git
git pull origin main

我收到关于...的警告

"Pulling without specifying how to reconcile divergent branches is discouraged."

但除此之外,拉动工作正常,自述文件 现在在我的本地目录中。

显然我在命令中有正确的“用户名”和“reponame”。

但是为了确保一切顺利,我修改了 readme 并尝试将其推送到 repo...

git add -A
git commit -m "First commit, updating readme"
git push origin main

并收到错误:

error: src refspec main does not match any
error: failed to push some refs to 'https://github.com/(username)/(reponame).git'

看网上的repo,只有一个分支:main

当我检查时在终端中...

git show-ref

我有两个输出...

5...(redacted)...e refs/heads/master
c...(redacted)...0 refs/remotes/origin/main

如果我尝试命令

push origin master

没有错误,但它在 repo 上创建了一个单独的分支。

所以,不用说,我完全迷路了,不知道发生了什么。

感谢任何建议。

我不确定,但在这种情况下,您的本地 master(在我看来您的本地分支机构称为 master,而不是 main)可能无法跟踪您的远程main。尝试这样做:结帐到您当地的 master 然后 git branch -u origin/main

TL;DR: git branch -m main

对于您的具体情况,解决方法很简单:只需将本地 b运行ch master 重命名为 main 即可很高兴从这里开始使用新奇的名称。阅读其余部分是可选的,但应该可以帮助您避免未来的障碍。

发生了什么(很长,可选)

这个序列:

mkdir (new-directory); cd (new-directory)

git init
git remote add origin https://github.com/(username)/(reponame).git
git pull origin main

在功能上并没有错,但这不是您通常应该做的;通常你只想:

git clone https://github.com/(username)/(reponame).git (new-directory)
cd (new directory)

如果要创建的目录名与reponame[=相同,则可以省略new-directory名361=]:Git 将剥离 .git 并使用它。 git clone 命令执行 mkdir 和 运行 新目录中的其余命令:

  1. [为 mkdir 步骤保留,如果您提供空目录供 git clone 使用,则跳过该步骤]
  2. git init [运行 在由步骤 1 创建或提供的新目录中]
  3. git远程添加来源<em>url</em>[使用你提供的URL]
  4. [保留额外 git config 步骤,如果你使用 -c 选项]
  5. git fetch origin(不是git pull!)
  6. git 切换 <em>b运行ch</em>,其中 branch 来自您的 -b 选项。

这个six-step过程就是git clone真正的意义所在。 git init 步骤使新的空存储库数据库 那个空目录中,创建一个隐藏的 .git 目录来保存它们。 git remote add 步骤保存您的 URL 以备将来使用,第 5 步 git fetch 让您的 Git 软件调用 Git 软件在保存 URL 并从该存储库复制所有 提交

在这个过程中的这一点——在第 5 步结束时——你有他们所有的 commitsno b运行ches 在全部git fetch 步骤确实看到了他们所有的 b运行ch 名称,但是在你的数据库中创建了 remote-tracking names,而不是 b运行ch 名称。这对 Git 很好,因为 Git 可以在没有任何 b运行ch 名称的情况下工作,但对 [=253= 不太好]humans,他们喜欢 b运行ch 名字。所以我们继续第 6 步,一个 git checkoutgit switch 创建一个 b运行ch name.

第 6 步创建的 b运行ch 名称将与克隆存储库的某些 b运行ch 名称拼写相同。如果它们(origin 存储库)有多个 b运行ch 名称,您可以使用 -b 选项选择哪个。不过,大多数人不会在 git clone 时使用 -b 选项。在这种情况下,您的 Git 软件询问他们的 Git 其他存储库推荐的 name 软件。

GitHub 为您提供了根据 GitHub 存储库进行设置的方法。 Git集线器,与 Git 一样,将其称为 默认 b运行ch。在过去,newly-created GitHub 存储库的名称默认为 master,但现在默认为名称 main。因为人们不向他们的 git clone 命令提供 -b 选项,所以有时了解某些 GitHub 存储库将使用哪个名称作为其“默认 b运行 有时很重要ch”设置。这是他们要推荐的名字,由于 Git 中的一个小缺陷,他们会推荐 b运行ch name even if that b运行ch名称不存在.

空 Git 存储库困境

A Git repository—与 checked-out 工作树不同,后者保存您可以处理/使用的普通文件—位于它的核心,只有两个数据库。

  • 一个,通常更大,包含 Git 的 提交对象 和其他支持对象。这些存储提交,存储库中的历史。每个提交,由 object ID (OID) 或更通俗地说,一个 hash ID 找到,存储每个文件的完整快照,及时冻结与您或任何人做出承诺时的形式一样;每个提交还存储一些 元数据 或有关该特定提交的信息。这些信息组合成 link 彼此的提交。

  • 第二个独立的数据库包含 names:b运行ch 名称、标签名称和所有其他形式的名称。每个名称只存储一个 OID(哈希 ID),特别是 b运行ch 名称总是存储 commit 哈希 ID。这些存储的哈希 ID 随着时间的推移而演变。

存储在 任何给定 b运行ch 名称中的哈希 ID,根据定义,是 last 提交被认为是“在 b运行ch 上”。然后,进行 new 提交包括进行提交,其父提交是 currently 最后一次提交ne,然后更改存储的哈希 ID in b运行ch 名称以保存新提交的新哈希 ID。

这些定义意味着如果我们根本没有提交,就没有哈希 ID,因此也不会有任何 b运行ch 名称。 b运行ch 名称必须 保存一些现有提交的哈希 ID。没有提交,就没有这样的哈希 ID。

尽管如此,要 使用 Git— 能够创建文件到 git add 然后 运行 git commit —我们 必须“在”一些 b运行ch 上。所以当我们 运行 git init 并且它创建了一对新的、完全空的数据库时,我们进退两难: 不可能有任何 b运行ches,但是我们必须“在”一些 b运行ch.

Git 解决了这个 Gordian knot 亚历山大应该做的方式,只是把它砍成碎片。 Git 规定 当前 b运行ch 名称—当我们在 Git repository—是存储在特殊名称HEAD中的名称,而HEAD可以包含一个b[=530=的名称]ch 不存在.

所以在 git init 之后,我们“开启”了 a non-existent b运行ch!当我们第一次提交时,b运行ch 就突然变成了。我们所做的提交是一个 root commit——一个没有父提交的提交,这在几个方面是特殊的——而新的提交,就像任何新的提交一样,获得一个新的、唯一的哈希 ID .现在 objects 数据库中有一个提交,有一个有效的哈希 ID 可以存储在 b运行ch 名称中,所以 Git 现在创建 b运行ch names 数据库中的名称,将哈希 ID 存储到该数据库中的名称中。

GitHub 在这一切中的作用

当你在 GitHub 使用他们的网络界面创建一个新的存储库时,GitHub 给你机会创建一个存储库,其中包含初始,somewhat-special root 提交。这消除了困境:现在有一个提交,所以可以有一个 b运行ch 名称。这也意味着你可以有一些初始文件,比如 README and/or a license and/or copyright and/or initial .gitignore: 任何你喜欢的文件,你可以放入 (提供 GitHub 给你一个模板)。

可以,如果您愿意,请创建一个totally-empty GitHub 存储库。当您这样做时,他们现在将推荐的(“默认”)b运行ch 名称设置为 main。在过去,他们曾经将其设置为 master由您来确保创建此 b运行ch!

(我怀疑大多数人都接受了“为我进行初始提交”的提议,但我没有这方面的实际统计数据。他们——GitHub——将不得不进行统计分析。)

你做了什么

你:

  • had GitHub 创建了一个带有初始提交的存储库,其中有一些哈希 ID,它们存储在名称 refs/heads/main 下:b运行ch main
  • 创建了你自己的完全空的存储库,没有提交,这是在 Git 自己的默认初始 b运行ch master(全名 refs/heads/master) ;然后
  • 运行 git pull origin main.

git pull命令主要是shorthand方便运行宁两个其他Git命令:

  1. git fetch,它获取您传递给 git pull 的大部分选项和参数;然后
  2. 第二个 Git 命令:git mergegit rebase 之一,带有一些毛茸茸的特殊外壳,用于“拉入空存储库”。1

git fetch,在你的例子中,获取了你在 GitHub 上所做的初始提交,在他们的 b运行ch 名称 main 下,创建 origin/main 在您的存储库中作为 remote-tracking 名称。请注意,此时您 仍然没有 b运行ch 名称!

现在你的 Git 运行 第二个命令。在您的情况下,这是 git merge。这是 git pull 产生警告的地方:

"Pulling without specifying how to reconcile divergent branches is discouraged."

这个警告有点奇怪,因为此时你没有“divergent b运行ches”。事实上,你根本没有任何 b运行ches!但这就是 special-case 处理的开始。你所在的 non-existent b运行ch,master,没有提交,与现有的“不同”,single-commit "b运行ch"2 其第一次、最后一次和唯一一次提交现在列在您的存储库中的名称 origin/main 下。

尽管如此,git merge 能够“合并”他们的 main——你的 origin/main——和你的 master。这 创建了你的 b运行ch 名称 master,识别与他们的 b运行ch 相同的 commit名字 main—你的 origin/main.

如果我们在您的存储库中绘制提交,我们只有一个提交,您引用为 c...(redacted)...0 的那个。您的 Git 发现这个提交使用名称 origin/main—realy refs/remotes/origin/main,这是它的全名,但人类通常会缩短这些名称,Git 有时会这样做,具体取决于您使用的 Git 命令。

因为 special-case 代码现在有效(至少从 Git 1.7 开始),这并没有破坏任何工作,而是 签出 您现在拥有的一次提交中的文件。现在您有了名为 master 的 b运行ch,将相同的提交标识为“他们的”(您的 GitHub 存储库的)b运行ch,名为 [=21] =] 在 GitHub 存储库中。

从这里开始,事情变得非常简单:您修改了 README 文件,添加并提交了。这产生了一个新提交,5...(redacted)...e,其父项是初始提交,并将新提交的名称存储在您的名称数据库中的名称 masterrefs/heads/master)中。


1这个特殊的外壳在早期的Git版本(1.5.something,甚至可能是1.6.something)和我至少一次被严重烧伤。这就是我开始避免使用 git pull 的原因之一——结果证明这是一件好事,因为通过执行单独的获取和 second-command 序列,人们可以了解 Git 是如何工作的!

2我在这里把单词“b运行ch”用引号括起来,因为 origin/main 不是 b运行ch name,这是一个 remote-tracking name。它是“b运行ch”吗?这取决于我们说“b运行ch”时的意思:What exactly do we mean by "branch"?


修复

如果您现在 重命名 您的 master 改为 main

git branch -m main

你最终得到:

A    <-- origin/main
 \
  B   <-- main (HEAD)

其中字母 A 代表初始提交的哈希 ID,B 代表您的新提交 5...(redacted)...e。提交 B 的父级是 A,因此您可以 git push origin main 将提交 B 发送到 您的 Git Hub 存储库,他们会将其添加到 他们的 main,以便您的 Git 在 您的[=] 中更新您的 origin/main 361=] 名称数据库:

A--B   <-- main (HEAD), origin/main

一切顺利。

您现在也可以随时(在推送之前或之后)设置您的 b运行ch 名称的 upstream 设置-现在 main因为你的 git branch -m 命令——到字符串 origin/main.3 你可以用一个单独的 git branch --set-upstream-to 命令来做到这一点,或者你甚至可以这样做你的git push一起git push -u origin main


3git branch --set-upstream-to 命令和类似的命令可以处理这样的字符串。但是,由于历史原因,in .git/config 文件中的配置分为两部分:remoteb运行ch。这里的映射很复杂,但如果你使用所有默认值,4 似乎 简单:我们只是将远程名称串在一起,origin,加上branch-name-as-seen-on-the-remote,main,得到origin/main.

4没有理由在这里使用默认值。然而,如果你奇怪地设置了你的获取映射,你可以将 origin/funky 作为你的 remote-tracking 名称,即使他们称它为 main。例如,您然后可以调用您的(本地)b运行ch winkerbean。这将作为 branch.winkerbean.remote = originbranch.winkerbean.merge = main 存储在您的 .git/config 中,即使 b运行ch winkerbean 的“上游”现在是 origin/funky git branch -r 向您显示 origin/funky 来源的 main不要这样做,你只会把自己逼疯。但它确实有效。


正在设置您的 Git 以使 git init 使用 main

如果你喜欢 main 而不是 master 的新世界,或者只是不够固执(或其他)不足以坚持活在过去, 5 你可以设置你的 Git 这样当你 运行 git init 时,它会让你进入不存在的 b运行ch 名称 main而不是不存在的 b运行ch 名称 master。不过,这需要 一个足够新的 Git,或者一个小技巧。

在 Git 版本 2.28 及更高版本中,您可以 运行:

git config --global init.defaultBranch main

一切就绪。但是,如果您的 Git 早于 版本 2.28,则您没有 init.defaultBranch(而且我认为您也没有 --initial-branch=)。在这里,您可以使用一个简单的技巧。当 git init 创建一个新的空存储库时,您将在 master,但您可以使用 git checkout --orphan 更改此 non-existent b运行ch 的名称:6

git checkout --orphan main

或者,进行第一次提交——这至少在一些旧版本的 Git 中是必需的——然后 运行:

git branch -m main

重命名 git commit 创建的 master


5但往事如此惬意!朦朦胧胧留恋... ♪可否难道那时一切都那么简单吗?还是时间重写了每一行? ♪不,等等,过去,现在,未来,都是可怕的。 It is best never to have been born. But who among us has such luck? One in a million, perhaps. —Alfred Polgar

6您也可以使用 git switch --orphan 来执行此操作,但是如果您的 Git 早于 2.28,则可能早于 2.23 而您不这样做也没有新奇的 git switch.请注意 git switch --orphan 清空 Git 的索引,而 git checkout --orphan 不会,但对于这种情况通常无关紧要。