验证标记是否在主分支中完成

时间:2018-04-05 05:56:25

标签: git version-control repository dvcs git-tag

在我正在工作的这个项目中,我们基于标签进行部署。虽然标签是针对主分支完成的(在你将其释放到那里之后),但有时候有人可能会对dev或release分支进行标记,这是不正确的。这会导致一些问题。

在我们的部署脚本中,我们使用类似于此问题中描述的流程从git克隆特定标记:Download a specific tag with Git

$ git clone
$ git checkout tags/<tag_name>

如何修改此脚本以检查此标记是否实际针对主分支执行?如果分支不是主服务器,我想停止部署并抛出错误。

感谢。

4 个答案:

答案 0 :(得分:4)

git branch -a --contains | grep 大师 | cut -d/ -f3

示例: git branch -a --contains v.1.2.3 | grep 大师 | cut -d/ -f3

答案 1 :(得分:2)

对于轻量级标签:

git branch --contains $(git rev-parse your-tag) | grep '^master$'

如果是带注释的标签:

git branch --contains $(git rev-list -n 1 your-annotated-tag) | grep '^master$'

如果标记指向的提交在master中,则结果不应为空(您甚至可以测试grep的退出代码,如果在master中则返回0,如果不在则返回1)

答案 2 :(得分:1)

简短的回答是你不能。事实上至少没有。

您可以做的是创建带注释的标记。这会在标签上显示一条消息。 然后,此消息可以包含当前检出的分支的分支名称。然后,您可以检查此邮件是否包含master

另一个解决方案是使用pre-push提交钩子来检查要推送的任何新标签是否可以从master分支到达。

这两种解决方案都需要在客户端进行设置,因此可以被愚蠢或恶意用户回避或禁用。

如果您可以控制您的Git存储库管理器,您可以考虑针对相同的可访问性标准进行预接收挂钩测试。

我的建议是找出自动化发布标记程序的正确方法。

祝你好运!

答案 3 :(得分:1)

正如几位人士指出的那样,在重新制定之前,你的问题无法得到真正的回答。这是因为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

目前,名称tagmaster都标识提交H,这是一个合并提交。但是,只要有人在master上创建提交,该图就会变为:

          tag
           |
           v
...--o--o--H--I   <-- master
          /
 ...--o--o   <-- develop

现在master标识了新提交I,因此rev-parse tag^{commit}执行H时会rev-parse master找到I并且I不会是平等的,测试也会失败。

(我在这里提交了提交I作为普通提交,但它可能是与第二个父提交的合并提交。如果是这样,想象一下从git branch --contains出现的第二个向后指向的线/箭头,指向其他一些早先的提交。)

Philippe's answer有两种形式,可以回答一个稍微不同的问题。由于分支名称​​ do 随着时间的推移而移动,我们可以使用master来查找所有分支名称,使标记的提交可以访问,并查看其中一个是否{ {1}}。这将给出上述案例的真实/是答案。不幸的是,它还会告诉您标记error包含在master中 - 这是真的! - 图表:

          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查找标记对象而不是其提交。the gitrevisions documentation中描述^{commit}后缀语法。)

一种判断任何给定标记所指向的提交是否仅在第一父链上发生的master历史记录中的方法。它不是最漂亮的:git branch --containsgit merge-base --is-ancestor是查找可达性的常用构建块,但都遵循所有父项。要仅关注第一个父项,我们需要使用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的{​​{1}}来管道grep输出(由于我们只关心状态而丢弃了grep的输出) :

$tagged_commit
例如

。 (这里的一个缺陷是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 将在每个可达的提交中一直运行;在大型存储库中,这可能需要。)