关于 multi-repo-git-ops
multi-repo-git-ops 是一项面向由父仓库和 Git 子模块组成项目的技能。它指导 Agent 发现仓库结构、管理各服务的分支与提交,并在适用时配合 BMAD 故事流程。
主要功能
- 01发现子模块及其跟踪分支
- 02将改动映射到相关仓库
- 03协调多个服务的分支
- 04指导提交、推送与拉取请求
- 05按 BMAD 故事安排 Git 操作
适用场景
- 01启动跨多个服务仓库的工作
- 02实现涉及多个服务的 BMAD 故事
- 03准备服务改动以供审查和合并
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ bmad-labs/ skills --skill multi-repo-git-ops安装后在 Claude Code 中输入/multi-repo-git-ops确认已加载。
详细介绍
多仓库 Git 操作
此技能用于处理使用 Git 子模块的多仓库系统中的 Git 操作。父仓库负责协调多个服务仓库,每个服务仓库都是独立的 Git 仓库,拥有自己的分支、历史记录和 CI/CD。
理解这一结构至关重要——这里的 Git 操作始终需要先确定哪些仓库会受到影响,再在每个仓库中正确执行操作。
发现流程
执行任何 Git 操作之前,都要动态发现项目结构。切勿假设服务名称、分支映射或目录布局——始终从 .gitmodules 读取。
步骤 1:识别子模块及其路径
# List all submodules with their paths
git config -f .gitmodules --get-regexp '\.path$'
步骤 2:识别每个子模块所跟踪的分支
# List all submodule branch mappings
git config -f .gitmodules --get-regexp '\.branch$'
# Get a specific service's default branch
git config -f .gitmodules submodule."{submodule-path}".branch
如果 .gitmodules 中未为某个子模块配置分支,请检查远程:
cd {submodule-path}
git remote show origin | grep 'HEAD branch'
步骤 3:检测项目方法论
- BMAD:检查
_bmad-output/implementation-artifacts/目录是否存在 - 如果检测到 BMAD,则适用 BMAD 集成部分
步骤 4:缓存本次会话中发现的信息
发现后,Agent 将了解:
- 所有子模块的名称和路径
- 每个子模块的默认分支
- 是否使用 BMAD
- 子模块目录前缀(通常为
services/)
在本次会话后续的所有操作中使用这些缓存信息。
仓库结构
典型的多仓库项目如下所示:
{parent-repo}/ (parent repo — planning & orchestration only)
├── .gitmodules (submodule definitions with tracked branches)
├── services/ (submodule repos — directory name may vary)
│ ├── auth-service/ (branch: develop)
│ ├── booking-service/ (branch: dev)
│ ├── config-repo/ (branch: main)
│ └── ...
└── [project methodology dirs] (e.g., _bmad-output/ if using BMAD)
关键原则:父仓库跟踪的是每个子模块的提交指针,而不是子模块代码本身。修改服务中的代码时,应先在服务仓库中提交,然后更新父仓库中的指针。
分支约定
各服务的默认分支
服务可能跟踪不同的默认分支——创建分支之前,始终先查明正确的默认分支:
# Get a service's default branch (always use this — never assume)
git config -f .gitmodules submodule."{submodule-path}".branch
同一项目中的不同服务通常使用不同的默认分支(例如 develop、dev、main)。.gitmodules 是唯一可靠的信息来源。
功能分支命名
对于基于工单的工作(JIRA/项目跟踪工具):
{type}/{TICKET-ID}-{short-description}
示例:feat/PROJ-1234-add-search-by-date、bugfix/PROJ-5678-fix-null-pointer
对于 BMAD 故事相关工作(根据故事文件名生成):
feat/{story-key}
故事键取自故事文件名。名为 1-2-user-authentication.md 的故事文件(表示 Epic 1、Story 2)会生成分支名 feat/1-2-user-authentication。
分支类型前缀:feat/、fix/、bugfix/、chore/、base/
BMAD 集成(如适用)
本部分仅适用于使用 BMAD 方法论的项目。通过检查 _bmad-output/implementation-artifacts/ 目录来进行检测。
BMAD 通过故事文件和冲刺状态跟踪来管理工作。本部分介绍 Git 操作如何对应 BMAD 生命周期。
故事如何引用服务
BMAD 故事文件位于 _bmad-output/implementation-artifacts/{story-key}.md。故事会通过多种方式引用受影响的服务——请检查以下所有位置:
- 故事标题或正文中的服务标签:
[auth-service]、[scheduler-service] - Dev Notes 部分:列出“要修改的源代码树组件”及其服务路径
- Project Structure Notes:引用
services/{service-name}/src/...等路径 - Tasks/Subtasks:各个任务可能会引用不同的服务
当故事未明确标注服务时,请查看 Dev Notes 和 Tasks 中提到的文件路径,以确定涉及哪些子模块目录。
BMAD 状态与 Git 操作映射
BMAD 会在 _bmad-output/implementation-artifacts/sprint-status.yaml 中跟踪故事状态。每次状态转换都有对应的 Git 操作:
| 故事状态变更 | 所需的 Git 操作 |
|---|---|
backlog → ready-for-dev | 无需 Git 操作(仅创建故事文件) |
ready-for-dev → in-progress | 在受影响的服务中创建功能分支 |
in-progress(持续进行中) | 在服务仓库中提交更改 |
in-progress → review | 推送所有服务分支,确保工作区干净 |
review → in-progress(修复) | 继续使用相同分支,并提交修复 |
review → done | PR 已合并,更新父级子模块指针 |
开始 BMAD 故事(Git 设置)
开始处理 BMAD 故事时,请执行以下 Git 操作:
步骤 1:阅读故事并确定涉及的服务
# Story files follow pattern: {epic_num}-{story_num}-{slug}.md
# Example: _bmad-output/implementation-artifacts/1-2-user-authentication.md
阅读故事文件。从以下内容中提取受影响的服务:
- 服务标签,例如
[auth-service] - Dev Notes 中的文件路径(例如
services/{service-name}/src/...) - 提及特定服务的任务描述
步骤 2:根据故事键生成分支名称
# Story file: 1-2-user-authentication.md → branch: feat/1-2-user-authentication
STORY_KEY="1-2-user-authentication" # from the story file name (without .md)
BRANCH_NAME="feat/${STORY_KEY}"
步骤 3:在所有受影响的服务中创建分支
# For each affected service (replace with actual discovered service names):
for SERVICE in auth-service scheduler-service; do
DEFAULT_BRANCH=$(git config -f .gitmodules submodule."services/$SERVICE".branch)
cd services/$SERVICE
git checkout $DEFAULT_BRANCH
git pull origin $DEFAULT_BRANCH
git checkout -b $BRANCH_NAME
git push -u origin $BRANCH_NAME
cd ../..
done
在所有受影响的服务中使用相同的分支名称,以便追踪。这样就能轻松找到系统中与某个故事相关的所有更改。
步骤 4:验证设置
# Confirm all affected services are on the correct branch
git submodule foreach --quiet 'BRANCH=$(git branch --show-current); echo "$(basename $(pwd)): $BRANCH"'
故事实施期间
提交更改——始终在服务目录中提交:
cd services/{service-name}
git add {specific-files}
git commit -m "feat({scope}): add validation for booking dates
Story: {story-key}"
跨多个服务协作——分别在每个服务中提交:
# Service A
cd services/auth-service
git add src/auth/jwt.service.ts src/auth/jwt.service.spec.ts
git commit -m "feat(auth): add JWT validation endpoint
Story: 1-2-user-authentication"
# Service B
cd ../bff-service
git add src/middleware/auth.middleware.ts
git commit -m "feat(middleware): add JWT auth middleware
Story: 1-2-user-authentication"
所有提交都必须遵循下文的提交消息格式部分——这对 release-please 正确生成变更日志和版本升级至关重要。
完成故事(推送与 PR)
故事准备好接受审查时:
步骤 1:在每个受影响的服务中运行质量检查
cd services/{service-name}
npm run lint
npm run format
npm run typecheck
npm test
步骤 2:推送每个服务的功能分支
# Push services first — always before parent
cd services/{service-name}
git push origin feat/{story-key}
步骤 3:为每个服务创建 PR
每个服务都需要单独的 PR:feat/{story-key} → 服务的默认分支。
PR 标题格式:feat({story-key}): {story title}
PR 正文应引用故事:Story: {epic_num}.{story_num} - {title}
步骤 4:PR 合并后,更新父级
# For each merged service:
cd services/{service-name}
git checkout {default-branch}
git pull origin {default-branch}
cd ../..
# Update parent pointers (use actual discovered service paths)
git add services/auth-service services/bff-service
git commit -m "chore: update submodule pointers after story {story-key}
Services updated:
- auth-service
- bff-service"
提交消息格式(兼容 Release-Please)
如果项目使用 release-please 自动管理版本和生成变更日志,那么每条提交消息都必须遵循 Conventional Commits 规范,以便 release-please 正确确定版本升级并生成有意义的变更日志。
即使不使用 release-please,Conventional Commits 也是多仓库系统的推荐格式,因为它能在多个仓库中生成整洁、可解析的 Git 历史记录。
不要添加 AI 共同作者尾注
绝不要在提交消息中为 AI Agent 添加 Co-Authored-By 尾注。 这意味着不要添加如下行:
Co-Authored-By: Claude <[email protected]>
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
提交消息只能包含下文所述的 Conventional Commits 结构。在提交中标注 AI 归属会污染变更日志、给 Git 历史记录增加噪声,而且毫无价值。此规则适用于所有提交——功能、修复、杂项或其他类型。
结构
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
标题规则:
type——必填(参见下方的类型表)scope——可选,用括号括起,用于描述受影响代码部分的名词description——必填,紧跟在:后- 使用祈使语气和现在时:“add”,而非“added”或“adds”
- 首字母不要大写
- 末尾不要加句号
- 长度控制在约 100 个字符以内
正文——可选,与标题之间以空行分隔。自由格式,用于说明“做了什么”以及“为什么”。
页脚——可选,与正文之间以空行分隔。用于记录破坏性变更、故事引用及其他元数据。
提交类型与版本升级
Release-please 仅在检测到可发布单元时才会创建发布——也就是类型对应版本升级的提交。选择正确的类型很重要,因为它会直接决定发布的版本以及变更日志中的内容。
可发布类型(会触发发布)
| 类型 | SemVer 升级 | 使用场景 |
|---|---|---|
feat | 次版本(0.x.0) | 新能力、端点、功能、UI 组件 |
fix | 修订版本(0.0.x) | 修复现有功能中的错误 |
deps | 修订版本(0.0.x) | 更新依赖项(package.json、锁定文件) |
不可发布类型(单独使用时不会触发发布)
| 类型 | 使用场景 |
|---|---|
refactor | 重构代码,但不改变行为 |
perf | 性能改进(特殊重构) |
test | 添加或修正测试 |
docs | 仅修改文档 |
style | 代码风格(格式、空白、分号) |
chore | 维护任务(.gitignore、配置、工具) |
build | 构建系统、依赖项、项目版本 |
ci | CI/CD 流水线和工作流变更 |
如果自上次发布以来只提交了不可发布类型,则不会创建发布 PR。请谨慎选择——如果变更实际属于 feat: 或 fix:,就不要使用 chore:。
选择正确的类型
New capability or feature? → feat
Fixing a bug? → fix
Dependency update? → deps
Performance improvement? → perf
Code restructuring, no behavior Δ? → refactor
Documentation only? → docs
Tests only? → test
CI/CD or build changes? → ci / build
Everything else (configs, tooling)? → chore
重大变更(主版本升级)
任何类型的提交只要包含重大变更,都会触发主版本升级。格式有两种:
选项 A — 在标题中使用 !:
feat(api)!: redesign authentication flow
BREAKING CHANGE: the /auth/login endpoint now requires OAuth2 tokens
instead of API keys. All existing integrations must migrate.
选项 B — 仅使用页脚:
refactor(database): migrate from MongoDB to PostgreSQL
BREAKING CHANGE: all database connection strings must be updated.
The MongoDB driver is no longer included.
页脚中的 BREAKING CHANGE(空格)和 BREAKING-CHANGE(连字符)都会被识别。
标记重大变更时,务必添加 BREAKING CHANGE: 页脚,说明迁移影响——即使使用 ! 格式也一样。使用者会通过该页脚了解需要做哪些变更。
作用域
作用域用于说明受影响的代码部分——它有助于整理变更日志,并在单体仓库配置中将变更分配到正确的软件包。
好的作用域(描述代码区域):
feat(auth): add JWT validation
fix(scheduler): correct cron expression parsing
refactor(booking): extract date utility
不好的作用域(这些属于反模式):
fix(PROJ-1234): ... ← ticket IDs are not scopes
feat(john): ... ← people are not scopes
使用领域或模块名称作为作用域(例如,auth、booking、scheduler、middleware、config、migration)。
Story 引用
如果使用 BMAD,请将 Story 键放在提交正文或页脚中,而不是作用域中:
feat(auth): add JWT validation endpoint
Implement token validation with RS256 signing.
Story: 1-2-user-authentication
Release-Please 专用页脚
| 页脚 | 用途 |
|---|---|
BREAKING CHANGE: <desc> | 触发主版本升级 |
Release-As: x.x.x | 强制指定版本号 |
Release-As 示例——适用于首次发布或协调版本跃迁:
chore: prepare v3.0.0 release
Release-As: 3.0.0
完整示例
简单功能(次版本升级):
feat(booking): add search by date range endpoint
附带上下文的错误修复(修订版本升级):
fix(auth): resolve token refresh race condition
The refresh token was being invalidated before the new access token
was issued, causing a brief window where requests would fail.
Story: 2-3-auth-improvements
重大变更(主版本升级):
feat(api)!: remove deprecated v1 booking endpoints
BREAKING CHANGE: all /api/v1/bookings/* endpoints are removed.
Consumers must migrate to /api/v2/bookings/* which uses the new
pagination format.
Story: 3-1-api-v2-migration
依赖项更新(修订版本升级):
deps: upgrade @nestjs/core to v11.0.0
仅修改测试(不发布):
test(scheduler): add unit tests for cron expression parser
维护任务(不发布):
chore: update .gitignore to exclude coverage reports
父仓库提交消息
在父仓库中更新子模块指针时,请使用 chore:,因为这些变更并不代表父仓库本身的功能变更:
chore: update submodule pointers after story 1-2-user-authentication
Services updated:
- auth-service
- bff-service
常规操作参考
创建功能分支(非 BMAD)
对于未关联 BMAD Story 的临时工作:
DEFAULT_BRANCH=$(git config -f .gitmodules submodule."{submodule-path}".branch)
cd {submodule-path}
git checkout $DEFAULT_BRANCH
git pull origin $DEFAULT_BRANCH
git checkout -b feat/{TICKET-ID}-{description}
git push -u origin feat/{TICKET-ID}-{description}
同步所有子模块
# Pull parent and update all submodule pointers
git pull origin main
git submodule update --recursive
# OR: Pull latest from each submodule's tracked branch
git submodule foreach 'git pull origin $(git config -f $toplevel/.gitmodules submodule.$name.branch)'
检查所有仓库的状态
# Quick status: show only services with changes or non-default branches
git submodule foreach --quiet \
'STATUS=$(git status --porcelain); BRANCH=$(git branch --show-current); DEFAULT=$(git config -f $toplevel/.gitmodules submodule.$name.branch); if [ -n "$STATUS" ] || [ "$BRANCH" != "$DEFAULT" ]; then echo "$(basename $(pwd)) [$BRANCH]: $([ -n "$STATUS" ] && echo "has changes" || echo "clean, non-default branch")"; fi'
将服务切换回默认分支
DEFAULT_BRANCH=$(git config -f .gitmodules submodule."{submodule-path}".branch)
cd {submodule-path}
git checkout $DEFAULT_BRANCH
git pull origin $DEFAULT_BRANCH
CI/CD 感知
在多仓库系统中,了解推送后会发生什么至关重要。典型的分支与部署映射如下:
| 分支 | 典型的推送效果 |
|---|---|
develop / dev | 可能会自动部署到开发环境——请先与用户确认 |
main | 稳定的参考分支——可使用部署工作流 |
test、stage、release | 通过 workflow_dispatch 手动部署 |
feat/*、base/* | CI 会运行(lint、test、build),但不会部署 |
实际部署行为取决于项目的 CI/CD 配置。检查项目是否将共享 CI/CD 工作流仓库作为子模块,并与用户确认哪些分支会触发部署。
安全规则
这些规则可避免多仓库系统中的常见错误:
- 始终先推送服务仓库,再推送父仓库——先推送父仓库会创建损坏的子模块指针,导致其他开发者无法检出
- 绝不对共享的默认分支(
develop、dev、main)执行强制推送——这些都是共享分支 - 推送到会触发部署的分支前,务必与用户确认
- 创建功能分支前,先检查服务的默认分支——不同服务的默认分支可能不同,从错误的基准分支创建分支会导致合并冲突
- 绝不要在服务仓库中使用
git add .或git add -A——始终添加指定文件,以避免提交密钥或构建产物 - 绝不要在任何仓库中提交
.env、凭据、密钥或node_modules - 多服务事项应在各服务中使用相同的分支名称——便于追踪
- 当 Git 操作改变事项状态时,更新 sprint-status.yaml(创建分支 →
in-progress,推送供审核 →review)——仅限使用 BMAD 时 - 提交信息中绝不要添加 AI Agent 的
Co-Authored-By尾注——不得注明 Claude、Copilot 或任何 AI 的参与。这类内容会污染变更日志和 Git 历史。提交信息只能包含约定式提交结构(类型、范围、描述、正文,以及用于注明破坏性变更/事项引用的页脚) - 所有提交信息都必须采用 Conventional Commits 格式——release-please 依赖该格式来自动确定版本号并生成变更日志。格式错误的提交信息可能导致版本发布遗漏或版本号调整不正确