Files
HomeAgent/docs/git-branching.md
JianFeeeee e2d04a31a7 docs(git): 发布分支改为一个中版本一条,补三级发布通道规范
## 一个中版本一条发布分支

原规范写 release/vX.Y.Z(含 patch 位),实践中 1.0.0 与 1.0.1 各建了一条
分支,导致同一条 1.0.x 发布线被切成互不相连的碎片——追溯时无法用一条
分支看完整条线的演进。改为 release/vX.Y.x,patch 位用 x 占位,
承载该中版本全部 patch 直到下一条中版本分支切出。

## 三级发布通道(alpha / beta / 正式)

通道由 tag 区分而非分支:三者共用同一条 release/vX.Y.x。

  alpha  vX.Y.Z-alpha.N  功能齐了未充分验证    仅内部自测
  beta   vX.Y.Z-beta.N   alpha 问题已修        小范围试用
  正式   vX.Y.Z          通过验证可上现网      所有用户

这是 semver 标准预发布语义(1.1.0-alpha.1 < 1.1.0-beta.1 < 1.1.0),
版本比较逻辑天然认得,无需额外约定。允许跳级但要在发布说明写明理由;
alpha/beta 产物不上现网——预发布通道的存在就是为了不拿 24/7 服务冒险。

## 回流仍是 cherry-pick

明确不改 merge:merge 会把已发布的版本号带进 main,与「main 的
meta.Version 始终是下一个未发布版本」直接矛盾。

新补一条:修复落地当天要 pick 到所有活跃 feature 分支,否则它们合回
main 时可能带回旧代码(2026-09-04 的 stage 双重解锁修复即同时 pick 到
main 与 feature/memory-media)。

## 运维纪律沉淀

把今天走通的部署流程写进第四节,其中两条是踩过的坑:
  - 备份配置库用 sqlite3 .backup 而非 cp(WAL 模式下 cp 可能拿到
    不一致快照)
  - install -m 0755 替换而非 cp(原子 rename,不写坏运行中的进程镜像)
健康检查列出六项,含「一次真实对话」与「fatal error 计数为 0」。

## 当前分支对齐

第三节更新为 2026-09-04 的实际状态,并记录 1.0.x 的 tag 历史表
(含 v1.0.2 未使用的原因、v1.0.3 直接跳正式 tag 的理由)。
按 patch 号命名的历史发布分支标注为应当删除的遗留形态。
2026-09-04 20:11:19 +08:00

12 KiB
Raw Blame History

Git 分支管理规范

生效2026-08-312026-09-04 修订(三级发布通道 + 单条发布分支)。 适用:本仓TrueAgent/HomeAgent与 third_party/homeagent-sdkSDK 仓)——两仓协作时分支策略必须一致,本规范两仓同用。 核心原则一句话:main 唯一长命、永远可部署一切新工作在特性分支一个中版本一条发布分支alpha/beta/正式由 tag 区分hotfix 只进发布分支并 cherry-pick 回 main。


一、分支类型总览

分支 生命周期 来源 去向 部署性
main 唯一长命分支 永远可部署
feature/xxx 短命(本次特性完成即删) main 合回 main 不部署
release/vX.Y.x 中命(整个中版本生命周期 main 打 tag → 构建发布 发布产物来源
hotfix直接提交发布分支 随发布分支 发布分支 cherry-pick 回 main
main ──────────────── E ──────────────── G ────────────────(永远可部署)
      │                                    ▲
      │ feature/xxx                         │ cherry-pick修复逐个 pick 回)
      ├── A ── B ──(合回)───────────────────┤
      │                                    │
      └── release/v1.0.x ────────────────────────────────────────────────
          │                                │              │
          ├─(tag v1.0.0-alpha.1) 内部验证   │              │
          ├─(tag v1.0.0-beta.1)  小范围试用 │              │
          ├─(tag v1.0.0)         正式发布   │              │
          ├─(hotfix) F ─────────────────────┤              │
          ├─(tag v1.0.1)         patch 发布 │              │
          ├─(hotfix) H ────────────────────────────────────┤
          └─(tag v1.0.3)         patch 发布

二、分支职责

1. main(唯一长命分支)

  • 唯一长期存在且永远可部署。任何时刻 git checkout main 出来都是可构建、可上线的状态。
  • 积攒下一个中版本的功能feature 分支完成即合回main 持续向前。
  • main 上不直接开发。所有改动经 feature 分支合入hotfix 经 cherry-pick 注入。
  • main 的 internal/meta.Version 始终是下一个未发布版本,不随 patch 发布变动。
  • 合入门禁(单人直推也遵守,不强制 PR 但强制验证):
    • make test 全绿
    • 涉及插件/工具链时:接口冻结检查 git diff third_party/homeagent-sdk/sdk/ 为空
    • go vet ./... 无新增告警

