ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

LobeHub PR 技能详解:canary 分支策略与跨层功能拆分(Stacked PR)实战

LobeHub PR 技能详解:canary 分支策略与跨层功能拆分(Stacked PR)实战 LobeHub PR 技能详解canary 分支策略与跨层功能拆分Stacked PR实战【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub本文基于 LobeHub 仓库中的 PR 技能文档 .agents/skills/pr/SKILL.md系统讲解该项目「一个分支一个 PR」的标准提交流程与「跨层功能拆分为有序 Stack PR」的分层合并策略。读完你可以掌握为什么 LobeHub 要求所有 PR 目标canary而非main、如何用一组并行 git 命令安全地收集上下文并创建 PR以及如何把一个横跨数据库、共享包、服务端 TRPC、桌面端/CLI 与 UI 的功能分支用可复现的 git 操作步骤安全地拆成「底层先合、上层后合」的两个 PR。技能定位与触发方式该文档是 LobeHub 内置的一个 Agent Skill用于自动化处理「为当前分支创建 PR」这类请求。从 frontmatter 可以看到技能名pruser-invocable: true可由用户直接调用。其描述覆盖了create pr、submit pr、open a PR、pull request、split this PR、stacked PR、backend should merge first、提 PR、拆 PR、后端先合、分层合并等触发词——中英文关键词都纳入了触发范围。配套策略文件 .agents/skills/pr/agents/openai.yaml 声明了policy: allow_implicit_invocation: false即该技能只允许显式调用不会被其他技能隐式触发——这在「改分支、强推远端」这类有副作用的操作上是一个重要的安全边界。技能整体分为两大部分前 6 步的标准单 PR 流程适用于绝大多数场景以及后半部分的Stacked PRs 跨层拆分流程适用于客户端依赖服务端新契约的跨层功能。分支策略canary 是开发主干main 是发布分支文档在开头就确立了两条硬性规则目标分支canary开发分支对应云生产环境main是发布分支——永远不要直接对main提 PR这一策略并非孤立存在仓库的主 Agent 规范 AGENTS.md 中「Git Workflow」一节给出了同样的约定canaryis the development branch (cloud production);mainis the release branch (periodically cherry-picks from canary). New branches should be created fromcanary; PRs should targetcanary. Use rebase forgit pull. Commit messages: prefix with gitmoji. Branch format:type/feature-name.也就是说 LobeHub 的分支模型是日常开发全部流入canarymain周期性从canary同步cherry-pick。仓库中还存在一条专门维护这一模型的 CI 工作流 .github/workflows/sync-main-to-canary.yaml当main收到 push 时机器人lobehubbot会自动以sync/main-to-canary-*为 head 分支、canary为 base 创建同步 PR并复用已存在的同名 open PR 避免重复。理解这一点很重要——它解释了为什么技能流程把canary当作「trunk」来处理所有比对如git log --oneline origin/canary..HEAD与 PR 目标选择。标准流程第一步并行收集上下文技能要求并行执行以下 6 条命令一次性摸清当前分支状态git branch --show-current # 当前分支名 git status --short # 未提交改动 git rev-parse --abbrev-ref {u} 2/dev/null # 远端跟踪upstream状态 git log --oneline origin/canary..HEAD # 尚未进入 canary 的提交 gh pr list --head $(git branch --show-current) --json number,title,state,url # 该分支是否已有 PR git diff --stat --stat-count20 origin/canary..HEAD # 变更概览限制 20 行防刷屏这几条命令覆盖了后续决策需要的全部信息分支名决定推送方式upstream 状态决定是否需要git push -uorigin/canary..HEAD的提交与 diff 决定「是否有东西可提 PR」以及 PR 描述怎么写而gh pr list则是防重复创建的关键检查——文档在 Notes 中明确要求如果该分支已存在 PR应告知用户而不是创建重复 PR。在默认分支上有未提交改动时先建分支再提 PR当当前分支是canary或main且存在未提交改动时文档给出了一条标准处理链git diff分析改动内容从改动推断分支名格式遵循type/short-description例如fix/i18n-cjk-spacing与 AGENTS.md 中type/feature-name的分支格式一致git checkout -b branch-name建分支并切换git add files显式暂存相关文件——文档特意强调优先使用显式文件路径而非git add .避免把无关改动带进 PR用符合 gitmoji 规范的提交信息提交对应 commitlint.config.mjs 基于lobehub/lint的提交校验继续后续步骤。边界情况也有明确约定如果当前在canary/main上既无未提交改动、也没有未推送提交直接中止——没有可创建 PR 的内容。推送、关联 Issue 与创建 PR推送无 upstream 时用git push -u origin $(git branch --show-current)有 upstream 时用git push origin $(git branch --show-current)。两条命令都带显式分支名——这一点在后文「Gotchas」中会被再次强调是防止误推canary的第一道防线。搜索关联 Issuegh issue list --search keywords --state all --limit 10。文档提醒只链接 scope 匹配的 issue避免挂到大而全的 umbrella issue 上没有匹配则跳过。创建 PRgh pr create --base canary要求标题格式gitmoji type(scope): description正文基于 PR 模板 ​.github/PULL_REQUEST_TEMPLATE.md 填写并勾选相应复选框用 magic keywordsFixes #123、Closes #123关联 GitHub issue适用时同时关联 Linear issueFixes LOBE-xxx正文使用HEREDOC传入以保留 Markdown 格式。仓库中的 ​.github/PULL_REQUEST_TEMPLATE.md 实际结构印证了模板要求UI 改动需要 Before/After 截图表格、「Test」小节含三个复选框Tested locally/Added/updated tests/No tests needed以及「Related Issue」小节Fixes #xxx, Closes #xxx, Related to #xxx。技能文档要求填写的四大要素——Change Type、Related Issue、Description of Change、How to Test——正是对这个模板各小节的映射。最后一步gh pr view --web在浏览器中打开 PR 页面供人工确认。另有一条全局约定所有 PR 内容必须使用英文。Stacked PRs当功能横跨多个层时如何拆分这是文档最具价值的部分。文档描述的典型跨层链路是packages/database的 schema/model → 某个共享packages/*库 → 服务端 TRPC 路由 →apps/desktop与apps/cli的调用方 →src/features下的 UI。仓库结构可以佐证这条链路的真实性packages/database/src 下包含schemas、models、repositories、core等目录packages/trpc 提供 TRPC 路由基础设施apps/desktop 与 apps/cli 则是两个主要客户端。为什么一个 PR 合不安全文档给出的核心论据是客户端调用的端点在同一个 PR 合入之前不存在于 trunk 上。此时单 PR 会出问题部分合入、回滚、独立 review 都会破坏一致性评审者无法单独验证任何一层。因此要把功能拆成有序 PR底层先合。排序规则调用方必须等被调用方先上 trunkA PR may only merge after every layer it calls is already on the trunk.即服务端契约新 TRPC procedure、返回结构变化、新表/新模型先合调用方desktop、CLI、UI后合。当难以判断时用一个问题打破平局「如果这个 PR 现在单独合入canary它能构建并正常工作吗」若不能它就该排在后面的 PR 里。文件归属判断三个不直觉的规则适配契约变化的前端代码要跟着服务端 PR 走。例如放宽 TRPC 返回结构listDevices返回值变为platform: string | null消费该结构的组件必须在同一个 PR里同步修改否则服务端 PR 单独合入就会让构建挂掉。原则是契约与其在仓库内的消费方一起交付。新的共享包跟着它的消费方走而不是默认归服务端——除非服务端也 import 它。一个只被 desktop/CLI 引用的包应该放在客户端 PR 里不要在下层 PR 中拖着无用的包。注意技能文档以lobechat/*指代共享包从当前仓库的 apps/cli/package.json 等文件看工作区包实际使用lobehub/*作用域例如lobehub/cli。工作区依赖声明package.json中的workspace:*、pnpm-workspace.yaml 条目随 import 它的代码走。当前仓库的 pnpm-workspace.yaml 声明了packages/**、e2e、apps/server、apps/share、apps/workbench、apps/desktop/src/main等工作区包拆分时这些声明应随对应包代码一起进入正确的 PR。git 操作配方把一个完整分支拆成 Stack起点假设一个分支feat/x上有单个包含全部改动的提交FULL且已推送到远端远端这份副本本身就是安全网之一。# 1. 安全网 —— 改写任何东西之前确保完整工作不可丢失 git branch backup/x-full FULL # 指向完整提交的本地 ref git branch feat/x-clients FULL # 上层分支从完整提交起步 # 2. 把下层分支重写为只含下层文件 git checkout feat/x # 这个分支将成为 SERVER PR git reset --hard origin/canary git checkout FULL -- server/db files… # 只暂存这些路径 git commit -m ✨ feat(...): server half git push --force-with-lease origin feat/x # 永远不用 --force永远不推 canary # 3. 在刚重写的下层 HEAD 之上构建上层分支 git checkout feat/x-clients git reset --hard feat/x # base 刚重写的 server HEAD git checkout backup/x-full -- client/ui files… # 只取剩余路径 git commit -m ✨ feat(...): client half git push -u origin feat/x-clients核心技巧是git checkout FULL -- paths从完整提交中按路径挑选文件到暂存区从而在干净的origin/canary基线上只重建下层改动上层分支则reset --hard到下层的重写结果之上形成物理上的依赖。随后创建上层 PR 时base 指向下层分支而非 trunkgh pr create --base feat/x --head feat/x-clients --title … --body …--base feat/x带来两个效果diff 只包含客户端文件不会泄漏服务端文件并且从机制上杜绝了客户端先于服务端合并的可能。服务端 PR 合入canary后把客户端 PR 的 base 重新指向canary——GitHub 通常在 base 分支合并时会自动 retarget文档建议同时在 PR 正文中说明这一点由人工确认。验证依赖关系确实成立拆分的意义在于上层确实需要下层。文档要求证明这一点在叠加上层分支上对调用方做类型检查确认下层引入的符号能解析cd apps/cli bun run type-check 21 | grep -iE connect\.ts|device\.register # 与你的改动相关的输出为空 栈式 base 提供了 device.register ✓其中 apps/cli/package.json 定义的type-check脚本为tsc --noEmit。文档还提示过滤到你实际改动的文件——这个仓库的独立类型检查会输出一些与你无关的环境噪音如__ELECTRON__、/types/llm、未构建的lobechat/types不要把这些当成拆分失败。PR 与 Linear 的记账规则每个 PR 只关闭自己所属层的 issue服务端 PR 写Closes LOBE-server客户端 PR 写Closes LOBE-pkg / desktop / cli不要让一个 PR 的正文去认领另一层的 issue。两个 PR 都标记为Part of LOBE-parent。创建 PR 时把各自关闭的子 issue 移到In Review而非 Done并补 completion 评论。这一点与配套技能 .agents/skills/linear/SKILL.md 的约定一致该技能明确「In Review 是 PR 创建后的状态Done 留给 PR 合并之后」且要求Fixes/Closes/Resolves LOBE-xxxmagic keywords 与 issue 上的完成评论成对完成——只写 magic keyword 而不留评论会让 Linear 上的人看不到任何上下文。Gotchas六个踩坑点永远不要推canary。用git checkout -b feat/x origin/canary切出的分支会跟踪origin/canary此时裸git push会推往 canary。务必始终使用显式分支名git push origin feat/x。重写下层分支用--force-with-lease不用--force——如果远端在你脚下移动了他人推送lease 检查会中止操作。reset --hard之前先备份。配方第 1 步的backup/x-full加上已推送的远端分支意味着完整提交在被改写前至少被 ≥3 个 ref 引用。可用git branch --contains FULL验证。锁文件问题文档指出这个 monorepo不提交根级pnpm-lock.yaml因此新增workspace:*依赖不产生 lockfile 变更。当前仓库确实没有根级 lockfile且 pnpm-workspace.yaml 显式声明了lockfile: false。反过来如果在一个确实提交lockfile 的仓库中使用这套配方拆分后需要在每个分支上分别重新生成 lockfile。不要过度拆分。两个 PR契约 / 调用方通常已经足够只读取现有端点的 UI 页面可以成为后续的独立 PR但不要为了拆分而拆分把同一层拆散到多个 PR 里。小结LobeHub 的 PR 技能把「提 PR」这件事工程化成了两个层级单 PR 场景下以canary为 trunk、以显式分支名推送、以模板与 magic keywords 收尾的六步标准流程跨层场景下以「单独合入能否构建并正常工作」为判据的排序规则、以git checkout FULL -- paths为核心的无损拆分配方以及用类型检查证明依赖关系真实存在的验证手段。整套流程的安全设计——三重 ref 备份、--force-with-lease、--base指向下层分支、显式分支名——都围绕同一个目标让分层合并既保持正确的依赖顺序又不丢失任何一行已经写完的代码。相关延伸阅读AGENTS.md完整 Git 工作流与质量检查约定、.agents/skills/linear/SKILL.mdLinear issue 状态与完成评论规范、​.github/PULL_REQUEST_TEMPLATE.mdPR 模板原文。【免费下载链接】lobehub LobeHub is your Chief Agent Operator, organizing your agents into 7×24 operations by hiring, scheduling, and reporting on your entire AI team.项目地址: https://gitcode.com/GitHub_Trending/lo/lobehub创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表