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

时间:2020-10-31 10:25:01

标签: git

我们有很多存储库,其中很多提交都没有包含在任何分支中,而是仅由于标签而保持活动状态。我想列出所有这些标签。还没有弄清楚,该怎么做。有没有人有一个想法,如何实现?

1 个答案:

答案 0 :(得分:3)

标记不是来自 提交,它们只是指向提交。但是您的问题确实有答案–只是有点奇怪。更准确的措词将使我们得到答案:

如何为每个标签测试标签所标识的提交是否包含在任何分支中?

因此,我们希望对每个标签进行操作并执行一些测试。有一些列举每个标签的命令。出于脚本目的,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。我们可以通过在开头的%(refname:short)中使用--format来解决此问题,而获得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 here。该脚本可以进行一些改进以使其采用Git选项等,但是现在我并不十分在意。它也很慢,可以通过将其编写为非常简单的Shell脚本以外的其他内容来进行改进,但请参见前面的说明。)


1 最佳是那些难以衡量的事情之一,但至少 I 认为它是最好的。< / p>