TL;DR
这是因为您的新分支的上游设置为origin/test。 Visual Studio 显然(错误)使用它来决定要求远程更新其test 分支。
重要背景
分支不是从分支创建的。分支是从 commits 创建的。了解和理解这一点很重要;如果你认为分支在 Git 中意味着什么,你最终会伤害自己。这就像假设 1980 年代的汽车具有自动驾驶能力。
上游
也就是说,在 Git 中,每个分支可以有一个 upstream 集。一个分支要么有上游集,要么没有上游集。要设置特定的上游,请使用git branch --set-upstream-to:
git branch --set-upstream-to=origin/somebranch
例如。当前分支现在将origin/somebranch 设置为其上游(除非命令失败,在这种情况下没有任何改变)。如果之前将 origin/otherbranch 设置为上游,则该上游现在被移除,因为只能设置一个上游:origin/somebranch 现在是上游。
要取消上游,使用git branch --unset-upstream:
git branch --unset-upstream
当前分支现在没有上游。如果之前没有上游,这个操作什么也不做;否则,它会删除上游设置。
一个分支的上游是:
- 另一个(本地)分支名称,或
- 远程跟踪名称,例如
origin/somebranch。
在命令行 Git 中,git push 的行为(没有其他参数)由push.default 设置控制。如果将其设置为simple——就像今天一样——这种git push只会推送到当前分支的上游,并且只有当上游设置为远程跟踪名称时——一旦远程部分被移除——匹配当前分支。
例如,假设当前分支名为br1,并且origin/br1 和origin/br2 都存在。如果我们运行:
git branch --set-upstream-to=origin/br2
git push
我们将收到来自git push 的错误,因为origin/br2 中的名称br2 与名称br1 不匹配。但是,如果我们再运行:
git branch --set-upstream-to=origin/br1
git push
git push 这次将调用 origin 并尝试让位于 origin 的 Git 存储库更新其他 Git 存储库的分支 br1(我们在本地记得的另一个 Git 上的分支为 @ 987654348@)。
分支创建和上游设置
当我们选择创建一个新分支时——使用git branch、git checkout -b 或git switch -c——我们必须给 Git 两件事:
- Git 需要它应该创建的新分支的名称。
- Git 需要一些现有提交的哈希ID。新创建的分支将指向这个现有的提交。
默认哈希ID,如果我们不给Git一个,就是当前提交的哈希ID(由当前分支名 在大多数情况下)。也就是说,如果我们运行了git checkout test,那么当前分支名称是test,我们运行:
git rev-parse test
和
git rev-parse HEAD
我们将从两个操作中获得相同的哈希 ID。这是因为特殊名称HEAD 附加到分支名称test,而分支名称test 指向其哈希ID 都为git rev-parse 命令打印的提交。
我们可以像这样绘制这个设置:
... <-F <-G <-H <-- test (HEAD)
也就是说,nametest指向一些带有一些哈希 ID 的提交,这里绘制为 H,代表“哈希”。 Git 中的每个提交都有快照和元数据,提交中的元数据包含以前提交哈希 ID 的列表,通常只有一个条目长。在这种情况下,commit H 包含一些较早提交的哈希 ID——即“指向”——我们只是调用其哈希 ID G。 (在每种情况下,真正的哈希 ID 都是一些看起来很丑的随机字母和数字字符串。)提交 G 有一个快照和元数据,因此指向更早的提交 F,它有一个快照和元数据等等。
当我们要求 Git 创建一个 新 分支名称时,在这种情况下使用 git checkout -b newbr,Git 将:
- 创建新名称,指向选定的提交——在本例中为提交
H——然后
- 将
HEAD 附加到新名称。
我们可以画出这样的结果:
... <-F <-G <-H <-- newbr (HEAD), test
也就是说,两个分支名称都指向同一个提交(其哈希 ID 为 H)。
当我们使用这种形式的分支创建时,新分支没有上游集。所以newbr 有没有上游。命令行git push 会在现代 Git 中使用默认值,给我们一个错误;我们将不得不运行git push -u origin newbr 或git push -u origin HEAD 在origin 上创建一个名称newbr,以便我们的Git 将在本地创建origin/newbr,这样我们就有一个origin/newbr 作为上游。 git push 的 -u 选项将立即设置作为上游。
Visual Studio 显然行为不同。如果没有设置上游,VS 显然只是运行git push origin newbr:newbr,而不是给我们一个错误。
但是,您可以告诉 Git,在您创建一个分支时,然后设置它的上游。为此,您必须使用通过名称而不是原始哈希 ID 为 Git 提供特定提交的形式:
git checkout -b newbr origin/test
例如。 这就是你正在做的事情。当你使用git checkout -b 或git switch -c 或git branch 这种形式时,Git 默认会:
- 使用名称
origin/test 查找提交哈希ID;
- 创建指向此提交的新分支名称
newbr;和
- 将
newbr的上游设置为origin/test。
请注意这个额外的步骤 3,当使用 git checkout -b newbr 而不使用该额外参数时,不会发生。
在命令行 Git 中,这个选择——创建一个新的分支with一个上游集——实际上可以通过多个选项来控制:
-
-t 或 --track 选项表示做这样做。
-
--no-track 选项表示不要这样做。
- 配置设置
branch.autoSetupMerge 表示是否执行此操作,在这种情况下,当未指定-t 或--no-track 时。
我上面给出的描述涵盖了branch.autoSetupMerge 未设置 或设置为true 时的正常行为。您可以将其设置为false,在这种情况下,分支创建选项始终如同指定了--no-track,您可以将其设置为always,这使得Git 将本地 分支设置为当您提供本地分支名称作为起点时,新分支的上游。
您可能不应该设置此选项,而只是避免给 git branch 一个起始名称,或者使用 --no-track 选项:
git checkout -b newbr $(git rev-parse origin/test)
或:
git checkout -b newbr --no-track origin/test
这两种方法都将避免为新分支设置任何上游。