2. feature/xxx(新特性/修复)

  • 命名:feature/<短横线描述>,如 feature/plugin-proc-migrationfeature/memory-media
  • 从 main 开出git checkout -b feature/xxx main
  • 完成后合回 main
    • 单人:直推(git merge --no-ff 保留特性边界,或 squash 成一个 commit二选一在团队内固定
    • 多人:走 PRreview 后合入)。
  • 合回后删除 feature 分支(避免累积)。

3. release/vX.Y.x(发布分支:一个中版本一条)

  • 命名用 x 占位 patch 位release/v1.0.x 承载 1.0.0 → 1.0.1 → … → 1.0.N 全部发布, 直到 release/v1.1.x 切出为止。不要按 patch 号建分支release/v1.0.1release/v1.0.3 各一条会把 同一发布线切成互不相连的碎片,追溯时无法用一条分支看完整条线的演进)。
  • 从 main 的某个可部署点切出git checkout -b release/v1.0.x main
  • 切出后冻结功能——发布分支上只做:版本号 bump、发布准备、bug 修复、文档。
  • 现网部署永远用发布分支上 tag 的构建产物,不是 main 头部、更不是 feature。

4. 三级发布通道alpha / beta / 正式)

通道由 tag 区分,不由分支区分——三者共用同一条 release/vX.Y.x

通道 tag 形式 含义 受众
alpha vX.Y.Z-alpha.N 功能齐了但未充分验证,可能有已知缺陷 仅内部/开发者自测
beta vX.Y.Z-beta.N alpha 问题已修,等待真实环境暴露长尾问题 小范围试用、愿意承担风险的用户
正式 vX.Y.Z 通过验证,可上现网 所有用户
  • 推进顺序alpha → beta → 正式,逐级向前,每级都是同一条分支上的新 tag。 这也是 semver 的标准预发布语义(1.1.0-alpha.1 < 1.1.0-beta.1 < 1.1.0 包管理器与版本比较逻辑天然认得,无需额外约定。
  • 允许跳级:若改动小、验证充分(如仅一处已定位并有回归测试覆盖的内核修复), 可直接打正式 tag。跳级要在发布说明里写明理由。
  • alpha/beta 的构建产物可以上传 release 附件,但必须在 gitcode release 上勾选 "预发布"标记,且发布说明首行标注通道与已知风险。
  • beta 未清零的严重问题不得进正式:正式 tag 意味着"我们认为它能上 24/7 现网"。

5. hotfix发布后发现的严重 bug

  • 场景:版本已发布后,发现只存在于该版本(或该发布线)的严重 bug。

  • 动作:直接把修复提交到发布分支 → 该分支重新构建、打下一个 patch tagv1.0.4)发布。

  • 关键hotfix 必须 cherry-pick 回 main

    # 在发布分支上提交修复(代码部分与版本号 bump 分开提交)
    git checkout release/v1.0.x
    git commit -m "fix(x): ..."                    # ① 修复本身
    git commit -m "chore(release): bump v1.0.4"    # ② 版本号(此 commit 不 pick 回 main
    git tag -a v1.0.4 -m "..."
    
    # 回到 main只挑修复本身
    git checkout main
    git cherry-pick <修复①的sha>                   # 只 pick ①,不 pick ②
    

    为什么 cherry-pick 而不是 merge发布分支只承载该版本特有的补丁merge 会把 版本号/发布相关改动一并带进 main 造成冲突,并让 main 的 meta.Version 变成 已发布的旧版本号。逐个 cherry-pick 让 main 精确地只获得修复本身。 版本号 bump 不要 pick 回 main。

  • 同时存在多个活跃 feature 分支时:修复也要 pick 到那些分支,否则它们合回 main 时 可能带回旧代码。实践做法是修复落地当天就 pick 到全部活跃分支 (如 2026-09-04 的 stage 双重解锁修复同时 pick 到 mainfeature/memory-media)。

  • hotfix 已逐个 pick 回 main ⇒ main 已含全部修复 ⇒ 无需再合并发布分支回 main。 这是本规范刻意为之——除非发布分支上有 main 想要的功能级改动(罕见), 否则发布分支永不 merge 回 main。

6. 发布分支退役

  • 下个中版本发布 = 上一条发布分支生命周期结束release/v1.1.x 出现即 release/v1.0.x 退役)。
  • 退役后可删可留:
    • 删除保持仓库干净tag 已保留全部历史,删分支不丢东西)。
    • 保留:便于追溯该发布线的历史构建(对 24/7 现网友好)。
  • 按 patch 号命名的历史发布分支应当合并/删除:它们是本规范修订前的遗留形态, 内容已被对应的 release/vX.Y.x 完全包含,保留只会让"哪条才是这条线"变得含糊。

