繁体   English   中英

如何列出提交中未包含在任何分支中的所有标签?

[英]How to list all tags from commits, which are not contained in any branch?

我们有一堆存储库,其中许多提交不包含在任何分支中,而只是由于标签而保持活动状态。 我想列出所有这些标签。 还没想好,怎么弄。 有没有人有想法,如何实现?

标签不是来自提交,它们只是指向提交。 但你的问题确实有答案——只是措辞有点奇怪。 更准确的措辞将我们引向答案:

对于每个标签,我如何测试标签标识的提交是否包含在任何分支中?

因此,我们想对每个标签进行操作并执行一些测试。 有枚举每个标签的命令。 出于脚本目的, git for-each-ref是最好的1工具。 所以我们开始:

git for-each-ref refs/tags

它打印出所有标签(到其标准输出)以及关于每个标签目标的额外信息:

$ git for-each-ref refs/tags
04c6e9e9ca34226db095bbaa1218030f99f0b7c6 commit refs/tags/a
d5aef6e4d58cfe1549adef5b436f3ace984e8c86 tag    refs/tags/b

例如。 a标签直接指向一个提交,即是一个轻量级标签; b标签转到带注释的标签对象。

这还不是一个解决方案,但它让我们到达那里。 我们要做的下一件事是找出带注释标签的目标是否是提交对象,如果是,则找到提交对象的哈希。 事实证明, git for-each-ref本身可以使用--format指令%(*objecttype)%(*objectname)来做到这一点。 令人讨厌的是,当标签是轻量级标签时,这些%(*...)指令不会产生任何内容,这需要一些噱头:

git for-each-ref \
    --format='%(refname) %(objecttype) %(objectname) %(*objecttype) %(*objectname)' \
    refs/tags

(为了发布目的,我已将其分成多行;在脚本中,我们可以只有一个长行而没有反斜杠-换行符序列)。

这会产生一系列三列或五列的行作为其输出。 前三列是引用名称、对象类型(可能是“标签”)、初始标签哈希 ID,然后如果存在最后两列,则是目标类型和最终目标 ID。 我们需要将这些提供给一个 shell 脚本:

git for-each-ref \
    --format='%(refname) %(objecttype) %(objectname) %(*objecttype) %(*objectname)' \
    refs/tags |
    while read name dtype dobj itype iobj; do
        ...
    done

现在,在...部分,我们实现我们的测试:直接或间接对象是提交,如果是,它是否可以通过任何分支名称访问?

“是提交的对象”测试非常简单。 首先,让我们使用间接宾语和名称(如果存在),否则使用直接宾语和名称:

    if [ $dtype = tag ]; then
        otype=$itype obj=$iobj
    else
        otype=$dtype obj=$dobj
    fi

现在我们将跳过非提交对象:

    [ $otype == commit ] || continue

最后,我们将测试对象的哈希 ID 是否可以从某个分支名称访问:

    n=$(git for-each-ref refs/heads --contains $obj | wc -l)

这个for-each-ref打印出到达给定对象的每个分支名称(以及for-each-ref其他数据)。 我们不关心实际的名称,只是是否任何人的名字,让我们看看有多少行内for-each-ref打印。 如果它为零,则此标记使此提交保持活动状态,因此让我们打印该标记:

    if [ $n -eq 0 ]; then
        echo "tag $name keeps $obj alive"
    fi

当我们运行整个过程时,有一个小缺陷:例如,这会打印tag refs/tags/a 我们可以通过在初始--format使用%(refname:short)来解决这个问题,我们将得到tag a

所以,最终的脚本如下:

git for-each-ref --format='%(refname:short) %(objecttype) %(objectname) %(*objecttype) %(*objectname)' refs/tags |
while read name dtype dobj itype iobj; do
    if [ $dtype = tag ]; then
        otype=$itype obj=$iobj
    else
        otype=$dtype obj=$dobj
    fi
    [ $otype == commit ] || continue
    n=$(git for-each-ref refs/heads --contains $obj | wc -l)
    if [ $n -eq 0 ]; then
        echo "tag $name keeps $obj alive"
    fi
done

(我已将其添加到 GitHub 此处。该脚本可以进行一些改进以使其采用 Git 选项等,但现在我不太在意。它也很慢,可以通过将其编写为不是一个非常简单的 shell 脚本,但请参阅前面的评论。)


1最好是那些难以衡量的事情之一,但至少发现它是最好的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM