验证标记是否在 master 分支中完成
Verify if a tag was done in the master branch
在我工作的这个项目中,我们根据标签进行部署。虽然标记必须针对 master 分支完成(在您在那里合并发布之后),但有时有人会错误地针对 dev 或发布分支进行标记,这是不正确的。这会导致几个问题。
在我们的部署脚本中,有一个步骤我们从 git 克隆了一个特定的标签,使用的过程类似于这个问题中描述的过程:Download a specific tag with Git
$ git clone
$ git checkout tags/<tag_name>
我如何修改这个脚本来检查这个标记是否真的是针对 master 分支完成的?如果分支不是主分支,我想停止部署并抛出错误。
谢谢。
简短的回答是你不能。至少不是事后。
您可以做的是创建一个带注释的标签。这会在标签上放置一条消息。
此消息然后可以包含当前签出的分支的分支名称。然后您可以检查此消息是否包含 master
.
另一种解决方案是使用 pre-push
提交挂钩来检查是否可以从 master
分支访问任何要推送的新标签。
这两种解决方案都需要在客户端进行设置,因此可能会被愚蠢或恶意的用户回避或禁用。
如果您可以控制 Git 存储库管理器,则可以考虑针对相同的可达性标准进行 pre-receive 挂钩测试。
我的建议是找出使您的 release-tagging 程序自动化的正确方法。
祝你好运!
如果是轻量级标签:
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 的退出代码,returns如果在 master 中则为 0,否则为 1)
正如一些人指出的那样,您的问题只有重新表述才能真正得到解答。这是因为 Git 标记或分支名称仅标识 一个特定的提交。 分支名称的预期效果是标识 提示 分支的提交,它会随着时间的推移而变化,因此它标识的特定提交也会随着时间而变化。标签名称的预期效果是永远标识一个特定的提交,而不更改。因此,如果有人标记 master
,将有一些时间解析名称 master
产生提交哈希 H,并解析标记名称 [=81] =]also 生成提交哈希 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
提前,之后它就不再有用了。如果我们按照我通常喜欢在 Whosebug 上绘制 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
绘制为普通提交,但它可能是带有第二个 parent 的合并提交。如果是这样,请想象第二个 backwards-pointing 行/箭头来自 I
,指向其他一些较早的提交。)
有两种形式,回答的问题略有不同。由于分支名称 do 随着时间的推移而移动,我们可以使用 git branch --contains
找到 all 使标记的提交可访问的分支名称,并查看如果其中之一是 master
。这将为上述情况给出正确/是的答案。不幸的是,它还会告诉您标签 error
包含在 master
中——这是真的!——对于 这个 图:
tag
|
v
...--o--o--H <-- master
/
...--o--G <-- develop
^
|
error
这是因为标签 error
标识了提交 G
,并且提交 G
可从 提交 H
(通过跟随 H
的第二个 parent)。事实上,沿着底行的任何标记,指向包含在 develop
分支中的任何提交,标识包含在 master
分支中的提交,因为 当前在 develop
上的每个提交] 也在 master
.
(顺便说一句,使用 git rev-parse your-tag
和使用 git rev-list -n 1 your-annotated-tag
之间的区别由使用 git rev-parse $tag^{commit}
涵盖。这里的问题是带注释的标签有一个实际的存储库 object,名称指向的 "annotated tag" 类型;单独使用 git rev-parse your-annotated-tag
会找到 标签 object 而不是它的提交。 ^{commit}
后缀语法在 the gitrevisions documentation 中有描述。)
是一种方法来判断任何给定标记指向的提交是否在master
的历史记录中,该历史记录仅发生在first-parent链。它不是最漂亮的:git branch --contains
和 git merge-base --is-ancestor
是查找可达性的常用构建块,但它们都遵循 all parents。要仅跟随 first parents,我们需要使用 git rev-list --first-parent
枚举仅跟随 first [=] 时可从名称 master
访问的所有提交120=]秒。然后我们简单地检查标记的修订是否在该列表中。这是一个糟糕的方法,它清楚地表明我们在做什么:
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
为了加快速度,最好通过搜索 $tagged_commit
的 grep
来传输 git rev-list
输出(丢弃 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
将 运行 一直通过每个可到达的提交;在大型存储库中,这可能需要 秒 。)
git 分支 -a --contains | grep 大师 |剪切-d/-f3
示例:
git branch -a --contains v.1.2.3 | grep 大师 |剪切-d/-f3
在我工作的这个项目中,我们根据标签进行部署。虽然标记必须针对 master 分支完成(在您在那里合并发布之后),但有时有人会错误地针对 dev 或发布分支进行标记,这是不正确的。这会导致几个问题。
在我们的部署脚本中,有一个步骤我们从 git 克隆了一个特定的标签,使用的过程类似于这个问题中描述的过程:Download a specific tag with Git
$ git clone
$ git checkout tags/<tag_name>
我如何修改这个脚本来检查这个标记是否真的是针对 master 分支完成的?如果分支不是主分支,我想停止部署并抛出错误。
谢谢。
简短的回答是你不能。至少不是事后。
您可以做的是创建一个带注释的标签。这会在标签上放置一条消息。
此消息然后可以包含当前签出的分支的分支名称。然后您可以检查此消息是否包含 master
.
另一种解决方案是使用 pre-push
提交挂钩来检查是否可以从 master
分支访问任何要推送的新标签。
这两种解决方案都需要在客户端进行设置,因此可能会被愚蠢或恶意的用户回避或禁用。
如果您可以控制 Git 存储库管理器,则可以考虑针对相同的可达性标准进行 pre-receive 挂钩测试。
我的建议是找出使您的 release-tagging 程序自动化的正确方法。
祝你好运!
如果是轻量级标签:
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 的退出代码,returns如果在 master 中则为 0,否则为 1)
正如一些人指出的那样,您的问题只有重新表述才能真正得到解答。这是因为 Git 标记或分支名称仅标识 一个特定的提交。 分支名称的预期效果是标识 提示 分支的提交,它会随着时间的推移而变化,因此它标识的特定提交也会随着时间而变化。标签名称的预期效果是永远标识一个特定的提交,而不更改。因此,如果有人标记 master
,将有一些时间解析名称 master
产生提交哈希 H,并解析标记名称 [=81] =]also 生成提交哈希 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
提前,之后它就不再有用了。如果我们按照我通常喜欢在 Whosebug 上绘制 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
绘制为普通提交,但它可能是带有第二个 parent 的合并提交。如果是这样,请想象第二个 backwards-pointing 行/箭头来自 I
,指向其他一些较早的提交。)
git branch --contains
找到 all 使标记的提交可访问的分支名称,并查看如果其中之一是 master
。这将为上述情况给出正确/是的答案。不幸的是,它还会告诉您标签 error
包含在 master
中——这是真的!——对于 这个 图:
tag
|
v
...--o--o--H <-- master
/
...--o--G <-- develop
^
|
error
这是因为标签 error
标识了提交 G
,并且提交 G
可从 提交 H
(通过跟随 H
的第二个 parent)。事实上,沿着底行的任何标记,指向包含在 develop
分支中的任何提交,标识包含在 master
分支中的提交,因为 当前在 develop
上的每个提交] 也在 master
.
(顺便说一句,使用 git rev-parse your-tag
和使用 git rev-list -n 1 your-annotated-tag
之间的区别由使用 git rev-parse $tag^{commit}
涵盖。这里的问题是带注释的标签有一个实际的存储库 object,名称指向的 "annotated tag" 类型;单独使用 git rev-parse your-annotated-tag
会找到 标签 object 而不是它的提交。 ^{commit}
后缀语法在 the gitrevisions documentation 中有描述。)
是一种方法来判断任何给定标记指向的提交是否在master
的历史记录中,该历史记录仅发生在first-parent链。它不是最漂亮的:git branch --contains
和 git merge-base --is-ancestor
是查找可达性的常用构建块,但它们都遵循 all parents。要仅跟随 first parents,我们需要使用 git rev-list --first-parent
枚举仅跟随 first [=] 时可从名称 master
访问的所有提交120=]秒。然后我们简单地检查标记的修订是否在该列表中。这是一个糟糕的方法,它清楚地表明我们在做什么:
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
为了加快速度,最好通过搜索 $tagged_commit
的 grep
来传输 git rev-list
输出(丢弃 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
将 运行 一直通过每个可到达的提交;在大型存储库中,这可能需要 秒 。)
git 分支 -a --contains | grep 大师 |剪切-d/-f3
示例: git branch -a --contains v.1.2.3 | grep 大师 |剪切-d/-f3