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

tes*_*ter 7 git version-control dvcs repository git-tag

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

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

$ git clone
$ git checkout tags/<tag_name>
Run Code Online (Sandbox Code Playgroud)

如何修改此脚本以检查此标记是否实际上是针对 master 分支完成的?如果分支不是主分支,我想然后停止部署并抛出错误。

谢谢。

tor*_*rek 8

正如一些人指出的那样,您的问题在重新表述之前无法真正得到解答。这是因为 Git 标签或分支名称只是标识一个特定的提交。 分支名称的预期效果是标识分支的提示提交,该提交随时间变化,以便它标识的特定提交也随时间变化。标签名称的预期效果是永远标识一个特定的提交,而无需更改。因此,如果有人标记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
Run Code Online (Sandbox Code Playgroud)

这个特定的测试是有效的,直到有人使分支名称master提前,之后它就不再有用了。如果我们按照我通常喜欢在 StackOverflow 上绘制 Git 提交图的方式来绘制它,我们可以将其视为:

          tag
           |
           v
...--o--o--H   <-- master
          /
 ...--o--o   <-- develop
Run Code Online (Sandbox Code Playgroud)

目前,名称tagmaster两者都标识提交 H,这是一个合并提交。但是,一旦有人在 上创建新的提交master,图表就会变成:

          tag
           |
           v
...--o--o--H--I   <-- master
          /
 ...--o--o   <-- develop
Run Code Online (Sandbox Code Playgroud)

现在master识别新提交I,因此在执行rev-parse tag^{commit}will find 的H同时执行rev-parse masterwill findI并且它们不会相等并且测试将失败。

(我在I这里将提交绘制为普通提交,但它可能是与第二个父项的合并提交。如果是这样,请想象从 中出现的第二条向后指向的线/箭头I,指向其他一些较早的提交。)

菲利普的回答有两种形式,并回答了一个略有不同的问题。由于分支名称举动随着时间的推移,我们可以利用git branch --contains查找所有分支机构的名称,使带标签的提交可达,看看是否其中之一是master。这将为上述案例提供正确/是的答案。不幸的是,它还会告诉您标签error包含在图表中master——这是真的!

          tag
           |
           v
...--o--o--H   <-- master
          /
 ...--o--G   <-- develop
         ^
         |
       error
Run Code Online (Sandbox Code Playgroud)

这是因为标签error识别提交G,并承诺G从可到达的提交H(由下面的第二个亲本H)。事实上,底行的任何标记,指向develop分支中包含的任何提交,标识包含在master分支中的提交,因为当前的每个提交develop也是 onmaster

(顺便说一句, usinggit rev-parse your-tag和 using之间的区别git rev-list -n 1 your-annotated-tag由 using 涵盖git rev-parse $tag^{commit}。这里的问题是带注释的标签有一个实际的存储库对象,类型为“带注释的标签”,名称指向它;git rev-parse your-annotated-tag单独使用会找到标签对象而不是它的提交。^{commit}后缀语法在gitrevisions 文档中描述。)

这里一个方式告诉是否提交到任何给定的标记点,是历史上的master只发生在第一父链。它不是最漂亮的:git branch --contains并且git 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
Run Code Online (Sandbox Code Playgroud)

为了使这个更快,最好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
Run Code Online (Sandbox Code Playgroud)

例如。(这里的一个缺陷是,它git rev-list会贯穿每个可到达的提交;在大型存储库中,这可能需要几秒钟。)


Phi*_*ppe 6

对于轻量级标签:

git branch --contains $(git rev-parse your-tag) | grep '^master$'
Run Code Online (Sandbox Code Playgroud)

如果是带注释的标签:

git branch --contains $(git rev-list -n 1 your-annotated-tag) | grep '^master$'
Run Code Online (Sandbox Code Playgroud)

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


Moh*_*avi 5

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

例子:

git branch -a --contains v.1.2.3 | grep master | cut -d/ -f3
Run Code Online (Sandbox Code Playgroud)