如果有带注释的标签,也只使用 gitlab 轻量级标签 - GitPython 考虑到这一点,请考虑 v2.30.0使用 refs/tags

问题描述

有没有可能说轻量级标签永远是首选?

我的问题:我使用 git 标签获取标签的哈希值。使用这个散列,我将向我的数据库添加一个文件。问题是,我必须始终确保保存轻量级标签的哈希值。如果有人在创建标签添加了一条消息,它将是一个带注释的标签。所以我想改变我的 git.describe 函数,让它只返回轻量级标签

我认为这是可能的,但我找不到任何例子。我唯一阅读的是文档和使用参考/标签。但我不知道如何。

我为此使用了 gitpythonhttps://gitpython.readthedocs.io/en/stable/tutorial.html
现在我是这样做的,现在自动首选带注释的标签

repo_dir = "example-git-repo-url.com"

repo = git.Repo(repo_dir)

tag_name = repo.git.describe(["--tags","--abbrev=0","--first-parent"])
tag_hash = repo.git.rev_parse(["--short=8",tag_name])
logger.info("Latest tag: %s (%s)",tag_name,tag_hash)

我不明白 documentation 的这一部分:

--标签

不要只使用带注释的标签,而是使用在 refs/tags 命名空间中找到的任何标签。此选项启用匹配轻量级(非注释)标签

使用 refs/tags 中的任何标签是什么意思?怎么样?

解决方法

您使用的 Python 包装器要么调用 Git,要么重新实现 Git(或两者兼而有之——其中一些包装器可以被告知是使用它们的内部实现,还是使用 subprocess 调用 Git)。如果您的特定包装器实现了自己的 git describe,它可能有办法做您想做的事。

如果您使用 Git 自己的内置 git describe,则没有直接的方法来获得您想要的东西。不过,--exclude 选项可能会让您足够接近。有关如何从 Python 包装器中使用它的想法,请参阅较长的部分。

基本问题是这样的:默认情况下,git describe 试图找到一个注释标签。选项 --tags 只是添加使用仅轻量级标签的能力。它永远不会取消使用带注释的标签的能力。

我没有得到这部分文档:

--tags

不要只使用带注释的标签,而是使用 refs/tags 命名空间中的任何标签。此选项启用匹配轻量级(非注释)标签。

使用在 refs/tags 中找到的任何标签是什么意思?

Ciro Santelli linkedHow can I list all lightweight tags? 这个问题有我关于如何使用 git for-each-ref 枚举所有轻量级标签的答案。这里缺少的背景(以及在上面引用的 the git tag documentation 中)是,在 Git 中,所有名称——分支名称、标签名称等等——都是 的特定形式>refsreferences。这些引用位于 namespaces

有关更完整的定义和一组示例,请参阅链接的维基百科文章,但现实世界中的一个常见名称空间示例与人类和 "given names" vs "surnames" 有关。如果您发现自己参加的聚会有太多人叫布鲁斯,您通常可以使用全名或表示字母(“布鲁斯 A”、“布鲁斯 J”)等来区分。

同样的想法在 Git 中也适用:如果您有一个名为 xyz分支一个名为 {{1} 的标签 },您可以使用全名或更全名来拼出您的意思:xyz 是分支,refs/heads/xyz 是标签。 所有标签都位于 refs/tags/xyz 下。我们通常只是省略 refs/tags/ 部分并说“标签 xyz”。

我在回答中注意到一个带注释的标签refs/tags/实际上由两部分组成:一个名为atag轻量级标签,以及带注释的标签类型的内部 Git 对象。内部 Git 对象的名称是哈希 ID。也就是说,Git 通过它的哈希 ID 找到这个带注释的标签对象:

  • 该对的轻量级标签部分是 Git refreference,并且
  • 所有 Git 引用都持有一个哈希 ID。

所以轻量级标签 atag 保存了带注释的标签对象的哈希 ID。带注释的标签对象反过来将标签目标的哈希 ID 作为其数据的一部分保存,这通常是提交。我们可以通过查看 Git 存储库中 Git 本身的标签来了解所有这些,例如 refs/tags/atag:

v2.30.0

