验证是不是在 master 分支中完成了标记

Posted

技术标签:

【中文标题】验证是不是在 master 分支中完成了标记【英文标题】:Verify if a tag was done in the master branch验证是否在 master 分支中完成了标记 【发布时间】:2018-09-14 20:19:47 【问题描述】:

在我正在工作的这个项目中,我们基于标签进行部署。虽然必须针对 master 分支进行标记(在您合并发布之后),但有时会错误地有人可以针对 dev 或 release 分支进行标记,这是不正确的。这会导致几个问题。

在我们的部署脚本中,有一个步骤是我们从 git 克隆特定标签,使用类似于此问题中描述的过程:Download a specific tag with Git

$ git clone
$ git checkout tags/<tag_name>

我怎样才能修改这个脚本来检查这个标签是否真的是针对主分支完成的?如果分支不是主分支,我想停止部署并抛出错误。

谢谢。

【问题讨论】:

由于标签只是指向提交的指针,分支也是如此,我认为这是不可能的。您可以检查标记的提交是否可以从master 访问,即是否是其历史的一部分。这可能有帮助吗?标记真的必须在master 分支上完成,还是如果提交是masters 历史的一部分就足够了? 我已经测试、更新并验证了我的答案... @kowsky 我需要了解标记的提交是否在主分支中,是的。 【参考方案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 提前,之后它不再有用。如果我们按照我通常喜欢在 *** 上绘制 Git 提交图的方式来绘制,我们可以将其视为:

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

当前名称tagmaster 都标识提交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 --containsgit 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 将一直运行到每个可访问的提交;在大型存储库中,这可能需要 。)

【讨论】:

【参考方案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)

【讨论】:

仅供参考,如果字符串 master 在分支名称中,则此操作失败 - 我使用的是:git branch --contains $(git rev-parse your-tag) | grep '^master$' Indeed ;) 使用 master 似乎很奇怪,但你是对的。我已经更新了答案。谢谢。 idk 如果当前接受的答案在 OP 的最终编辑之前被接受,但这个答案应该是基于意图的接受答案。【参考方案3】:

git 分支 -a --contains | grep 大师 |剪切 -d/ -f3

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

【讨论】:

【参考方案4】:

简短的回答是你不能。至少事后不会。

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

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

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

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

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

祝你好运!

【讨论】:

以上是关于验证是不是在 master 分支中完成了标记的主要内容,如果未能解决你的问题,请参考以下文章

高级表单验证 - 如何在用户完成后在字段旁边添加复选标记? --Javascript [关闭]

Jenkins参数化构建

Git&ApartCI - 如何在邀请功能破坏之前验证代码冲突?

避免将 master 合并到开发分支中

GitVersionTask 需要手动标记主提交

为 GitHub 拉取请求添加验证检查