Git 规约

提交消息/Commit Message

格式

推荐使用《约定式提交 (Convertional Commits)》格式书写 Commit Message。(请完整阅读后再继续以下内容)如果你在使用 npx f2elint@latest 初始化项目时启用了 Commitlint, 那么约定式提交规则也将一并启用。

<类型>[范围]: <描述>

[正文]

[脚注]

语言

  • 国际化项目和开源项目推荐使用英文书写 Commit Message,以增强协作。
  • 其他项目推荐使用开发团队最普遍使用的语言,以提升效率。
  • 不要使用其他人无法理解的语言书写 Commit Message。
  • 不要使用拼音或不常见的缩写书写 Commit Message,以避免歧义。

类型/type

type 用来描述本次提交的改动类型,可选值及对应含义如下:

  • feat: 新增功能
  • fix: 修复 bug
  • docs: 文档相关的改动
  • style: 对代码的格式化改动,代码逻辑并未产生任何变化(例如代码缩进,分号的移除和添加)
  • test: 新增或修改测试用例
  • refactor: 重构代码或其他优化举措
  • chore: 项目工程方面的改动,代码逻辑并未产生任何变化
  • revert: 恢复之前的提交

注意:

  1. commit message 的 type 和 changelog 的 type 存在紧密联系,然而它们两者之间并非一一对应,比如在 changelog 中一般不会指出文档 docs 或测试用例 test 等方面发生的变化
  2. css 样式文件的修改一般属于 feat 或者 fix,并不是 style

范围/scope

scope 用来描述本次提交所涉及到的改动范围(例如模块、功能或其他任何限定的范围)。

scope 的具体取值视项目而定。以淘宝详情页为例,取值可以是:header, footer, favorite, sku, etc...

如果是 monorepo 的项目,scope 取值可以是 subpackage 的名称。例如 babel 项目中对某个 package 的修改:

chore(babel-helper-plugin-utils): add npmignore

描述/description

description 用来概括和描述本次提交的改动内容,需注意以下几点:

  1. 时态方面使用一般现在时,不要使用过去时。虽然查看 message 时,message 内容本身都发生在过去,然而对于主题来说,使用现在时的时态更简洁明确,并且更易达成一致性:

    # good
    docs: delete redundant docs
    
    # bad
    docs: deleted redundant docs

    StackOverflow: Should I use past or present tense in git commit messages

  2. 句式使用祈使句。即一般情况不要增加主语。因为在绝大情况下,主语都是作者『我』:

    # good
    docs: delete redundant docs
    
    # bad
    docs: i delete redundant docs
  3. 句首无需大写,句尾无需结束标点。因为主题(或标题)本身不用形成完整的句子:

    # good
    docs: delete redundant docs
    
    # bad
    docs: Delete redundant docs.

正文/body

日志的内容主体 body 用来描述详细的提交内容,可写可不写,需注意以下几点:

  1. 时态方面使用一般现在时,不要用过去时态。
  2. 句式视情况而定,一般使用祈使句式。
  3. 标点方面遵循一般的文档格式规约。

footer 通常用于代码评审过程记录、作者签名等。例如:

Reported-by: User1 <user1@example.com>
Helped-by: User2 <user2@example.com>
Reviewed-by: User3 <user3@example.com>
Signed-off-by: Author <author@example.com>

为什么要有签名区?

因为一个提交的元信息中只有作者(author)、提交者(committer)两个字段,而一段代码的诞生,参与的人往往不止于此,还可能有问题报告者(Reported-by)、代码评审者(Reviewed-by)、上游 Committer 的签名(Signed-off-by)。为此一些开源项目(如 Git、Linux)的一个约定俗成的习惯,是在提交的最后加上签名,每个贡献者一行,从上到下可以看到这段代码诞生的过程。

