正如一些人所指出的,您的问题在重新表述之前无法真正得到回答。这是因为 Git 标记或分支名称只是标识一个特定的提交。 分支名称的预期效果是标识一个分支的 tip 提交,该提交随时间而变化,因此它标识的特定提交也会随着时间而变化。标记名称的预期效果是永久标识一个特定的提交,而不会更改。因此,如果有人标记master,将在某些时刻解析名称master 产生提交哈希H,解析标签名称也 产生提交哈希 H:
if test $(git rev-parse master) = $(git rev-parse $tag^{commit}); then
echo "master and $tag both identify the same commit"
else
echo "master and $tag identify two different commits"
fi
此特定测试有效,直到有人将分支名称 master 提前,之后它不再有用。如果我们按照我通常喜欢在 StackOverflow 上绘制 Git 提交图的方式来绘制,我们可以将其视为:
tag
|
v
...--o--o--H <-- master
/
...--o--o <-- develop
当前名称tag 和master 都标识提交H,这是一个合并提交。但是,一旦有人在 master 上创建了 new 提交,图表就会变成:
tag
|
v
...--o--o--H--I <-- master
/
...--o--o <-- develop
现在master 识别出新的提交I,因此执行rev-parse tag^{commit} 将找到H,而执行rev-parse master 将找到I,它们将不相等,测试将失败。
(我在这里将提交I 绘制为普通提交,但它可能是与第二个父级的合并提交。如果是这样,想象第二个向后指向的线/箭头从I 出现,指向其他一些较早的提交。)
Philippe's answer 有两种形式,回答略有不同的问题。由于分支名称确实会随着时间移动,我们可以使用git branch --contains 来查找使标记提交可访问的所有分支名称,并查看其中一个是否为master .这将为上述情况给出正确/是的答案。不幸的是,它还会告诉您标签 error 包含在 master 中——这是真的!——对于 this 图表:
tag
|
v
...--o--o--H <-- master
/
...--o--G <-- develop
^
|
error
这是因为标签error 标识了提交G,并且提交G 可从提交H 访问(通过跟随H 的第二个父级)。事实上,沿着底行的任何标签,指向包含在develop 分支中的任何提交,都标识了包含在master 分支中的提交,因为当前在develop 上的每个提交也在master。
(顺便说一句,使用git rev-parse your-tag 和使用git rev-list -n 1 your-annotated-tag 之间的区别可以通过使用git rev-parse $tag^{commit} 来解决。这里的问题是带注释的标记有一个实际的存储库对象,类型为“带注释的标记”,名称点;单独使用 git rev-parse your-annotated-tag 会找到 tag 对象而不是其提交。^{commit} 后缀语法在 the gitrevisions documentation 中描述。)
有一种方法来判断任何给定标记指向的提交是否在仅发生在第一父链上的master 的历史中。这不是最漂亮的:git branch --contains 和 git merge-base --is-ancestor 是寻找可达性的常用构建块,但它们都遵循所有父母。要仅关注 first 父母,我们需要使用 git rev-list --first-parent 来枚举所有可从名称 master 获得的提交,当仅关注第一父母时。然后我们只需检查标记的修订版是否在该列表中。这是一种不好的方法,可以清楚地说明我们在做什么:
tagged_commit=$(git rev-parse $tag^{commit}) ||
fatal "tag $tag does not exist or does not point to a commit object"
found=false
for hash in $(git rev-list --first-parent master); do
test $hash == $tagged_commit && found=true
done
为了加快速度,最好将git rev-list 输出通过grep 来搜索$tagged_commit(丢弃grep 的输出,因为我们只关心状态):
if git rev-list --first-parent master | grep $tagged_commit >/dev/null; then
echo "$tag points to a commit reachable via first-parent from master"
else
echo "$tag does not point to a commit reachable via first-parent from master"
fi
例如。 (这里的一个缺陷是git rev-list 将一直运行到每个可访问的提交;在大型存储库中,这可能需要 秒。)