如果有带注释的标签,也只使用 gitlab 轻量级标签 - GitPython

Only use gitlab light-weight tags also if there is a annotated tag - GitPython

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

我的问题:我使用 git 标签来获取标签的哈希值。使用此哈希,我将向我的数据库中添加一个文件。问题是,我总是必须确保我保存了轻量级标签的哈希值。如果有人在创建标签时添加了一条消息,它将是一个带注释的标签。所以我想改变我的 git.describe 功能,它只是 returns 我的轻量级标签。

我认为这是可能的,但我找不到任何例子。我唯一阅读的是文档和使用 ref/tags。但是我不知道怎么办。

我为此使用了 GitPython:https://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 只是 添加了 使用仅轻量级标签的能力。它永远不会删除使用带注释的标签的能力。

I don't get this part of the documentation:

--tags

Instead of using only the annotated tags, use any tag found in refs/tags namespace. This option enables matching a lightweight (non-annotated) tag.

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

to How can I list all lightweight tags? This question has my answer about how to enumerate all lightweight tags, using git for-each-ref. The background missing here (and in the git tag documentation from which the above is quoted) is that in Git, all names—branch names, tag names, and so on—are specific forms of refs or references. These references live in namespaces.

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

同样的想法在 Git 中起作用:如果你有一个名为 xyz 分支 tag命名为xyz,可以用全名,或者更全的名字来拼写出你指的是哪一个:refs/heads/xyz是分支,refs/tags/xyz 是标签。 所有 个标签都在 refs/tags/ 下。我们通常只是省略 refs/tags/ 部分并说“tag xyz”。

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

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

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

$ 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]

第一个散列 ID 2d9685d47a7e516281aa093bf0cddc8aafa72448 注释标签对象 的散列 ID。 git cat-file -p 命令打印出带注释的标记对象的内容,该内容以 object 行开头,然后是 type 行,然后是 tag 行,依此类推。这个特殊注释标签的主要目的是携带 PGP 签名(此处截断)。

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

考虑到这一点,考虑 git describe

要让 git describe 完成其默认工作,它必须枚举带注释的标签。这意味着查看每个 refs/tags/* 名称。每个这样的名字要么是一个轻量级标签,直接引用一些不是带注释的标签对象的对象,要么是一个带注释的标签,因为它指的是引用某个目标对象的带注释的标记对象。这有点含糊——或者说是满脑子的东西——所以如果需要的话多看几次。

因为 git describe 只想查看 annotated 标签,它所做的是查看 every 标签,然后扔掉那些直接引用不是注释标签的标签。这意味着 git describe 现在只有带注释的标签的 table。然后它可以继续工作并找出这些标签名称中的哪些(如果有的话)是 suitable.

--tags 标志只是告诉 git tag不要丢弃仅用于轻量级的标签。 剩下的就是 [=130= refs/tags 名称中的所有 个标签space,即所有标签。

使用--exclude

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

您可能只对各种实验感兴趣,不包括早期版本。在这种情况下,您可以通过以下方式排除所有 releases/*early/*

git describe --exclude 'releases/*' --exclude 'early/*'

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

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

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

  • 对于每个带注释的标签,添加 --exclude 和标签。

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


1如果标签的名称包含特殊的 glob 字符,即 *?[,我们可能在这里惹上麻烦。一个可以:

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

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

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