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/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 上的分支,我们在本地记得是 origin/br1
)。
分支创建和上游设置
当我们选择使用 git branch
、git checkout -b
或 git switch -c
创建一个新分支时,我们必须给出 Git 两件事:
- Git 需要它应该创建的新分支的 name。
- 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 将:
- 创建新名称,指向选定的提交——在本例中为提交
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 显然只是 运行s 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 中,这个选择——创建一个新的分支 和 一个上游集——实际上可以通过多个选项来控制:
-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
这两种方法都将避免为新分支设置任何上游。
有人可以告诉我我的行为有什么问题吗?我正在尝试从远程分支创建一个新分支,并将新分支推送到我的存储库。
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/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 上的分支,我们在本地记得是 origin/br1
)。
分支创建和上游设置
当我们选择使用 git branch
、git checkout -b
或 git switch -c
创建一个新分支时,我们必须给出 Git 两件事:
- Git 需要它应该创建的新分支的 name。
- 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 将:
- 创建新名称,指向选定的提交——在本例中为提交
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 显然只是 运行s 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 中,这个选择——创建一个新的分支 和 一个上游集——实际上可以通过多个选项来控制:
-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
这两种方法都将避免为新分支设置任何上游。