第一个散列 ID $ git rev-parse v2.30.0 2d9685d47a7e516281aa093bf0cddc8aafa72448 $ git cat-file -p 2d9685d47a7e516281aa093bf0cddc8aafa72448 | sed 's/@/ /' object 71ca53e8125e36efbda17293c50027d31681a41f type commit tag v2.30.0 tagger Junio C Hamano <gitster pobox.com> 1609110954 -0800 Git 2.30 -----BEGIN PGP SIGNATURE----- [snipped] 带注释的标签对象2d9685d47a7e516281aa093bf0cddc8aafa72448 命令打印出带注释的标签对象的内容,它以 git cat-file -p 行开头,然后是 object 行,然后是 type 行,依此类推。这个特殊的带注释的标签的主要目的是携带 PGP 签名(这里剪掉了)。

tag 行包含此特定注释标签的目标的哈希 ID:object,它是一个提交对象。如果 71ca53e8125e36efbda17293c50027d31681a41f 是一个轻量级标签,而不是一个带注释的标签,那么名称 v2.30.0 将直接包含 refs/tags/v2.30.0。但是,71ca53e8125e36efbda17293c50027d31681a41f 指的是带注释的标签对象 refs/tags/v2.30.0,因此我们称 2d9685d47a7e516281aa093bf0cddc8aafa72448 是带注释的标签。它由构建这一对:名称指的是第一个对象,而第一个对象指的是另一个对象。

考虑到这一点,请考虑 v2.30.0

要使 git describe 执行其默认工作,它必须枚举带注释的标签。这意味着查看每个 git describe 名称。每个这样的名称要么是一个轻量级标签,直接引用某个不是带注释的标签对象的对象,要么是一个带注释的标签,因为它引用了一个带注释的标签对象,该对象引用了到某个目标对象。这有点吃不完——或满头——所以如果需要,请多看几次。

由于 refs/tags/* 只想查看带注释的标签,它所做的就是查看每个标签,然后扔掉 那些直接引用注释标签的标签。这意味着 git describe 现在只有带注释的标签的表格。然后它可以继续工作并找出这些标签名称中的哪一个(如果有的话)是合适的。

git describe 标志只是告诉 --tags不要丢弃仅轻量级的标签。这让它查看所有标签在 git tag 命名空间中,即所有标签。

使用 refs/tags

--exclude 选项旨在丢弃某种标记名称模式。例如,假设某个存储库作者将他们的标签组织成“发布”、“早期”和“实验”:标签 --exclude 是 1.0 版,而 releases/1.0early/1.0-alpha 是 alpha和 1.0 版的测试版。与此同时,early/1.0-beta 可能有一些正在试验中的功能,可能会也可能不会进入 2.0 版。

您可能只对各种实验感兴趣,不包括早期版本。在这种情况下,您可以使用以下命令排除所有 experiments/featureXreleases/*

early/*

(这里的引号是为了防止 git describe --exclude 'releases/*' --exclude 'early/*' 被你的 shell 吃掉;这在各种情况下可能是不必要的,但很少有伤害)。

虽然 exclude 选项采用“glob 模式”,但任何实际标签都是有效的 glob 模式。只是它只匹配那个标签。1 所以,在你的 Python 程序中,你可以:

  • 查找所有标签名称,使用您可以使用的任何工具来枚举 * 空间中的所有名称。请注意,使用 refs/tags/ 时不需要尾部斜杠,因为它会添加 git for-each-ref 本身,但您使用的任何 API 都可能需要。检查文档。2

  • 对于每个带注释的标记,添加 '/*' 和标记。

然后使用 --exclude 和这些 git describe --tags 来想出一个描述性标签(如果有的话)。通过排除所有带注释的标签,您将只获得一个轻量级标签。


1如果标签的名称包含特殊的全局字符,即 --exclude*?,我们可能会在这里遇到麻烦。一个可以:

  • 希望没有标签使用这些字符,或者
  • 如果需要,请检查并引用它们。

但另请参见 the git check-ref-format documentation,它明确禁止此类字符。我已经看到 StackOverflow 上的问题暗示某些分支名称包含禁止字符,因此它们确实出现在实际情况中,可能是从非 Git 软件写入它不应该写入的文件。然后由程序员决定是否检查这些,如果发现,如何处理它们。

2这通常会导致发现文档不完整。在这一点上,您可以检查实现 - 换句话说,阅读源代码 - 和/或实验以发现实际行为。然而,这通常表明编写 API 的人没有仔细考虑边缘情况,并且不同版本的程序或库可能表现不同。