您的分支创建正在按照您希望的方式工作。您所看到的原因与 Git 标记的内部结构有关,这有点奇怪。
Git 在其小小的 gitty 心中,都是关于 commits 的,它们由散列 ID 编号,通常以十六进制表示:例如,bfd271932228f8ce33b68b82ffee5ee3b2386a17。
为了使提交工作,Git 需要另外两个内部支持对象,Git 称之为 trees 和 blob。这些也有哈希 ID。您通常不会看到这些哈希 ID:它们不会“泄露”太多。 (不过,Blob 哈希 ID 确实出现在 git diff 输出中的 index: 行中,如果您查找它们,可以找到树哈希:这些都不是隐藏的。他们只是不get all up in your face 提交哈希 ID 的方式。)
标签,在 Git 中,标记一个提交——但你可以在这里选择:轻量级标签直接保存一个提交哈希 ID,所以如果你有提交 bfd27...,你可以创建一个轻量级存储该哈希 ID 的标签。但是,如果您想存储更多信息,Git 有一个支持对象,称为标记对象 或带注释的标记对象。我们让 Git 创建其中一个对象,存储额外的数据(例如 PGP 签名或其他数据),并且该对象获得自己的唯一哈希 ID,例如 ff8db8992102ca7ce76f55169d06173c888c9447。
标签对象本身与注释数据一起存储 commit 哈希 ID bfd271932228f8ce33b68b82ffee5ee3b2386a17。由于这些哈希 ID 各自唯一标识对应的对象,Git 可以使用 tag ID ff8db... 找到 commit 对象,通过读取标签对象并找到存储的提交哈希 ID。 (不可能走另一条路:commit bfd27... 在我们创建任何指向它的标签之前就已经确定了,因此我们无法添加那些标签 ID 到稍后提交。因此,像往常一样使用 Git,我们必须向后工作,从较新的对象到较旧的对象。)
使用git rev-parse v2.6.0-rc3,您可以获得带注释的标签对象的哈希ID。从这里 Git 可以找到提交。标签名称可以直接指向一个提交——这再次使它成为一个轻量级标签——或者指向一个标签对象,使标签名称成为一个带注释的标签。 Git 可以通过任何一种方式找到提交。
Branch 名称与标签名称不同,受到限制:它们可能只包含一些(现有)commit 的哈希 ID。所以当创建一个新的分支名称时,如果你给 Git 一个带注释的标签对象的哈希 ID,或者一个解析 is 的带注释的标签对象的名称,Git 会继续跟随带注释的标签对象到它的目标,需要是一个提交。1
这正是您在这里看到的。创建分支名称遵循标记提交的标记。其他分支名称也指向同一个提交——这很好也很正常。当您使用git checkout 或git switch 获得这些分支名称之一并进行new 提交时,Git 将像往常一样进行新提交,并且作为@ 的最后一步987654334@,会将新提交的hash ID写入当前分支名,导致分支前进。
使用git checkout v2.6.0-rc3 或git switch --detach v2.6.0-rc3 检出标签将使Git 进入分离的HEAD 模式,其中HEAD 包含提交的原始哈希ID。在这种情况下,进行新提交将新提交的哈希 ID 直接存储在特殊名称 HEAD 中,而不是任何分支名称中。这意味着重新附加HEAD——它用分支名称而不是提交哈希ID覆盖HEAD存储槽——“丢失”新的提交,这就是你通常不做新工作的原因在 detached-HEAD 模式下。2
这里要提到最后一件事,那就是git rev-parse 有很多句法技巧来处理这个问题。它们都包含在 the gitrevisions documentation 中,但在这里对相关的快速概述很有用:
-
git rev-parse v2.6.0-rc3 只是为您获取 v2.6.0-rc3 解析为的任何 ID:在这种情况下,refs/tags/v2.6.0-rc3 解析为带注释的标签。
-
git rev-parse v2.6.0-rc3^{commit} 找到与v2.6.0-rc3 关联的commit:也就是说,如果这是一个标签,它会剥离标签并要求结果是一个提交。
-
git rev-parse v2.6.0-rc3^{tree} 找到与v2.6.0-rc3 关联的树:也就是说,如果这是一个标签,它会剥离标签;如果这是一个提交,它会找到存储在该提交中的顶级树in;它要求最终结果是树的哈希 ID。
-
git rev-parse v2.6.0-rc3^{} 查找与v2.6.0-rc3 关联的哈希 ID,如果是标签,则剥离标签(然后成功停止并生成哈希 ID,无论找到的对象类型如何)。
在这种情况下,git branch test001 v2.6.0-rc3 或 git checkout -b test001 v2.6.0-rc3 在内部具有与使用 v2.6.0-rc3^{commit} 和 git rev-parse 相同的效果。
这些语法技巧适用于大多数 Git 命令:在任何可能需要哈希 ID 的地方,您都可以使用名称,并且您提供的任何名称都会经过 git rev-parse 用来将其转换为哈希 ID 的相同过程。
1带注释的标签可以直接指向树或blob对象。如果这样做,则不能使用它们来创建新的分支名称。带注释的标签对象也可以包含另一个带注释的标签对象的哈希ID作为它们的目标哈希ID;在这种情况下,Git 会继续间接寻址,直到找到最终对象。这种重复的间接方式称为 peeling 标签,其概念来自于一层一层剥洋葱,直到你找出里面是什么。当然,就洋葱而言,当你剥去所有层时,除了气味之外什么都没有了。 ?
2这里的例外情况包括:
-
git rebase 故意使用这种模式来构建一个新的提交链。完成后,git rebase 会强制要重新设置的分支名称指向新提交的最后一个。
-
如果你喜欢,你可以在这种模式下工作一段时间,然后自己创建一个新的分支名,或者强制一些现有的分支名指向新的提交。
-
如果您确实在 detached-HEAD 模式下工作不小心,您可以使用 git reflog 找到您想要的提交并创建一个分支名称(或标签名称!)来找到它。
Git 主要只是在这里提供机制,您可以在此基础上构建任何您喜欢的东西。