还可以添加其他元信息,例如:

  1. 引用 Issues

    可以在 commit 信息里使用关键字 + Issue ID(Gitlab / Github / Redmine 或其他平台的),来表明该提交解决了某个 Issue。推荐使用的关键字有:

    close
    closes
    closed
    fix
    fixes
    fixed
    resolve
    resolves
    resolved

    关键字的选用可以根据当前语义、关联的 Issue 是否在当前仓库下,甚至是 commit 消息的长度限制来决定。

    • close: 关闭当前仓库的 Issue
    • fix: 关闭当前或其他仓库的 Issue, 一般指 Bug 修复
    • resolve: 关闭当前或其他仓库的 Issue

    关闭多个 Issues 使用如下格式:

    Close #1, #2, #3
    Close #1, close #2, close #3
    
    Fix #1, #2, #3
    Fix #1, close #2, close #3
    
    Resolve #1, #2, #3
    Resolve #1, close #2, close #3
  2. 破坏性变动(Breaking changes)

    如果本次提交的改动是破坏性的,需要在这里声明:

    BREAKING CHANGE: 为了组件 API 规范的统一,本次升级将 size 属性的 value 值从 `s|m|l` 替换为 `small|medium|large`。
    
    请按照如下方式升级:
    
    <Button size="s">提交</Button>
    -->
    <Button size="small">提交</Button>
    
    继续使用 size="m" 可能会导致样式错误。

流水线/GitLab CI

node-ci-template 用于前端 Web 应用和 npm 包项目接入统一 GitLab CI。业务项目通过 include 引入模板后,可获得依赖安装、构建、单元测试、Sonar 扫描、Docker 镜像构建、npm 包发布、SBOM 上传和部署扩展能力。

模板支持 pnpmnpm,默认使用 pnpm

快速接入

业务项目 .gitlab-ci.yml

yaml
stages:
  - build
  - test
  - release
  - verify
  - deploy

include:
  - project: 'cq/dev/ci/node-ci-template'
    ref: main
    file:
      - '/.template/workflow.yml'
      - '/.template/anchor.yml'
      - '/.template/build.yml'
      - '/.template/test.yml'
      - '/.template/release.yml'
      - '/.template/verify.yml'
      - '/.template/deploy.yml'

variables:
  PRODUCT_NAME: 'your-product-name'
  DOCKER_HARBOR_PROJECT: 'cq-saas'
  PACKAGE_MANAGER: 'pnpm'
  NEXUS_UPLOAD_ENABLED: 'false'

如需项目自定义部署逻辑,可在业务仓库增加 .ci/deploy.yml 并额外 include:

yaml
- local: '/.ci/deploy.yml'
  rules:
    - exists:
        - '.ci/deploy.yml'

业务项目要求

  • package.json 中提供 build 脚本,或设置 BUILD_COMMAND
  • 如启用集成验证,提供 verify 脚本,或设置 VERIFY_COMMAND
  • 默认单元测试使用 Vitest,项目需安装 vitest@vitest/coverage-v8
  • 如启用 Docker 镜像构建,项目需提供一个或多个 Dockerfile
  • 如启用发布说明检查,创建发布 tag 前需维护 CHANGELOG.md 并包含该 tag。
  • 业务镜像的 FROM 应使用内部 Harbor 镜像,不应直接使用公网镜像仓库。

最低测试依赖示例:

json
{
  "devDependencies": {
    "vitest": "^2.0.0",
    "@vitest/coverage-v8": "^2.0.0"
  }
}

流水线触发

场景条件主要执行内容
MR 检查MR 目标分支为 dev*构建、单元测试、Sonar 扫描
开发分支构建push 到 dev*构建、测试、可选开发镜像、集成验证、可选部署
预发布分支构建push 到 release*构建、集成验证、可选部署
正式发布push V* / v* tag构建正式镜像、可选 npm 发布、上传 SBOM、可选上传附件和 CHANGELOG 检查

以下情况会被模板跳过:dev* -> release* MR、release* -> main MR、非 dev* / release* 分支 push、非 V / v 开头的 tag。

必填变量

变量说明
PRODUCT_NAME产品名,用于镜像仓库路径
DOCKER_HARBOR_PROJECTHarbor 项目名,通常为 cq-saas

常用可选变量

