Git 标签与发布流程:轻量标签、附注标签与签名标签

Git 标签与发布流程:轻量标签、附注标签与签名标签

本文基于 raw/git-tags.md 的学习笔记整理、扩写,补充了让标签真正发挥作用的发布工程实践:命名规范、CI 触发、流水线中的签名校验、标签保护,以及热修复与回滚的处理方式。

英文版:git-tags-in-the-release-process.en.md

同目录相关笔记:git-mental-model-snapshots-pointers.zh.md

缩写对照表

缩写 英文全称 中文
CI/CD Continuous Integration / Continuous Delivery 持续集成 / 持续交付
GPG GNU Privacy Guard GNU 隐私卫士(Git 调用的 OpenPGP 实现)
SSH Secure Shell 安全外壳协议
X.509 ITU-T standard for public key certificates 公钥证书标准(S/MIME 签名使用)
S/MIME Secure/Multipurpose Internet Mail Extensions 安全多用途互联网邮件扩展
SemVer Semantic Versioning 语义化版本(主版本.次版本.修订号
RC Release Candidate 发布候选版本
SBOM Software Bill of Materials 软件物料清单
SHA Secure Hash Algorithm 安全散列算法(Git 对象标识)
PR Pull Request 合并请求

一、为什么发布不能只靠一个 commit 哈希

一次发布本质上是一个承诺:我们上线的就是这段代码,并且是某个人认定它可以发布的。 commit 的 SHA 能精确标识代码,但它不携带任何"发布意图"------没人能从 a1b23c4 看出这是 1.12.3 版本、是什么时候切的、由谁批准的。

承载这份意图的东西就是标签(tag)。但不同形态的标签承载的信息量差别很大,而这个差别会在审计人员、供应链扫描工具或事故复盘问出"证明这个构建来自经过批准的源码"时立刻暴露出来。

标签有三种形态,它们在仓库里是真正不同的对象,而不是同一个东西的三种写法。


二、轻量标签(lightweight tag)------只是一个指针

bash 复制代码
git tag 1.12.3

这条命令只写入一个引用 refs/tags/1.12.3,里面存着一个 commit 哈希,除此之外什么都不创建。
#mermaid-svg-VyB8DuU7Klavby4Q{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VyB8DuU7Klavby4Q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VyB8DuU7Klavby4Q .error-icon{fill:#552222;}#mermaid-svg-VyB8DuU7Klavby4Q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VyB8DuU7Klavby4Q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VyB8DuU7Klavby4Q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VyB8DuU7Klavby4Q .marker.cross{stroke:#333333;}#mermaid-svg-VyB8DuU7Klavby4Q svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VyB8DuU7Klavby4Q p{margin:0;}#mermaid-svg-VyB8DuU7Klavby4Q .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VyB8DuU7Klavby4Q .cluster-label text{fill:#333;}#mermaid-svg-VyB8DuU7Klavby4Q .cluster-label span{color:#333;}#mermaid-svg-VyB8DuU7Klavby4Q .cluster-label span p{background-color:transparent;}#mermaid-svg-VyB8DuU7Klavby4Q .label text,#mermaid-svg-VyB8DuU7Klavby4Q span{fill:#333;color:#333;}#mermaid-svg-VyB8DuU7Klavby4Q .node rect,#mermaid-svg-VyB8DuU7Klavby4Q .node circle,#mermaid-svg-VyB8DuU7Klavby4Q .node ellipse,#mermaid-svg-VyB8DuU7Klavby4Q .node polygon,#mermaid-svg-VyB8DuU7Klavby4Q .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VyB8DuU7Klavby4Q .rough-node .label text,#mermaid-svg-VyB8DuU7Klavby4Q .node .label text,#mermaid-svg-VyB8DuU7Klavby4Q .image-shape .label,#mermaid-svg-VyB8DuU7Klavby4Q .icon-shape .label{text-anchor:middle;}#mermaid-svg-VyB8DuU7Klavby4Q .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VyB8DuU7Klavby4Q .rough-node .label,#mermaid-svg-VyB8DuU7Klavby4Q .node .label,#mermaid-svg-VyB8DuU7Klavby4Q .image-shape .label,#mermaid-svg-VyB8DuU7Klavby4Q .icon-shape .label{text-align:center;}#mermaid-svg-VyB8DuU7Klavby4Q .node.clickable{cursor:pointer;}#mermaid-svg-VyB8DuU7Klavby4Q .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VyB8DuU7Klavby4Q .arrowheadPath{fill:#333333;}#mermaid-svg-VyB8DuU7Klavby4Q .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VyB8DuU7Klavby4Q .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VyB8DuU7Klavby4Q .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VyB8DuU7Klavby4Q .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VyB8DuU7Klavby4Q .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VyB8DuU7Klavby4Q .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VyB8DuU7Klavby4Q .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VyB8DuU7Klavby4Q .cluster text{fill:#333;}#mermaid-svg-VyB8DuU7Klavby4Q .cluster span{color:#333;}#mermaid-svg-VyB8DuU7Klavby4Q div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VyB8DuU7Klavby4Q .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VyB8DuU7Klavby4Q rect.text{fill:none;stroke-width:0;}#mermaid-svg-VyB8DuU7Klavby4Q .icon-shape,#mermaid-svg-VyB8DuU7Klavby4Q .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VyB8DuU7Klavby4Q .icon-shape p,#mermaid-svg-VyB8DuU7Klavby4Q .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VyB8DuU7Klavby4Q .icon-shape .label rect,#mermaid-svg-VyB8DuU7Klavby4Q .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VyB8DuU7Klavby4Q .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VyB8DuU7Klavby4Q .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VyB8DuU7Klavby4Q :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-VyB8DuU7Klavby4Q .ref>*{fill:#eef3ff!important;stroke:#4a72c4!important;}#mermaid-svg-VyB8DuU7Klavby4Q .ref span{fill:#eef3ff!important;stroke:#4a72c4!important;} refs/tags/1.12.3

(仅引用)
发布 commit

没有说明信息、没有打标签的人、没有自己的时间戳、没有签名。它携带的元数据和一个分支名一样多------也就是没有。当私人书签用("我当时在调试的那个提交")没问题,用于正式发布则不合适。


三、附注标签(annotated tag)------带发布元数据的真实对象

bash 复制代码
git tag -a 1.12.3 -m "Release 1.12.3"

这会在对象数据库中创建一个标签对象,引用指向的是这个对象,而不是直接指向 commit。标签对象中保存:

  • 标签名
  • 目标 commit
  • 打标签者身份
  • 时间戳
  • 发布说明

#mermaid-svg-uPcG7lFZIPSoX3Wc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uPcG7lFZIPSoX3Wc .error-icon{fill:#552222;}#mermaid-svg-uPcG7lFZIPSoX3Wc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uPcG7lFZIPSoX3Wc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .marker.cross{stroke:#333333;}#mermaid-svg-uPcG7lFZIPSoX3Wc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uPcG7lFZIPSoX3Wc p{margin:0;}#mermaid-svg-uPcG7lFZIPSoX3Wc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster-label text{fill:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster-label span{color:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster-label span p{background-color:transparent;}#mermaid-svg-uPcG7lFZIPSoX3Wc .label text,#mermaid-svg-uPcG7lFZIPSoX3Wc span{fill:#333;color:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .node rect,#mermaid-svg-uPcG7lFZIPSoX3Wc .node circle,#mermaid-svg-uPcG7lFZIPSoX3Wc .node ellipse,#mermaid-svg-uPcG7lFZIPSoX3Wc .node polygon,#mermaid-svg-uPcG7lFZIPSoX3Wc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .rough-node .label text,#mermaid-svg-uPcG7lFZIPSoX3Wc .node .label text,#mermaid-svg-uPcG7lFZIPSoX3Wc .image-shape .label,#mermaid-svg-uPcG7lFZIPSoX3Wc .icon-shape .label{text-anchor:middle;}#mermaid-svg-uPcG7lFZIPSoX3Wc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .rough-node .label,#mermaid-svg-uPcG7lFZIPSoX3Wc .node .label,#mermaid-svg-uPcG7lFZIPSoX3Wc .image-shape .label,#mermaid-svg-uPcG7lFZIPSoX3Wc .icon-shape .label{text-align:center;}#mermaid-svg-uPcG7lFZIPSoX3Wc .node.clickable{cursor:pointer;}#mermaid-svg-uPcG7lFZIPSoX3Wc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .arrowheadPath{fill:#333333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uPcG7lFZIPSoX3Wc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uPcG7lFZIPSoX3Wc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uPcG7lFZIPSoX3Wc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster text{fill:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc .cluster span{color:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uPcG7lFZIPSoX3Wc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uPcG7lFZIPSoX3Wc rect.text{fill:none;stroke-width:0;}#mermaid-svg-uPcG7lFZIPSoX3Wc .icon-shape,#mermaid-svg-uPcG7lFZIPSoX3Wc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uPcG7lFZIPSoX3Wc .icon-shape p,#mermaid-svg-uPcG7lFZIPSoX3Wc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uPcG7lFZIPSoX3Wc .icon-shape .label rect,#mermaid-svg-uPcG7lFZIPSoX3Wc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uPcG7lFZIPSoX3Wc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uPcG7lFZIPSoX3Wc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uPcG7lFZIPSoX3Wc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-uPcG7lFZIPSoX3Wc .ref>*{fill:#eef3ff!important;stroke:#4a72c4!important;}#mermaid-svg-uPcG7lFZIPSoX3Wc .ref span{fill:#eef3ff!important;stroke:#4a72c4!important;}#mermaid-svg-uPcG7lFZIPSoX3Wc .tag>*{fill:#e6f7ed!important;stroke:#3a9a63!important;}#mermaid-svg-uPcG7lFZIPSoX3Wc .tag span{fill:#e6f7ed!important;stroke:#3a9a63!important;} refs/tags/1.12.3
附注标签对象

打标签者 · 日期 · 说明
发布 commit

查看方式:

bash 复制代码
git show 1.12.3          # 先显示标签对象头,再显示 commit 与 diff
git cat-file -p 1.12.3   # 只看标签对象本身的原始内容

这是一次实质性的升级------发布记录下了是什么什么时候 ,并声称是谁 。问题就在最后这个词:任何拥有写权限的人都能用任意 user.name / user.email 创建标签,这些元数据是自己填的。它记录了作者身份,但并不能证明作者身份。


四、签名附注标签(signed annotated tag)------同样的对象,外加密码学签名

bash 复制代码
git tag -s 1.12.3 -m "Release 1.12.3"

对象结构与字段完全相同,只是在标签内容之上附加了一段签名:
#mermaid-svg-GwcWrTGDns6AAjCw{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GwcWrTGDns6AAjCw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GwcWrTGDns6AAjCw .error-icon{fill:#552222;}#mermaid-svg-GwcWrTGDns6AAjCw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GwcWrTGDns6AAjCw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GwcWrTGDns6AAjCw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GwcWrTGDns6AAjCw .marker.cross{stroke:#333333;}#mermaid-svg-GwcWrTGDns6AAjCw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GwcWrTGDns6AAjCw p{margin:0;}#mermaid-svg-GwcWrTGDns6AAjCw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-GwcWrTGDns6AAjCw .cluster-label text{fill:#333;}#mermaid-svg-GwcWrTGDns6AAjCw .cluster-label span{color:#333;}#mermaid-svg-GwcWrTGDns6AAjCw .cluster-label span p{background-color:transparent;}#mermaid-svg-GwcWrTGDns6AAjCw .label text,#mermaid-svg-GwcWrTGDns6AAjCw span{fill:#333;color:#333;}#mermaid-svg-GwcWrTGDns6AAjCw .node rect,#mermaid-svg-GwcWrTGDns6AAjCw .node circle,#mermaid-svg-GwcWrTGDns6AAjCw .node ellipse,#mermaid-svg-GwcWrTGDns6AAjCw .node polygon,#mermaid-svg-GwcWrTGDns6AAjCw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GwcWrTGDns6AAjCw .rough-node .label text,#mermaid-svg-GwcWrTGDns6AAjCw .node .label text,#mermaid-svg-GwcWrTGDns6AAjCw .image-shape .label,#mermaid-svg-GwcWrTGDns6AAjCw .icon-shape .label{text-anchor:middle;}#mermaid-svg-GwcWrTGDns6AAjCw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GwcWrTGDns6AAjCw .rough-node .label,#mermaid-svg-GwcWrTGDns6AAjCw .node .label,#mermaid-svg-GwcWrTGDns6AAjCw .image-shape .label,#mermaid-svg-GwcWrTGDns6AAjCw .icon-shape .label{text-align:center;}#mermaid-svg-GwcWrTGDns6AAjCw .node.clickable{cursor:pointer;}#mermaid-svg-GwcWrTGDns6AAjCw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GwcWrTGDns6AAjCw .arrowheadPath{fill:#333333;}#mermaid-svg-GwcWrTGDns6AAjCw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GwcWrTGDns6AAjCw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GwcWrTGDns6AAjCw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GwcWrTGDns6AAjCw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GwcWrTGDns6AAjCw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GwcWrTGDns6AAjCw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GwcWrTGDns6AAjCw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GwcWrTGDns6AAjCw .cluster text{fill:#333;}#mermaid-svg-GwcWrTGDns6AAjCw .cluster span{color:#333;}#mermaid-svg-GwcWrTGDns6AAjCw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GwcWrTGDns6AAjCw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GwcWrTGDns6AAjCw rect.text{fill:none;stroke-width:0;}#mermaid-svg-GwcWrTGDns6AAjCw .icon-shape,#mermaid-svg-GwcWrTGDns6AAjCw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GwcWrTGDns6AAjCw .icon-shape p,#mermaid-svg-GwcWrTGDns6AAjCw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GwcWrTGDns6AAjCw .icon-shape .label rect,#mermaid-svg-GwcWrTGDns6AAjCw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GwcWrTGDns6AAjCw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GwcWrTGDns6AAjCw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GwcWrTGDns6AAjCw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-GwcWrTGDns6AAjCw .ref>*{fill:#eef3ff!important;stroke:#4a72c4!important;}#mermaid-svg-GwcWrTGDns6AAjCw .ref span{fill:#eef3ff!important;stroke:#4a72c4!important;}#mermaid-svg-GwcWrTGDns6AAjCw .tag>*{fill:#fff3d6!important;stroke:#d9a422!important;}#mermaid-svg-GwcWrTGDns6AAjCw .tag span{fill:#fff3d6!important;stroke:#d9a422!important;} refs/tags/1.12.3
附注标签对象

打标签者 · 日期 · 说明

+ 密码学签名
发布 commit

校验:

bash 复制代码
git tag -v 1.12.3        # 等价写法:git verify-tag 1.12.3

校验通过证明了三件事,这三点值得说精确:

  1. 该标签是用你事先已信任的公钥所对应的私钥签的。
  2. 签名覆盖的内容自签名以来没有被改动
  3. 该标签指向的,仍是签名当时它指向的那个 commit。

根据配置不同,签名可以使用 GPGSSHX.509(S/MIME) 密钥:

bash 复制代码
# GPG(默认)
git config --global user.signingkey <key-id>

# SSH 签名------通常摩擦最小,直接复用你推送用的密钥
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub

# 让所有附注标签默认签名,避免有人忘了加 -s
git config --global tag.gpgSign true

SSH 签名必须先配置 allowed signers 文件,校验才有意义------没有它,git tag -v 只能说"这里有一段签名",而不能说"这是我信任的签名":

bash 复制代码
git config --global gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
# 每行格式:邮箱  namespaces="git"  ssh-ed25519 AAAA...

五、三者对比

特性 轻量标签 附注标签 签名附注标签
独立的标签对象
打标签者与时间戳
发布说明
密码学签名
能否发现标签对象被篡改 无密码学证明
能否证明持有已批准的签名密钥
是否需要配置签名密钥
git describe 默认是否纳入

从发布治理角度看,优先级很清晰:

  • 签名附注标签 ------ 只要签名与密钥校验在运维上支持得起来,就应优先使用。
  • 附注标签 ------ 暂时无法签名时可接受的退而求其次方案。
  • 轻量标签 ------ 正式发布应避免使用;它既没有发布元数据,也没有真实性证据。

还有一个容易忽略的副作用:git describe 默认会忽略轻量标签,除非加 --tags。如果你的构建用 git describe 生成版本号,轻量标签会悄无声息地不生效。

bash 复制代码
git describe --tags --always --dirty    # 例如 1.12.3-4-gA1b23c4-dirty

六、标签如何驱动整条发布流程

标签不是发布之后 贴上去的标记------在标签驱动(tag-driven)的流水线里,创建标签这个动作本身就是发布的起点。仅此一个设计,就让每一个进入生产环境的构建都拥有了一个不可变、已签名、可读的入口。
#mermaid-svg-vI1FJtCNcj8B5QZh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vI1FJtCNcj8B5QZh .error-icon{fill:#552222;}#mermaid-svg-vI1FJtCNcj8B5QZh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vI1FJtCNcj8B5QZh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vI1FJtCNcj8B5QZh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vI1FJtCNcj8B5QZh .marker.cross{stroke:#333333;}#mermaid-svg-vI1FJtCNcj8B5QZh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vI1FJtCNcj8B5QZh p{margin:0;}#mermaid-svg-vI1FJtCNcj8B5QZh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster-label text{fill:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster-label span{color:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster-label span p{background-color:transparent;}#mermaid-svg-vI1FJtCNcj8B5QZh .label text,#mermaid-svg-vI1FJtCNcj8B5QZh span{fill:#333;color:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh .node rect,#mermaid-svg-vI1FJtCNcj8B5QZh .node circle,#mermaid-svg-vI1FJtCNcj8B5QZh .node ellipse,#mermaid-svg-vI1FJtCNcj8B5QZh .node polygon,#mermaid-svg-vI1FJtCNcj8B5QZh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vI1FJtCNcj8B5QZh .rough-node .label text,#mermaid-svg-vI1FJtCNcj8B5QZh .node .label text,#mermaid-svg-vI1FJtCNcj8B5QZh .image-shape .label,#mermaid-svg-vI1FJtCNcj8B5QZh .icon-shape .label{text-anchor:middle;}#mermaid-svg-vI1FJtCNcj8B5QZh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vI1FJtCNcj8B5QZh .rough-node .label,#mermaid-svg-vI1FJtCNcj8B5QZh .node .label,#mermaid-svg-vI1FJtCNcj8B5QZh .image-shape .label,#mermaid-svg-vI1FJtCNcj8B5QZh .icon-shape .label{text-align:center;}#mermaid-svg-vI1FJtCNcj8B5QZh .node.clickable{cursor:pointer;}#mermaid-svg-vI1FJtCNcj8B5QZh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vI1FJtCNcj8B5QZh .arrowheadPath{fill:#333333;}#mermaid-svg-vI1FJtCNcj8B5QZh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vI1FJtCNcj8B5QZh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vI1FJtCNcj8B5QZh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vI1FJtCNcj8B5QZh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vI1FJtCNcj8B5QZh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vI1FJtCNcj8B5QZh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster text{fill:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh .cluster span{color:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vI1FJtCNcj8B5QZh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vI1FJtCNcj8B5QZh rect.text{fill:none;stroke-width:0;}#mermaid-svg-vI1FJtCNcj8B5QZh .icon-shape,#mermaid-svg-vI1FJtCNcj8B5QZh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vI1FJtCNcj8B5QZh .icon-shape p,#mermaid-svg-vI1FJtCNcj8B5QZh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vI1FJtCNcj8B5QZh .icon-shape .label rect,#mermaid-svg-vI1FJtCNcj8B5QZh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vI1FJtCNcj8B5QZh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vI1FJtCNcj8B5QZh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vI1FJtCNcj8B5QZh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-vI1FJtCNcj8B5QZh .fail>*{fill:#ffe1e1!important;stroke:#c04a4a!important;}#mermaid-svg-vI1FJtCNcj8B5QZh .fail span{fill:#ffe1e1!important;stroke:#c04a4a!important;} 签名不可信

或签名者无权限
校验通过
发布 commit 合入 main

(CI 通过、PR 已批准)
发布负责人签名打标签

git tag -s 1.12.3 -m 'Release 1.12.3'
git push origin 1.12.3
CI:校验标签
流水线失败

不产出任何制品
从打标签的 commit 构建

版本号取自标签
发布制品 + SBOM

把摘要写入发布记录
按标签部署

预发 → 生产
留存证据:

标签、签名者、审批、摘要

6.1 定好命名规范,不要临场发挥

选定一种规范并在 CI 中强制执行。常见做法是 SemVer 加 v 前缀(v1.12.3)------前缀让标签能轻松地与分支名、与脚本里的裸数字区分开。原始笔记用的是不带前缀的形式(1.12.3);两种都可行,但在同一个仓库里混用几乎必然会把工具链搞坏。

bash 复制代码
v1.12.3          # 正式发布
v1.13.0-rc.1     # 发布候选版本------SemVer 预发布号,排序在 v1.13.0 之前

列出标签时要按版本序而非字典序:

bash 复制代码
git tag --sort=-v:refname | head
git tag -l 'v1.12.*'

6.2 标签默认不会被推送

这一点反复坑人:git push 不会推送标签。

bash 复制代码
git push origin v1.12.3      # 推送单个标签------发布流程中最明确、最正确的写法
git push --follow-tags       # 推送 commit 及其可达的附注标签
git push --tags              # 推送所有本地标签,包括垃圾标签------脚本里别用

发布流程里优先推送明确指名的那一个标签。--tags 会把某人本地随手打的半成品标签一并发布出去。

6.3 让 CI 在构建之前先校验标签

没人校验的签名只是装饰。校验失败时流水线必须拒绝构建:

bash 复制代码
set -euo pipefail
git fetch --tags --force
git tag -v "$TAG"                                    # 不可信则返回非零,直接失败
test "$(git rev-parse "$TAG^{commit}")" = "$(git rev-parse origin/main)"   # 可选:要求标签落在 main 上

在 GitHub Actions 中,触发器和检出方式都很关键------actions/checkout 默认是不含标签对象的浅克隆,必须显式拉取:

yaml 复制代码
on:
  push:
    tags: ['v[0-9]+.[0-9]+.[0-9]+']

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # 完整历史 + 标签对象,verify/describe 都依赖它
      - name: Verify release tag
        run: git tag -v "${GITHUB_REF_NAME}"

6.4 已发布的标签视为不可变

移动一个标签,是对发布历史破坏性最强的操作:已经拉取过 v1.12.3 的人手里还是旧对象,之后才拉取的人拿到的是另一个对象,于是 "v1.12.3" 不再指代任何确定的东西。签名在这里救不了你------被强制移动的标签,对新的 commit 而言签名依然是有效的。

  • 在代码托管平台开启标签保护(GitHub:rulesets → tag rules;GitLab:protected tags),只允许发布角色创建标签,禁止任何人删除或强制更新。
  • 发布有问题就切一个新版本v1.12.4 的成本很低;被改写过的 v1.12.3 则是永久性的信任问题。
  • 确实需要撤回某个标签时,显式删除并对外公告------绝不要悄悄改指向:
bash 复制代码
git push origin --delete v1.12.3   # 需要保护规则允许删除
git tag -d v1.12.3                 # 清理本地

6.5 热修复与回滚都从标签出发

因为标签就是精确的快照指针,这两个操作都是机械化的:

bash 复制代码
# 从已发布版本拉热修复分支,而不是从一直在动的 main
git switch --detach v1.12.3
git switch -c hotfix/1.12.4
# ... 修复、评审、按流程合入 ...
git tag -s v1.12.4 -m "Release 1.12.4 --- 修复会话泄漏"
git push origin v1.12.4

# 回滚部署:重新部署此前已校验过的标签
git tag -v v1.12.2 && deploy v1.12.2

注意回滚时仍然执行了校验。故障压力之下的回滚,恰恰是未经校验的制品最容易溜进生产的时刻。


七、信任边界------签名不能证明什么

这是多数团队理解得最模糊的一点。有效的签名证明的是对签名密钥的控制权,它不能证明这次发布经过了评审、测试或批准。 持有受信任密钥的人可以给任何东西签名,包括一个从未走过 PR 的 commit。

补上这道缺口靠的是组织流程,而不是 Git 的某个功能:

  • 明确哪些密钥受信任 ------ 用纳入版本管理的 allowed signers 文件,或平台托管的密钥清单,且只能通过评审变更。
  • 确认签名者有发布批准权限 ------ 密钥有效性与发布授权是两个独立问题,必须对照一份持续维护的人员名单核验。
  • 保护标签不被删除和强制更新 ------ 见 6.4。
  • 及时吊销已泄露的密钥,并对疑似泄露时间窗之后签名的一切内容重新校验或重新签发。
  • 留存审批与制品来源证据 ------ 标签、签名者、审批记录、制品摘要要放在一起保存,保留期限满足审计要求。
  • 校验整条链路,而不只是标签 ------ 一个签名标签,如果对应的构建拉取的是未锁定版本的依赖,那么当天仓库给什么就发布什么。

#mermaid-svg-MY5AwCiCD8xutNFF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MY5AwCiCD8xutNFF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MY5AwCiCD8xutNFF .error-icon{fill:#552222;}#mermaid-svg-MY5AwCiCD8xutNFF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MY5AwCiCD8xutNFF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MY5AwCiCD8xutNFF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MY5AwCiCD8xutNFF .marker.cross{stroke:#333333;}#mermaid-svg-MY5AwCiCD8xutNFF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MY5AwCiCD8xutNFF p{margin:0;}#mermaid-svg-MY5AwCiCD8xutNFF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MY5AwCiCD8xutNFF .cluster-label text{fill:#333;}#mermaid-svg-MY5AwCiCD8xutNFF .cluster-label span{color:#333;}#mermaid-svg-MY5AwCiCD8xutNFF .cluster-label span p{background-color:transparent;}#mermaid-svg-MY5AwCiCD8xutNFF .label text,#mermaid-svg-MY5AwCiCD8xutNFF span{fill:#333;color:#333;}#mermaid-svg-MY5AwCiCD8xutNFF .node rect,#mermaid-svg-MY5AwCiCD8xutNFF .node circle,#mermaid-svg-MY5AwCiCD8xutNFF .node ellipse,#mermaid-svg-MY5AwCiCD8xutNFF .node polygon,#mermaid-svg-MY5AwCiCD8xutNFF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MY5AwCiCD8xutNFF .rough-node .label text,#mermaid-svg-MY5AwCiCD8xutNFF .node .label text,#mermaid-svg-MY5AwCiCD8xutNFF .image-shape .label,#mermaid-svg-MY5AwCiCD8xutNFF .icon-shape .label{text-anchor:middle;}#mermaid-svg-MY5AwCiCD8xutNFF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MY5AwCiCD8xutNFF .rough-node .label,#mermaid-svg-MY5AwCiCD8xutNFF .node .label,#mermaid-svg-MY5AwCiCD8xutNFF .image-shape .label,#mermaid-svg-MY5AwCiCD8xutNFF .icon-shape .label{text-align:center;}#mermaid-svg-MY5AwCiCD8xutNFF .node.clickable{cursor:pointer;}#mermaid-svg-MY5AwCiCD8xutNFF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MY5AwCiCD8xutNFF .arrowheadPath{fill:#333333;}#mermaid-svg-MY5AwCiCD8xutNFF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MY5AwCiCD8xutNFF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MY5AwCiCD8xutNFF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MY5AwCiCD8xutNFF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MY5AwCiCD8xutNFF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MY5AwCiCD8xutNFF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MY5AwCiCD8xutNFF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MY5AwCiCD8xutNFF .cluster text{fill:#333;}#mermaid-svg-MY5AwCiCD8xutNFF .cluster span{color:#333;}#mermaid-svg-MY5AwCiCD8xutNFF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MY5AwCiCD8xutNFF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MY5AwCiCD8xutNFF rect.text{fill:none;stroke-width:0;}#mermaid-svg-MY5AwCiCD8xutNFF .icon-shape,#mermaid-svg-MY5AwCiCD8xutNFF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MY5AwCiCD8xutNFF .icon-shape p,#mermaid-svg-MY5AwCiCD8xutNFF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MY5AwCiCD8xutNFF .icon-shape .label rect,#mermaid-svg-MY5AwCiCD8xutNFF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MY5AwCiCD8xutNFF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MY5AwCiCD8xutNFF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MY5AwCiCD8xutNFF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-MY5AwCiCD8xutNFF .ok>*{fill:#e6f7ed!important;stroke:#3a9a63!important;}#mermaid-svg-MY5AwCiCD8xutNFF .ok span{fill:#e6f7ed!important;stroke:#3a9a63!important;}#mermaid-svg-MY5AwCiCD8xutNFF .gap>*{fill:#ffe1e1!important;stroke:#c04a4a!important;}#mermaid-svg-MY5AwCiCD8xutNFF .gap span{fill:#ffe1e1!important;stroke:#c04a4a!important;} 由流程与制度

填补的缺口
签名不能证明的
代码经过评审
签名者获得授权
构建输入可信
签名能证明的
持有密钥控制权
内容未被改动
仍指向原始 commit


八、命令速查

目标 命令
创建签名发布标签 git tag -s v1.12.3 -m "Release 1.12.3"
创建附注标签(不签名) git tag -a v1.12.3 -m "Release 1.12.3"
给历史 commit 打标签 git tag -s v1.12.3 <commit> -m "..."
校验签名 git tag -v v1.12.3
只看标签对象 git cat-file -p v1.12.3
按版本序列出 git tag --sort=-v:refname
列出标签及说明 git tag -n9 -l 'v1.*'
描述 HEAD 最近的标签 git describe --tags --always --dirty
两次发布之间的提交 git log --oneline v1.12.2..v1.12.3
推送单个标签 git push origin v1.12.3
推送 commit 及其附注标签 git push --follow-tags
拉取标签(并清理失效标签) git fetch --tags --prune --prune-tags
删除远端标签 git push origin --delete v1.12.3
默认对所有附注标签签名 git config --global tag.gpgSign true

九、值得记住的坑

  • git push 不推送标签。 "已经发出去了"却没有触发任何流水线,八成就是这个原因。
  • CI 的浅克隆里没有标签对象。 git describegit tag -v 都会以令人困惑的方式失败;设置 fetch-depth: 0
  • 没有配置信任源时,git tag -v 校验不了任何有意义的东西 ------ GPG 需要公钥进钥匙串,SSH 需要 allowedSignersFile
  • 对已发布标签执行 git tag -f,本质就是一次历史改写。已经拉取过的使用方不会更新,也不会收到任何提示。
  • 轻量标签对 git describe 不可见 (除非加 --tags)------ 版本号会悄悄停留在旧值上。
  • 标签打错 commit 比不打标签更糟。 推送前务必确认 git rev-parse v1.12.3^{commit}^{commit} 会把标签对象逐层剥到它最终指向的 commit。
  • 签名 ≠ 批准。 在向任何人宣称"发布链路是安全的"之前,请重读第七节。

小结

轻量标签是一个书签;附注标签是一份发布记录;签名附注标签则是一份可以证明未被篡改的发布记录------再配上 CI 的校验与平台的标签保护,它就成了整条发布流程赖以挂靠的不可变入口。密码学是其中简单的那一半;真正把"签名有效"变成"签名有意义"的,是信任库、授权名单和标签保护规则。

相关推荐
wear工程师1 小时前
Git 冲突怎么解决:别只删标记,先看懂暂存区的 1、2、3 阶段
git
村雨遥1 小时前
覆盖提交不等于删除,这才是彻底清除 Git 历史的正确姿势
git·github
淮北4942 小时前
ubuntu22 默认输入法调整频率
运维·服务器·git·ubuntu·elasticsearch
dunge20264 小时前
2026 ChatGPT Plus / Pro + Codex 实战:从 0 搭一套 AI 编程项目模板,AGENTS.md + Git + 测试一次配好
人工智能·git·chatgpt
福如意如我心意1 天前
git清除仓库敏感文件并同步至远端的操作
git
Brilliantwxx1 天前
【Linux】 开发|Git 版本控制与 gdb/cgdb 调试
linux·笔记·git
.Hypocritical.2 天前
Git 全套实操|配置 /.gitignore/ 分支合并 / 冲突解决 /git stash 完整教程
git
来日方长。。。。long2 天前
Hermes Agent橙皮书共读|第三篇:保姆级实战部署|本地/VPS从零搭建Hermes
ide·git·hermes
其实防守也摸鱼2 天前
VS Code Git 工作树:解锁多分支并行开发新体验
数据库·git·安全·oracle·架构·自动化