Create/Push 基于远程的存储库的新本地分支

Create/Push new local branch to repository based on remote

有人可以告诉我我的行为有什么问题吗?我正在尝试从远程分支创建一个新分支,并将新分支推送到我的存储库。

Description:
-Currently on branch "test"
-git pull
-git checkout -b <new_branch_name> origin/test

我确认我的分支已经切换到新的分支。但是,当我 commit/pushed 通过 visual studio 进行更改时,未创建新分支并将更改推送到远程“origin/test”。

相反,如果我 运行 不带第二个参数“origin/test”的结帐命令(当前仍在 origin/test 分支上)然后我执行 git push,我的新分支创建了

为什么会这样?

TL;DR

因为你的新分支的上游设置为origin/test。 Visual Studio 显然(误)使用它来决定要求遥控器更新其 test 分支。

重要背景

分支不是从分支创建的。分支是从 提交 创建的。了解和理解这一点很重要;如果你认为分支在 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/br1origin/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 上的分支,我们在本地记得是 origin/br1)。

分支创建和上游设置

当我们选择使用 git branchgit checkout -bgit switch -c 创建一个新分支时,我们必须给出 Git 两件事:

  1. Git 需要它应该创建的新分支的 name
  2. 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)

也就是说,name test 指向 some commit with some hash ID,这里绘制为H,代表“哈希”。 Git 中的每个提交都有快照和元数据,并且提交中的元数据包含以前提交哈希 ID 的列表,通常只有一个条目长。在这种情况下,提交 H 包含一些较早提交的哈希 ID(即“指向”),我们只是调用其哈希 ID G。 (在每种情况下,真正的哈希 ID 都是一些看起来很丑的随机字母和数字串。)提交 G 有一个快照和元数据,因此指向更早的提交 F,其中有快照和元数据,等等。

当我们要求 Git 创建一个 new 分支名称时,git checkout -b newbr 在这种情况下,Git 将:

  1. 创建新名称,指向选定的提交——在本例中为提交 H——然后
  2. HEAD 附加到新名称。

我们可以这样得出结果:

... <-F <-G <-H   <-- newbr (HEAD), test

也就是说,两个分支名称都指向同一个提交(其哈希ID为H)。

当我们使用这种形式创建分支时,新分支没有上游集。所以newbr没有上游。命令行 git push 将在现代 Git 中使用默认值,给我们一个错误;我们将不得不 运行 git push -u origin newbrgit push -u origin HEADorigin 上创建一个名称 newbr 以便我们的 Git 将创建 origin/newbr 在本地,这样我们就有一个 origin/newbr 作为上游。 git push-u 选项将立即 设置 作为上游。

Visual Studio 显然表现不同。 如果没有设置上游,VS 显然只是 运行s git push origin newbr:newbr,而不是给我们一个错误。

但是,您可以告诉 Git,在您创建一个分支时,设置它的上游那时候。为此,您 必须 使用按名称而不是原始哈希 ID 给 Git 特定提交的形式:

git checkout -b newbr origin/test

例如。 这就是你正在做的。 当你使用这种形式的 git checkout -bgit switch -cgit branch 时,默认情况下 Git 会:

  1. 使用名称 origin/test 查找提交哈希 ID;
  2. 创建指向此提交的新分支名称newbr;和
  3. newbr的上游设为origin/test

注意这个额外的步骤 3,当使用 git checkout -b newbr 时没有那个附加参数时 不会 发生。

在命令行 Git 中,这个选择——创建一个新的分支 一个上游集——实际上可以通过多个选项来控制:

  • -t--track 选项说 do do this.
  • --no-track选项说不要这样做
  • 配置设置 branch.autoSetupMerge 说明是否执行此操作,在这种情况下,当 -t--no-track 未指定时。

我上面给出的描述涵盖了 branch.autoSetupMerge 未设置 或设置为 true 时的正常行为。您可以将它设置为 false,在这种情况下,分支创建选项总是像指定了 --no-track 一样工作,并且您可以将它设置为 always,这使得 Git 设置了一个local 当您提供本地分支名称作为起点时,分支作为新分支的上游。

你可能不应该设置这个选项,并且简单地避免给 git branch 一个起始名称,或者使用 --no-track 选项:

git checkout -b newbr $(git rev-parse origin/test)

或:

git checkout -b newbr --no-track origin/test

这两种方法都将避免为新分支设置任何上游。