变量默认值说明
CI_NODE_IMAGE$CI_REGISTRY/cq-common/ci/node/node22-pnpm-kaniko:20260414P2Node CI 镜像
PACKAGE_MANAGERpnpm可选 pnpmnpm
PNPM_VERSION10.0.0使用 pnpm 时的版本
NPM_CONFIG_REGISTRYhttps://nexus.in.whatspos.cn/repository/pax-npm-group/npm/pnpm 依赖安装源
INSTALL_COMMAND自动覆盖依赖安装命令
BUILD_COMMAND自动覆盖构建命令
TEST_COMMAND自动覆盖单元测试命令;设置后不收集默认 JUnit/覆盖率报告
VERIFY_COMMAND自动覆盖集成验证命令
BUILD_WORKSPACE_CACHE_PATHS./[!.]*构建后传递给后续 job 的工作区路径或 glob
DOCKERFILE_SEARCH_ROOT.Dockerfile 搜索根目录;monorepo 推荐设置为 ./apps
DOCKER_IMAGE_NAME""镜像名基准值;空值时使用 CI_PROJECT_NAME
DOCKER_BUILD_ENABLEDtrue是否在发布流水线构建 Docker 镜像
DEPLOY_DEV_ENABLEDtrue是否构建开发镜像并启用开发部署
SONAR_SCAN_ENABLEDtrue是否运行 SonarQube 扫描
NEXUS_UPLOAD_ENABLEDfalsetag 发布时是否发布 npm 包到 Nexus
SBOM_UPLOAD_ENABLEDtruetag 发布时是否生成并上传 SBOM
SEAFILE_UPLOAD_ENABLEDfalsetag 发布时是否启用 Seafile 上传 job
INTEGRATION_TEST_ENABLEDtrue是否运行集成验证
CHANGELOG_CHECK_ENABLEDfalsetag 发布时是否检查 CHANGELOG.md
NEXUS_NPM_REGISTRY自动npm 发布仓库;未设置时使用 ${NEXUS_URL}/repository/npm-releases/
SONAR_SCAN_VERSION4.3.6Sonar 扫描器版本
CDXGEN_VERSION11.1.5SBOM 生成器版本

示例:

yaml
variables:
  PRODUCT_NAME: 'payment'
  DOCKER_HARBOR_PROJECT: 'cq-saas'
  PACKAGE_MANAGER: 'pnpm'
  DOCKER_IMAGE_NAME: 'payment-web'
  DOCKERFILE_SEARCH_ROOT: './apps'
  NEXUS_UPLOAD_ENABLED: 'false'

Docker 镜像命名

镜像路径:

text
harbor.in.whatspos.cn/{DOCKER_HARBOR_PROJECT}/{PRODUCT_NAME}/{MODULE_NAME}:{TAG}

MODULE_NAME 规则:

Dockerfile 位置模块名
根目录 Dockerfile${DOCKER_IMAGE_NAME:-CI_PROJECT_NAME}
子目录 Dockerfile${DOCKER_IMAGE_NAME:-CI_PROJECT_NAME}-目录名

tag 规则:

场景tag
开发镜像${CI_COMMIT_SHORT_SHA}-DEV
正式镜像CI_COMMIT_TAG

示例:GitLab 项目名 payment-webPRODUCT_NAME=payment

text
Dockerfile          -> harbor.in.whatspos.cn/cq-saas/payment/payment-web:V1.0.0
admin/Dockerfile    -> harbor.in.whatspos.cn/cq-saas/payment/payment-web-admin:V1.0.0

设置 DOCKER_IMAGE_NAME=payment-frontend 后:

text
Dockerfile          -> harbor.in.whatspos.cn/cq-saas/payment/payment-frontend:V1.0.0
admin/Dockerfile    -> harbor.in.whatspos.cn/cq-saas/payment/payment-frontend-admin:V1.0.0

Monorepo 建议

对于 pnpm workspace / turbo 这类多应用仓库:

yaml
variables:
  DOCKERFILE_SEARCH_ROOT: './apps'
  BUILD_WORKSPACE_CACHE_PATHS: 'apps/'

推荐将应用 Dockerfile 放在固定目录,例如:

text
apps/
  admin/Dockerfile
  portal/Dockerfile

生成镜像名示例:

text
payment-web-admin
payment-web-portal

npm 包发布

NEXUS_UPLOAD_ENABLED=true 时,tag 发布流水线会执行 npm 包发布。

  • SPA 或纯 Web 镜像项目通常设置 NEXUS_UPLOAD_ENABLED=false
  • npm 包项目需保证 package.jsonnameversion 正确。
  • 如需指定仓库,设置 NEXUS_NPM_REGISTRY

发布前检查

  • 正式发布请创建 V*v* 开头的 Git tag。
  • 如设置 CHANGELOG_CHECK_ENABLED=trueCHANGELOG.md 必须包含当前发布 tag。
  • 如项目不需要 Docker 镜像,设置 DOCKER_BUILD_ENABLED=false
  • 如项目不需要 npm 发布,设置 NEXUS_UPLOAD_ENABLED=false
  • 如项目不需要 SBOM 上传,设置 SBOM_UPLOAD_ENABLED=false

