分支或标签名称在某种程度上只是一个短的、易于理解的名称,用于表示你已经看到的那些 Git 巨大且丑陋的哈希 ID 之一。
实际上,像分支名 X 和标签名 X 这样的名称之间几乎没有什么区别。事实上,主要的区别在于:
- 分支 X 的完整名称为 refs/heads/X;
- 标签 X 的完整名称为 refs/tags/X;
- 通常情况下,标签名不应该更改其所指向的哈希 ID,但是分支名通常会随着时间的推移以明确定义的方式发生更改:它总是指向添加到分支中的最新提交。
实际上,这正是 Git 知道哪些提交属于该分支的方法:分支名 X(实际上是 refs/heads/X)标识了作为该分支末端的特定提交。然后,该提交再引用其父提交,该父提交可能曾经是该分支的末端。父提交引用其自己的父提交,以此类推;这就是该分支的历史记录。
当 Git 向您报告分支名称时,它会去掉前缀 refs/heads/。当它向您报告标签名称时,它会去掉前缀 refs/tags/。(有一些例外情况,但这是通常规则。)
通常情况下,具有简短形式 X 的两个事物是不明智的...并不是因为 Git 会感到困惑,而是因为您会感到困惑。Git 有非常精确(尽管相当复杂)的规则,总是选择一个......但不一定是您所想要的那个。这就是为什么您会收到警告消息的原因。
您可以选择忽略该警告,或更努力地避免它。要更加努力地避免它,请使用 Git 的所谓 “plumbing” 命令。这些命令旨在从脚本中使用,并具有非常可预测和可重复的行为,但它们往往需要大量的输入(这没关系,这是计算机在输入)。创建、删除或更新引用(引用是“分支、标签或其他名称”的花哨长单词)的 plumbing 命令是 git update-ref,并且它要求您每次都拼写出 refs/heads/X 或 refs/tags/X。因此,您可以编写一个脚本,检查 refs/heads/whatever 和/或 refs/tags/whatever 是否存在,并创建或删除 “另一个”。
要查找某个引用是否存在,并获取其值,请使用plumbing命令
git rev-parse
并拼写出引用的完整名称。如果该名称不存在,则命令将失败(并返回非零状态,通常会打印一条消息,但您可以抑制此消息),如果该名称存在,则会打印出哈希ID。
注意:当您删除一个分支名称时,也会删除其reflog。分支(或任何引用)的reflog包含该引用在过去30到90天左右的所有先前值(数字有点复杂,如果需要可以进行调整)。由于标签通常不移动,它们的reflog(如果有)往往相当无聊:“这个标签始终具有值
0f39ca43...
”然而,正如我们上面所提到的那样,分支确实会移动,它们的reflog充满了“昨天这个分支指向哪里”等信息。
git update-ref refs/heads/v3.2 v3.2; git tag -d v3.2
- jthillgit rev-parse /refs/tags/feature-123_tag
这样的命令来检查标签是否已存在。这正确吗? - smartgit-convert-branch-to-tag
的脚本可能会检查其参数是否命名了一个现有的分支和零个现有的标签,然后创建一个标签并删除该分支;一个名为git-convert-tag-to-branch
的脚本将检查对应项并执行相应操作。或者,一个名为git-switcheroo
的脚本将接受一个名称,并确定它当前是(分支,非标签)还是(非分支,标签),无论现在是哪种情况,都会切换它。将这些命名的脚本放在您的$PATH
中,可以让您运行git <whatever>
来运行它们。最后, - torekrefs/tags/feature-123_tag
,而不是/refs/tags/feature-123_tag
。如果您使用未经限定的名称(例如git rev-parse feature
),Git将使用链接文档中的六步过程来确定要使用哪个名称... 除了git checkout
首先尝试将其用作分支名称(请注意,在六个步骤的过程中,Git首先尝试使用标签名称,因此git checkout
与其他Git命令不同意哪个优先级)。 - torek