三、当前分支对齐2026-09-04 执行)

主仓TrueAgent

分支 状态 处理
main 含全部 hotfix逐个 cherry-pickmeta.Version = 下一个未发布版本 保持
release/v1.0.x 承载 v1.0.0 / v1.0.1 / v1.0.3 全部 tag release/v1.0.1 重命名而来2026-09-04
release/v1.0.0 9b92a04,已被 1.0.x 线完全包含(merge-base --is-ancestor 验证通过) 🗑️ 已删除(本地 + 远端tag v1.0.0 保留全部历史
release/v1.0.1 旧 patch 号命名 🗑️ 已重命名为 release/v1.0.x(远端旧名删除)
feature/memory-media 记忆系统媒体(多模态)支持,进行中 完成后合回 main 并删除
feature/plugin-proc-migration 已合入 main525aa1f 待删(规范要求合回后删除)

1.0.x 发布线 tag 历史

tag 提交 通道 说明
v1.0.0 9b92a04 正式 外部插件从 C ABI 迁移到子进程 + 共享内存
v1.0.1 e671a8c 正式 多模态 bugfix假成功、能力声明与回退链、see_video 帧数语义)
v1.0.3 26dc76f 正式 内核 stage 协调器双重解锁(直接跳正式:单点修复 + 反向验证 + 全类审计)

v1.0.2 未使用:该号从未发布也无 tag留空以免与任何本地构建混淆。


四、现网部署与版本对应(运维纪律)

  • 现网 homed 永远部署 release/vX.Y.x 分支上 tag 的构建产物,路径见 Makefilemake buildbuild/homed)。
  • systemd 服务(/usr/local/bin/homed)替换流程:
    1. 备份旧二进制(homed.bak.pre<版本>.<时间戳>
    2. 备份配置库(sqlite3 .backup,不用 cp——WAL 模式下 cp 可能拿到不一致快照)
    3. 记录当前插件建链清单,供重启后逐项比对
    4. install -m 0755 替换(原子 rename不会写坏正在运行的进程镜像
    5. systemctl restart homeagent
    6. 健康检查:版本号、插件清单无缺失、/api/v1/status、一次真实对话、fatal error 计数为 0
  • 改造期间现网不得部署 main 或 feature 的中间态——只有发版才用发布分支的 tag。
  • alpha/beta tag 的产物不上现网(现网是 24/7 服务,预发布通道的存在就是为了不拿它冒险)。
  • 涉及 SDK 仓时:主仓 go.modreplace => ./third_party/homeagent-sdk 指向本地 vendored 副本, 发版前确认 vendored SDK 与 SDK 仓 release tag 一致(两仓版本对齐是第一优先级)。

五、快速参考命令

# 新特性
git checkout main && git pull
git checkout -b feature/xxx
# ... 开发 ...
git checkout main && git merge --no-ff feature/xxx   # 或 squash
git branch -d feature/xxx

# 开一条新中版本的发布线
git checkout -b release/v1.1.x main
git commit -am "chore(release): bump v1.1.0-alpha.1"
git tag -a v1.1.0-alpha.1 -m "..."                   # alpha内部验证
# ... 修问题 ...
git commit -am "chore(release): bump v1.1.0-beta.1"
git tag -a v1.1.0-beta.1 -m "..."                    # beta小范围试用
# ... 真实环境验证 ...
git commit -am "chore(release): bump v1.1.0"
git tag -a v1.1.0 -m "..."                           # 正式

# hotfix发布后——注意是同一条 release/v1.0.x不新建分支
git checkout release/v1.0.x
git commit -am "fix(x): 严重 bug"                    # ① 修复
git commit -am "chore(release): bump v1.0.4"         # ② 版本号
git tag -a v1.0.4 -m "..."
git checkout main
git cherry-pick <修复①的sha>                          # ③ 只挑修复
# 若有活跃 feature 分支,也 pick 过去
git checkout feature/xxx && git cherry-pick <main 上那个 pick 的 sha>

# 发布分支退役(下个中版本发布后,可选)
git branch -d release/v1.0.x                          # tag 已保存历史,删分支不丢东西

六、本规范与「接口冻结」约束的关系

  • feature 分支合回 main 的门禁(git diff third_party/homeagent-sdk/sdk/ 为空)是本仓特有的硬约束,独立于 Git 流程本身。
  • internal/sdk 不受冻结约束,可自由扩展;冻结只针对公开 SDK 接口(third_party/homeagent-sdk/sdk/)。
  • 若整改确需突破公开接口,走变更评审(见 docs/zh/plugin-interface-matrix.md §七), 并同步 SDKCompatibleVersion 与 SDK 仓的 release tag。