分支管理/Branch Management

1. 设计原则

  • 长期分支只保留 mainreleasedev
  • 日常开发统一走 feature-* -> dev -> release -> main
  • 版本以 Git Tag 为准,分支不承担长期版本编号语义
  • 开发阶段不打源码 Tag
  • 开发阶段镜像 Tag 使用 ${CI_COMMIT_SHORT_SHA}-DEV
  • 开发镜像仅在 DOCKER_BUILD_ENABLED=trueDEPLOY_DEV_ENABLED=true 时构建
  • 正式发布镜像 Tag 与源码 Tag 完全一致:V...[-SNAPSHOT]
  • 不设计长期并行 dev/V* / release/V*
  • 仅在少数特殊场景下允许短期 dev/V*,完成后必须合并回 dev 并删除

2. 分支清单

长期分支

分支说明直接 push
main公共基线与正式发布结果归档面,只接受来自 release 的 merge❌ 禁止
release发布稳定化分支,用于集成测试、回归、验收与发版确认❌ 禁止
dev唯一日常集成分支,接受来自 feature-* 的 merge❌ 禁止

短命分支

分支命名规范来源目标合并后删除
功能分支feature-{需求ID}devdev
BUG 修复分支feature-{BUG ID}devdev
临时版本分支dev/V{版本标识}dev 或约定起点dev

dev/V* 仅用于少数场景下的短期隔离开发,不属于长期分支体系。


3. 标准工作流(日常)

适用于:绝大多数日常开发场景。

text
feature-{需求ID} ──► dev ──► release ──► main
                    │         │
                    │         └── 打正式 Tag:V1.00.01-20260420-P1[-SNAPSHOT]
                    └── 生成开发镜像:{CI_COMMIT_SHORT_SHA}-DEV

流程步骤

开发阶段

  1. dev 切出 feature-{需求ID}
  2. 开发完成后发起 MR → dev,经 Code Review 合并
  3. dev 触发 dev_branch_build 流水线
  4. DOCKER_BUILD_ENABLED=trueDEPLOY_DEV_ENABLED=true 时,开发流水线生成开发镜像,镜像 Tag 使用 ${CI_COMMIT_SHORT_SHA}-DEV
  5. 开发环境基于该镜像完成验证

发布准备 6. 需要发版时,将 dev 合并到 release 7. release 触发 release_branch_build 流水线,执行测试、回归、验收等发布前确认8. release 分支本身不承担长期开发,只用于稳定化与发布确认

发布 9. 在 release 分支打正式 Tag,格式见 标签命名 10. Tag 触发 tag_release 流水线,构建并推送正式镜像到 Harbor 11. 正式镜像 Tag 与源码 Tag 完全一致,例如 V1.00.01-20260420-P1[-SNAPSHOT] 12. 部署验证通过后,将 release 合并到 main


4. 开发阶段镜像策略

开发阶段的目标是持续验证,不是形成正式版本,因此开发镜像与正式发布镜像必须分开管理。

规则

  • 开发阶段不打源码 Tag
  • 开发镜像仅用于开发环境验证
  • 开发镜像 Tag 使用 ${CI_COMMIT_SHORT_SHA}-DEV
  • 仅当 DOCKER_BUILD_ENABLED=trueDEPLOY_DEV_ENABLED=true 时构建开发镜像
  • -DEV 仅表示开发临时镜像,不代表正式版本
  • 正式发布阶段继续使用源码 Tag 作为正式镜像 Tag

示例

场景镜像 Tag 示例含义
开发阶段a1b2c3d4-DEVdev 分支某次提交生成的开发临时镜像
正式发布V1.00.01-20260420-P1正式发布镜像
预发布/试发V1.00.01-20260420-P1-SNAPSHOT非正式发布镜像

5. 短期 dev/V* 使用规则

dev/V* 只用于低频的特殊隔离场景,不作为长期并行版本线存在。

适用场景

  • 某次短期需求需要与日常 dev 隔离
  • 某个版本窗口内需要临时冻结一部分变更
  • 某次特殊修复不适合直接在 dev 上持续叠加

约束规则

  • dev/V* 仅在确有必要时创建
  • 不配套长期 release/V*
  • 不将 dev/V* 视为长期维护分支
  • 工作完成后必须合并回 dev
  • 合并完成后应删除该分支

