本篇学习指南
读完你能
  • 看懂分支模型,知道 main / release / feature / hotfix / custom / dev 各自干嘛
  • 按规范给分支命名,能独立从正确的基点拉出新分支
  • 理解 tag 为什么不可移动、为什么分支一律从 release 分支头拉
阅读时长
约 12 分钟 · 纯阅读,无需动手
前置知识
会用基本 git 命令;没接触过 Git 先看下方「概念入门」
接着读
概念入门
如果你是第一次用 Git
分支 branch
代码的「平行宇宙」。主线(main)是正式版本,开发者从主线拉出一个分支做修改,不影响主线代码,做完再合并回去。
标签 tag
给某个时刻的代码打一个不可移动的标记,像书签。分支会随提交移动,tag 不会。我们用 tag 标记每个发布版本。
合并 merge
把一个分支的改动「融合」到另一个分支。
GitFlow
一种流行的分支管理模型,定义了不同分支的角色和协作方式。我们的分支模型参考了 GitFlow,但做了简化(不设 develop 分支)。
分支就像从主路岔出去一条小路,走完再汇回来
分支(branch)
main 主线
feature/xxx
做完改动后合并回主线
tag 标记
v2.0
v2.1
v2.2
…
tag 不会移动,像书签一样固定在某次提交上
1
文档说明

本文档定义公司全部产品研发的分支模型、命名规范、生命周期管理与 Tag 规范,适用于所有前后端开发人员。

  • 与现有产物的关系(以下路径取自 EasyPro 仓库,其他产品参照同理):

- data/sql/:初始建库脚本(多数为空占位文件),已迁移为 sql/release/2.1.0/ 全量建库。

- doc/驱动设计文档/modbus...升级SQL.sql:已实践的「游标遍历 + 幂等保护」增量脚本范式,本规范将其作为 SQL 增量脚本标准模板(现已汇总为 sql/release/2.1.1/3_modbus.sql)。

- doc/智问/、doc/驱动设计文档/:散落的 SQL 脚本,已按分支镜像规则归入 sql/ 对应目录(详见 06·SQL 版本化)。

本文档不含强制自动化(如 Git hook、CI 校验),属人工约定。涉及破坏性操作(分支重命名、SQL 迁移、文件清理)的清单见各章末,由负责人确认后另行执行。

2
分支模型与命名规范
2.1 分支模型(保留版本分支模式)

本项目采用发布版本化部署(每个版本打成集成安装包,本地化部署到项目现场),因此沿用「长期版本分支」模式,而非纯 GitFlow 或 GitHub Flow:

