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

Git 如何列出提交中未包含在任何分支中的所有标记?,git,Git,我们有一组存储库,其中许多提交不包含在任何分支中,但仅由于标记而保持活动状态。我想列出所有这样的标签。我还不知道怎么做。有人有什么想法,如何实现吗?标记不是来自提交,它们只是指向提交。但你的问题确实有答案,只是措辞有点奇怪。更准确的措辞会让我们找到答案: 对于每个标记,如何测试标记标识的提交是否包含在任何分支中 因此,我们希望对每个标签进行操作并执行一些测试。有一些命令枚举每个标记。出于编写脚本的目的,git For each ref是最好的工具。因此,我们从以下方面开始: git for-ea

我们有一组存储库,其中许多提交不包含在任何分支中,但仅由于标记而保持活动状态。我想列出所有这样的标签。我还不知道怎么做。有人有什么想法,如何实现吗?

标记不是来自提交,它们只是指向提交。但你的问题确实有答案,只是措辞有点奇怪。更准确的措辞会让我们找到答案:

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

因此,我们希望对每个标签进行操作并执行一些测试。有一些命令枚举每个标记。出于编写脚本的目的,
git For each ref
是最好的工具。因此,我们从以下方面开始:

git for-each-ref refs/tags
它打印出所有标签(到其标准输出)以及关于每个标签目标的额外信息:

$ git for-each-ref refs/tags
04c6e9e9ca34226db095bbaa1218030f99f0b7c6 commit refs/tags/a
d5aef6e4d58cfe1549adef5b436f3ace984e8c86 tag    refs/tags/b
比如说。
a
标记直接指向提交,即是一个轻量级标记;
b
标记指向带注释的标记对象

这还不是一个解决方案,但它正在把我们带到那里。接下来我们要做的事情是找出带注释标记的目标是否是提交对象,如果是,则找到提交对象的哈希。事实证明,使用
--format
指令
%(*objecttype)
%(*objectname)
,每个ref的
git本身就可以做到这一点。令人烦恼的是,当标记是轻量级标记时,这些
%(*…)
指令不会产生任何结果,这需要一些技巧:

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
打印出到达给定对象的每个分支名称(以及每个ref
的其他数据)。我们不关心实际的名称,只关心是否有任何名称,所以让我们计算每个ref的内部
打印多少行。如果为零,则此标记将保持此提交活动,因此让我们打印此标记:

    if [ $n -eq 0 ]; then
        echo "tag $name keeps $obj alive"
    fi
当我们运行整个程序时,有一个小缺陷:例如,这会打印
tag refs/tags/a
。我们可以通过在
--format
的开头使用
%(refname:short)
来解决这个问题,我们将得到
标记a

最后的脚本是:

git for each ref--format='%(refname:short)%(objecttype)%(objectname)%(*objecttype)%(*objectname)refs/tags|
读取名称数据类型dobj时,读取类型iobj;做
如果[$dtype=tag];然后
otype=$itype obj=$iobj
其他的
otype=$dtype obj=$dobj
fi
[$otype==commit]| |继续
n=$(每个参考/头部的git——包含$obj | wc-l)
如果[$n-等式0];然后
echo“标记$name使$obj保持活动状态”
fi
完成
(我已经将此添加到GitHub中。该脚本可能会有一些改进,以使其采用Git选项等,但目前我并不太在意。它也非常慢,可以通过将其作为非常简单的shell脚本以外的其他脚本编写来改进,但请参见前面的备注。)


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