示例流程

text
dev ──► dev/V1.00.00 ──► dev

若未来并行开发成为常态,应单独重设计分支模型,而不是在当前规范上不断叠加例外。


6. 与 CI 流水线的对应关系

操作触发分支 / TagCI 流水线类型执行内容
MR 到 devMR 事件mr_check编译 + 单元测试
push 到 devdevdev_branch_build编译 + 单元测试 + 按开关条件构建开发镜像
push 到 releasereleaserelease_branch_build编译 + 发布前验证
V* / v* TagTagtag_release构建正式镜像 + 推送 Harbor

7. 分支保护规则(GitLab 配置建议)

分支允许 push允许 merge需要 Code Review
mainMaintainer
releaseMaintainer
devDeveloper+
feature-*Developer+Developer+
dev/V*Developer+

标签命名/Tag Naming

Git Tag 通常用来标记发布的版本。

1. 正式版与非正式版

类型Git tag 要求说明
正式版不得包含子串 -SNAPSHOT对外可交付、可长期追溯的发布标识
非正式版(试发、重复构建、内部验证等)带后缀 -SNAPSHOT表示非最终发布,可与 Maven -SNAPSHOT 语义对齐(见第 5 节)

判定规则:tag 名称中是否出现 -SNAPSHOT(区分大小写按团队统一,建议与 Maven 一致为 大写 SNAPSHOT)。


2. 推荐 tag 形态

整体结构:

text
V{主版本}-{YYYYMMDD}-P{序号}[-SNAPSHOT]
必填含义示例
V前缀,与 CI workflowV* / v* 规则一致;若统一用大写 V,则全仓库一致V
{主版本}对外语义化版本,建议 主.次.修订 形式1.00.01
-分隔符
{YYYYMMDD}封版或打 tag 的日期(8 位)20260416
-分隔符
P{序号}同一天、同一主版本下的第几次修订;P 字面量 + 正整数,建议从 P1P1
-SNAPSHOT仅非正式版附加正式版省略

2.1 示例

场景合法示例
正式版V1.00.01-20260416-P1
非正式版V1.00.01-20260416-P1-SNAPSHOT
同一天第二次正式修订V1.00.01-20260416-P2

2.2 与 v* 小写前缀

若仓库同时允许 v1.00.01-20260416-P1,须在组织内约定 仅大写 V大小写二选一,并与 GitLab CI workflow.rules 中的正则 保持一致,避免 tag 能推但流水线不触发。


3. 递增与唯一性建议

  1. 主版本 {主版本}:随产品里程碑变更,由产品/研发在发布前确定。
  2. 日期 YYYYMMDD:取 打 tag 当日(或封版日),与发布说明、变更日志日期对齐。
  3. 同日修订 P{n}:同一 主版本 + 日期 下,正式版从 P1 递增;若先打 SNAPSHOT 再转正,建议 新打正式 tag(去掉 -SNAPSHOT 并视情况升 P),避免移动 tag。
  4. 唯一性:同一仓库内 勿重复使用 已推远程的正式版 tag;-SNAPSHOT 类 tag 若允许覆盖,须在团队内明确 禁止对正式 tag 重写

4. 与 CI / 镜像 tag 的关系

  • 当前模板约定:业务镜像仅在 tag_release(匹配 V*/v*)时构建并推送,镜像 :TAG = CI_COMMIT_TAG
  • 因此 Git tag 字符串即镜像 tag,命名时需考虑 Harbor 长度与字符限制(过长仅影响可读性,一般可接受)。
  • 开发阶段镜像使用 ${CI_COMMIT_SHORT_SHA}-DEV,仅作为开发环境临时镜像标识,不属于 Git Tag / 正式版本命名规则范畴

5. 与 Maven -SNAPSHOT 的边界(说明性)

  • Git tag 中的 -SNAPSHOT 仅表示「非正式 Git 发布线」,不会自动改变 Maven 的 pom.xml 版本或 Nexus 仓库类型。
  • 若希望 Git 命名与 Maven 仓库(releases / snapshots)严格一致,需在业务工程或发布脚本中 单独约定 pom 版本与 maven-deploy 目标仓库,本文不替代 Maven 版本策略文档。

参考资料

  1. Git Book
  2. Angular Commit Message Format
  3. Karma Git Commit Msg