多版本并行时的分支关系(以 EasyPro v2.1.x 为例)
release/2.1.0
v2.1.0
已部署到现场
├─
feature/2.1.0/bacnet-cov
开发完成后合回 release/2.1.0
├─
hotfix/2.1.0/cov-fix
→
① 合回 release/2.1.0(当前版本现场拿到修复)
② 合回 release/2.1.1(新版本也包含修复)
├─
custom/2.1.0/huzhou-energy
禁止合回 release,单独打安装包交付
└─
line/eos/2.1.0
子产品产品线,独立演进、默认不回主版本(详见产品线分支节)
├─
feature/eos/2.1.0/择机算法
EOS 专属功能 → 回 line/eos/2.1.0
└─
hotfix/eos/2.1.0/电价bug
→ 回 line/eos/2.1.0 + line/eos/2.1.1
release/2.1.1
开发中,承接 hotfix 回流
└─
feature/2.1.1/zw
智问功能开发
release/2.1.0 → tag v2.1.0 → 现场部署
├─feature/2.1.0/*功能开发 → 回 release/2.1.0
├─hotfix/2.1.0/*现场修复 → 回 release/2.1.0 + release/2.1.1
├─custom/2.1.0/*客户定制 → 禁止回 release
└─line/eos/2.1.0子产品产品线 → 下行同步、上行默认禁止(详见产品线分支)
release/2.1.1 — 下个开发中版本,承接 hotfix 回流 release/2.2.0 — 更远的规划版本

多版本并行时,版本号前缀能一眼看出分支基于哪个版本、回流到哪个 release,避免跨版本误操作。

子产品(节能 EOS、轻量版、碳排等)走独立的 line/<产品>/<版本> 产品线,自带 feature/hotfix,长期独立演进、不合并回主版本(详见下方产品线分支节)。

与 GitFlow 的取舍:

  • 保留 GitFlow 的「长期 release 分支 + 现场修复回流」思想,契合现场版本化部署。
  • 不设独立 develop 分支(项目规模与团队节奏不需要集成分支,release 分支直接承担集成职责)。
2.2 分支命名规范(前后端统一)

通用格式:<前缀>/<版本号>/<语义>,其中版本号为语义化版本点分(如 2.1.1,不带 v 前缀)。

分支名拆解示例
feature
类型前缀
/
2.1.1
版本号
/
bacnet-cov
语义描述
正确示例
release/2.1.1 — 版本主线
feature/2.1.1/bacnet-cov — BACnet COV 功能
hotfix/2.1.0/knx隧道假死优化 — 语义段中文短语,同样合规
custom/2.1.0/huzhou-energy — 湖州定制
错误示范
2.1.1-bacnet — 缺 feature/ 前缀
feature/bacnet-cov — 缺版本号
feature/2.1.1_智问 — 语义段禁用下划线(中文本身可以)
dev-huzhou — 定制应该用 custom/
类型前缀示例合并去向
版本主线release/release/2.1.1— (作为发布基线,长期保留)
功能开发feature/feature/2.1.1/bacnet-cov回所属 release/2.1.1
现场修复hotfix/hotfix/2.1.0/cov-fix回 release/2.1.0 + 下个开发中 release/2.1.1
客户定制custom/custom/2.1.0/huzhou-energy 禁止合入任何 release
产品线(子产品)line/line/eos/2.1.0长期保留;下行同步、上行默认禁止(详见产品线分支)
个人临时dev/dev/2.1.1/zhangsan-simulate用完即删,不长期保留

版本号单独成段、置于语义之前,用 / 分隔。这样按分支名排序时,同一前缀下同版本的分支自然聚拢。

规则细则:

  • feature/ / hotfix/ / custom/ / dev/ 必须带版本号,标明基于哪个版本(多版本并行下避免归属不明)。
  • feature 只面向未发布版本:已打 tag 的版本禁止新开 feature/ 分支;已发布版本的演进只有两条路:不改库的 hotfix(03·规则三)或 custom 定制(改库需求的唯一出口)。
  • 语义部分中文或英文均可:中文用简明短语(如 knx隧道假死优化),英文用小写 + 连字符(如 bacnet-cov);禁止空格与特殊符号;应体现「功能/问题」,而非人名。
  • dev/ 版本号可省略(个人临时分支、基点随意),但带版本号便于归类时仍推荐。
  • 前端工程同样遵守本表;如需区分前后端,可在语义部分加 -fe 后缀(如 feature/2.1.1/optiview-sso-fe)。
  • 子产品(产品线)用四段式:<前缀>/<产品代号>/<版本号>/<语义>(如 feature/eos/2.1.0/择机算法),产品代号占第二段、版本号降至第三段;主产品视为默认产品线,省略产品段、仍用三段式(详见产品线分支节)。
  • 避免与版本主线节点同名:版本号作为独立 / 段时,不能与同前缀下的 release 节点同名(即 release/2.1.1 与 release/2.1.1/xxx 不能并存)。由于版本主线用 release/2.1.1、其余前缀各异,实际不冲突。
规范命名
  • feature/2.1.1/bacnet-cov — 功能+版本+语义清晰
  • hotfix/2.1.0/knx隧道假死优化 — 中文语义段简明确切
  • custom/2.1.0/huzhou-energy — 客户+功能明确
  • dev/2.1.1/zhangsan-test — 个人实验有归属
共同点:前缀/版本/语义三段齐全,语义用中文短语或英文连字符
不规范命名
  • feature/bacnet — 缺版本号,不知道基于哪个版本
  • hotfix/v2.1.0/fix — 版本号不该带 v
  • feature/2.1.1_智问 — 下划线禁用(中文可以)
  • dev-zhangsan — 缺前缀斜杠,看着像定制
每条都违反了某项细则,提交评审会直接打回
2.3 分支生命周期

创建基点规则:

分支类型从哪里拉命令
feature/当前开发中(未打 tag)的 release/x.x.x;已发布版本禁止git checkout -b feature/2.1.1/xxx release/2.1.1
hotfix/对应版本的 release 分支头git checkout -b hotfix/2.1.0/xxx release/2.1.0
custom/对应版本的 release 分支头,创建后保持下行同步git checkout -b custom/2.1.0/huzhou-xxx release/2.1.0
dev/任意基点(个人临时)git checkout -b dev/2.1.1/zhangsan-xxx
line/对应主产品版本的 release 分支头(分叉基线),之后保持下行同步git checkout -b line/eos/2.1.0-1.0.0 release/2.1.0
feature/<产品>/(产品线内)该产品线主线 line/<产品>/<版本>git checkout -b feature/eos/2.1.0/xxx line/eos/2.1.0
hotfix/<产品>/(产品线内)该产品线主线头 line/<产品>/<版本>git checkout -b hotfix/eos/2.1.0/xxx line/eos/2.1.0

为什么一律从 release 分支头拉,不从 tag 拉?回流门禁(03·规则二)保证 release 分支头始终对应「现场已验证状态」,即 tag + 历次已验证修复,只接收通过验证的回流。这样从 release 头拉分支,现场验证的产物(release 头 + 修复)与回流后的 release 内容完全一致,验证即对回流生效;若从 tag 拉,验证的只是「tag + 修复」,回流到已前进的 release 后该组合从未被验证。custom/ 与 line/ 同理:release 头比冻结的 tag 更完整,下行同步一次即把全部通用修复带给客户/子产品。若分叉时必须与某个精确发布包对齐,先 git reset 到对应 tag 再拉。

分支的一生:从哪里来 → 做什么 → 到哪里去
feature
从 release 拉出
开发新功能
合回 release
删除
git checkout -b feature/2.1.1/bacnet release/2.1.1 · 期限 ≤2 周
hotfix
从 release 头 拉出
修复 bug(禁改库)
现场验证 → 双向回流
删除
git checkout -b hotfix/2.1.0/fix release/2.1.0 · 只改代码不改库
custom
从 release 头 拉出
服务客户现场
定期下行同步
不回 release
git checkout -b custom/2.1.0/huzhou-energy release/2.1.0 · 单独打安装包交付
dev
任意基点
临时实验
删除
git checkout -b dev/2.1.1/zhangsan-test · 期限 ≤3 天,用完即删

关键:feature / hotfix / custom / line 一律从 release 分支头拉取,不从 tag 拉。前提是回流门禁保证 release 头 =「tag + 历次已验证修复」,基线可信,且比冻结的 tag 更完整;分叉必须与某个精确发布包对齐时,先 git reset 到对应 tag 再拉。

使用期限:

  • feature/ ≤ 2 周,超期需拆分。
  • dev/ ≤ 3 天,仅作临时实验。
  • hotfix/ 现场验证通过、双向回流完成后即删除,不长期保留。
  • custom/ 可长期存在(持续服务某客户),但需在 doc/ 备案其定制内容。
  • line/ 主线及产品线内分支随子产品生命周期长期存在;新建产品线须先在 doc/规则/产品线登记.md 登记(详见产品线登记)。

为什么 feature 要限时 2 周、dev 限时 3 天?分支活得越久,与 release 头的偏差就越大,合回时冲突面指数上涨,现场验证的范围也跟着膨胀,最后变成「不敢合、合不动」。期限是在逼着把大功能拆成可验证的小步,每一步独立走「开发 → 现场验证 → 回流」。dev 分支同理:实验超过 3 天还没转正,要么转 feature 走正式流程,要么删掉。

清理流程:

BASH
# 1. 删除本地已合并分支(保留 main 与所有 release/)
git branch --merged | grep -vE '^\*|main|release/' | xargs -n1 git branch -d

# 2. 清理远程已删除分支的本地引用
git fetch -p

# 3. 删除远程已合并分支(需人工确认)
git push origin --delete <branch-name>

# 建议每月执行一次清理,负责人统一处理

为什么每月都要清理分支?这是踩过的坑:仓库曾积累 90+ 个分支,大量已合并的 dev-人名 分支长期残留,命名空间被污染,定制分支混在个人分支里差点被误合并进新版本(见 历史问题与迁移任务)。已合并的分支留着没有任何价值,只会让下一个人拉错基线。

2.4 Tag 规范
  • 格式:统一 vX.Y.Z(语义化版本 SemVer);维护性微版本用四段 vX.Y.Z.N(如 v2.1.0.1,见 03·规则五)。tag 段数即类型:三段 = 正式版,四段 = 微版本。
  • 版本号的含义与变化规则
    v
    前缀
    2
    MAJOR
    不兼容大改
    .
    1
    MINOR
    新功能
    .
    0
    PATCH
    补丁修复
    版本号变化示例
    v2.1.0
    v2.1.1
    PATCH:修了个 bug
    v2.1.1
    v2.2.0
    MINOR:新增了功能
    v2.2.0
    v3.0.0
    MAJOR:架构大改,不兼容旧版
    tag 号 = 安装包版本号 · 版本发布当天打 tag · 必须用 annotated tag

- MAJOR:不兼容的大版本(2.x → 3.x)

- MINOR:新功能版本(2.1.x → 2.2.0)

- PATCH:补丁版本(2.1.0 → 2.1.1)

  • 打 tag 时机:版本发布的当天,tag 号与安装包版本号完全一致。
  • 必须用 annotated tag(带说明信息):
BASH
git tag -a v2.1.0 -m "Release v2.1.0

功能:
- 新增 BACnet IP 驱动 COV 订阅
- 智问大模型配置

修复:
- Modbus 写功能码切换
- COV 订阅判断逻辑"
  • 废弃混用:当前存在的 2.0.0(无 v)、v2.0.0-pre、ems-lite_V1.0.0 应统一为 vX.Y.Z 格式;pre-release 用 vX.Y.Z-rc.N(如 v2.1.0-rc.1)。
  • 子产品 tag 格式:<产品代号>-vX.Y.Z(如 EOS 用 eos-v2.1.0),主产品 tag 格式 vX.Y.Z 不变。注意子产品 hotfix/<产品>/、custom/<产品>/ 的拉取基点是其产品线主线头(line/<产品>/<版本>),tag 用于标记发布版本(详见产品线命名格式)。
3
产品线分支(line/,子产品独立演进)
3.1 为什么需要产品线分支

除主产品外,公司还有若干独立子产品:节能 EOS、轻量版(ems-lite)、碳排(carbon)等。它们有三个共同点:

  • 基于主产品某个版本分叉而来,但长期独立演进、不合并回主版本;
  • 有自己的多版本节奏(基于 2.1.0 → 演进到自己的 2.1.1 → ……);
  • 内部同样需要 feature/、hotfix/ 这套二级分支。

现有的 custom/(见 03-合并回流与定制隔离)是为单客户交付设计的,承载不了子产品的二级结构与多版本演进;而让它们裸名(reduceEnergy、ems-lite、carbon)与版本分支同处一层,又会污染命名空间。为此引入产品线分支 line/。

3.2 命名格式:统一「插段」

核心思路是在主产品三段式的第二段插入产品代号,前缀不变,这样前缀仍表达「做什么」,第二段表达「给哪个产品」,两个维度正交。

第二段插产品代号:三段 → 四段
主产品(三段)
feature
/
2.1.0
/
bacnet
前缀 / 版本 / 语义
产品线(四段)
feature
/
eos
/
2.1.0
/
择机算法
前缀 / 产品 / 版本 / 语义

不把子支线挂到 line/ 下(如 line/eos/feature/...),而是让前缀即类型——与主产品体系统一、GitLab 按类型折叠、Hook 与服务端保护规则可按 feature/* 一套复用。

前缀主产品(三段)产品线(四段)产品线示例从哪拉回流去向
主线release/2.1.0line/eos/2.1.0line/eos/2.1.0主产品 release/2.1.0 头发布基线,长期保留
功能feature/2.1.0/xfeature/eos/2.1.0/xfeature/eos/2.1.0/择机算法line/eos/2.1.0回 line/eos/2.1.0
修复hotfix/2.1.0/xhotfix/eos/2.1.0/xhotfix/eos/2.1.0/电价bugline/eos/2.1.0 头回 line/eos/2.1.0 + line/eos/2.1.1
定制custom/2.1.0/客户-xcustom/eos/2.1.0/客户-xcustom/eos/2.1.0/huzhou-报表line/eos/2.1.0 头,保持下行同步 禁回 line/eos,也禁回主产品
临时dev/2.1.1/xdev/eos/2.1.1/xdev/eos/2.1.1/zhangsan-x任意用完即删
  • 产品代号英文小写(eos / lite / carbon),全局唯一,在 doc/规则/产品线登记.md 备案后使用。
  • 语义段复用 §2.2 规则;产品代号不计入语义段。
  • 产品线主线 line/<产品>/<版本> 长期保留,地位等同 release/<版本>。
  • 子产品版本号二选一,登记时定死(一条产品线只允许一种):独立编号(推荐,子产品有自身发布节奏时)主线用双版本段 line/<产品>/<基线版本>-<自身版本>(如 line/eos/2.1.0-1.0.0,读作「基于 2.1.0 的 EOS 1.0.0」,基线在前、创建后不变);同号(完全跟随主产品节奏时)基线=自身,单段即可,如 line/eos/2.1.0。
  • 子支线与交付物只带自身版本(基线由所属主线决定):feature/eos/1.1.0/<语义>、sql/line/eos/1.0.0/、sql/tag/eos/1.0.0/;tag 用自身版本(如 eos-v1.0.0),annotated 说明中记录对应基线。
  • 大版本换基 = 新主线分支:下个自身大版本若换到新主产品基线(如 2.1.1),新建 line/eos/2.1.1-2.0.0(旧主线保留),登记表更新并在备注追加换基记录;日常下行同步是 merge,不改基线段。
  • 维护性微版本 tag 一律四段:子产品 eos-v1.0.0.1、主产品 v2.1.0.1——三段 = 正式版,四段 = 微版本,看段数即知类型;子产品正式小版本仍用修订位递增(1.0.0 → 1.0.1,三段 tag)。
  • Tag 格式 <产品>-vX.Y.Z(如 eos-v2.1.0),主产品 tag 不变。

hotfix/eos、custom/eos 的基点是 EOS 自己的产品线主线头(line/eos/2.1.0),不是主产品 release/2.1.0——确保基线与 EOS 实际演进状态一致,并随下行同步获得最新修复。

3.3 回流规则(产品线分支的灵魂)
主产品 ↔ 子产品的回流方向
release/2.1.0
主产品
① 下行同步(允许)
↓
↑
② 上行回流(默认禁止)
line/eos/2.1.0
子产品
  • ① 下行同步(主线→子产品):允许、选择性。子产品负责人按需 git merge 或 rebase 主产品通用更新。
  • ② 上行回流(子产品→主线):默认禁止(与 custom/ 同思想)。
  • ③ 例外:通用修复回流。子产品上修了主产品也存在的通用 bug,负责人判断后 cherry-pick 单个提交回主产品 release/——人工判断、逐提交、严禁整分支合并。
  • ④ 子产品内部双向回流。hotfix/eos/2.1.0/xxx 须回流到 line/eos/2.1.0 + 下个 line/eos/2.1.1,规则同主产品 hotfix,只是 release/ 换成 line/eos/。
  • ⑤ 子产品内的客户定制(custom/eos/...)双重不回流。既禁回 line/eos/,也禁回主产品 release/;交付方式为从该 custom/eos/ 单独打安装包。
BASH
# ① 下行同步:把主产品通用更新搬进子产品
git checkout line/eos/2.1.0
git merge release/2.1.0          # 或按需 cherry-pick 特定提交

# ③ 例外:子产品上的通用 bug 回流主产品(逐提交,人工确认)
git checkout release/2.1.1
git cherry-pick <eos-通用修复-commit>

# ④ 子产品 hotfix 双向回流
git checkout line/eos/2.1.0 && git merge hotfix/eos/2.1.0/xxx
git checkout line/eos/2.1.1 && git merge hotfix/eos/2.1.0/xxx
git branch -d hotfix/eos/2.1.0/xxx
3.4 line/(产品线)vs custom/(客户定制)
维度custom/ 客户定制line/ 产品线
分叉原因某客户的定制需求一个独立子产品
二级结构无(单线)有(内部 feature/hotfix)
版本节奏跟随主产品基线独立多版本演进
回主产品 禁止 默认禁止(通用 bug 可逐提交 cherry-pick)
从主产品取更新单向 cherry-pick下行同步 merge/rebase
生命周期服务某客户,可长期子产品存活期,长期
3.5 产品线登记

每个子产品在 doc/规则/产品线登记.md 备案:产品代号、中文名、基线主产品版本、维护人、当前版本、产品线主线分支、交付现场、文档链接。

无登记不开产品线分支——避免产品代号泛滥、归属与维护人不明确。

新手常见错误
这些是真实踩过的坑,点开看「为什么错 + 怎么改」
我的分支名写了 v 前缀:hotfix/v2.1.0/fix-login,被说不对?
版本号不带 v 前缀——v 只用在 tag 上(v2.1.0)。分支里写 hotfix/2.1.0/fix-login 即可。这样分支和 tag 一眼能区分:带 v 的是 tag,不带 v 的是分支。
规范让我从 release/2.1.0 分支头拉 hotfix,可分支头上不是可能有未发布代码吗?
规范早期版本确实规定从 tag 拉,现已修订为一律从 release 分支头拉。它能成立的前提是回流门禁(03·规则二):未经现场验证的分支禁止回流,所以 release 头只包含「tag + 已验证修复」,和现场实际运行状态一致且更完整(带上了历次修复)——从 tag 拉反而会丢掉这些修复。若你要复现某个精确发布包的问题,先 git reset 到对应 tag 再拉分支即可。
我把客户定制的代码合进了 release,想着大家一起用,结果被批评了?
custom/ 分支严禁合入任何 release。因为定制代码是给某一个客户的,一旦进了 release,所有客户升级时都会拿到——可能引发意料外的问题。定制只在该 custom/ 分支单独打安装包交付给那个客户。
我不知道该用 feature/ 还是 dev/,感觉都是我自己开发?
看用途:feature/ 是正式的功能开发(要合回 release、会被评审,期限 ≤2 周);dev/ 是个人临时实验(用完即删、期限 ≤3 天,不进正式流程)。如果你只是想试试某个想法,用 dev/;如果是要交付的功能,用 feature/。
分支名里我用了中文或下划线,会有什么后果?
中文本身没问题(语义段允许中文简明短语,如 hotfix/2.1.0/knx隧道假死优化;英文则用小写 + 连字符)。真正禁止的是空格与特殊符号,以及用下划线做分隔(fix_login 应写 fix-login)——它们在脚本校验、URL 转义、跨平台时都可能出问题。语义段应体现「功能/问题」而非人名。
我把 EOS 的功能分支挂到了 line 下面,写成 line/eos/feature/择机算法,感觉层级很清晰,为什么说不规范?
产品线分支不「嵌套」,而是插段——前缀仍然是类型,第二段插产品代号。应写成 feature/eos/2.1.0/择机算法(前缀 / 产品 / 版本 / 语义)。这样和主产品的 feature/2.1.0/xxx 体系统一:前缀即类型,GitLab 能按 feature/* 一套规则折叠与保护。挂成 line/eos/feature/... 会让前缀含义混乱、按类型聚合失效。
我在 EOS 上修了个 bug,直接从主产品的 v2.1.0 拉了 hotfix/eos/2.1.0/电价bug,结果基线对不上?
产品线的 hotfix 要从EOS 自己的产品线主线头(line/eos/2.1.0)拉,不是主产品 release/2.1.0,更不是主产品 tag。主产品分支里没有 EOS 分叉后的演进代码——从主产品拉,基线既不是 EOS 现场版本、又缺了 EOS 的功能,要修的 bug 也无从附着。主产品自己的分支一律从 release 头拉,产品线内部对应的就是 line/<产品>/<版本> 主线头。
我在 EOS 上修了个主产品也有的通用 bug,顺手把 line/eos 整支 merge 回了 release/2.1.1,结果被要求回退?
产品线 → 主产品默认禁止整支合并,只能逐提交 cherry-pick。因为 line/eos 上混着 EOS 专属功能(择机算法等)和通用修复,整支 merge 会把 EOS 专属代码也灌进主产品、污染所有客户的版本。正确做法:人工挑出那几个通用修复的 commit,git cherry-pick <commit> 逐个搬到 release/;判断标准是这个 bug 主产品是否也存在。EOS 专属的东西永远不回主产品。