适用范围:使用 Claude Code 在 Monorepo、多独立 Git 仓库、git worktree、submodule 等场景下进行协作开发。
先立个基调:多仓协作难不倒 AI,出问题的几乎都在人这一侧:拓扑没选对,约定没写成机制,上下文没做卫生。
一、先选仓库拓扑:结构决定协作方式
后面所有实践都建立在一个前提上:仓库结构本身选对了。AI 多端开发(前端 + 后端 + 小程序……)的仓库组织,主流是三种方案,外加一个轻量变体。
1.1 方案一:工作台仓库 + git submodule(最重)
按端拆分独立仓库,另建一个"工作台"仓库,把各端仓库以 submodule 引入,统一在工作台开发:
| |
这套方案真正买到的只有一样东西:版本锁定,工作台记录每个子仓的精确 commit。
- 优点:git 原生强制锁定,“哪个前端版本配哪个后端版本"可复现、可回滚;CI checkout 工作台即得完整组合,集成测试可以直接自动化;各子仓权限与发布边界清晰
- 缺点:两步提交、detached HEAD、
update --remote,submodule 的这套摩擦一样不少;提交规则要显式写进工作台 CLAUDE.md 约束 AI,否则容易两步提交漏一步(见第六节);日常联调本要同步演进到最新,锁定旧 commit 反而是摩擦源
适用:多团队分治、发布需要组合版本审计。没有这个需求,摩擦全是白付的。
1.2 方案二:工作台仓库 + gitignore 普通文件夹(甜点位)
在方案一基础上去掉 submodule:各端仓库直接 git clone 进工作台目录,每个被忽略的文件夹内部都是一个完整的独立 git 仓库,有自己的 .git/、完整历史和远端;工作台的 .gitignore 只是让工作台对它们视而不见,工作台仓库本身只版本化"胶水层”:
| |
本质是 “meta repo” 模式(Android repo、meta 这类工具的手工版)。
- 优点:零 submodule 摩擦;所有子仓都在主工作目录之内,AI 全部可见,不需要任何
/add-dir,各子仓自己的 CLAUDE.md 也会按需自动加载(见 4.3 节);跨仓胶水(CLAUDE.md、联调脚本、接口契约)有地方版本化;子仓各自保留独立历史与独立部署 - 缺点:工作台是空壳、不可复现;没有版本锁定;集成 CI 跑不起来;嵌套仓库下 AI 容易搞错提交目标
缺点看着多,但每条都有便宜的机制兜底:
工作台是个空壳,不可复现:clone 下来只有胶水层。但这条在 AI 时代成色大打折扣,把子仓清单(目录名、仓库地址、分支)写进工作台 CLAUDE.md 就够了:
1 2 3## 子仓清单 - frontend/ → git@github.com:org/frontend.git(main) - backend/ → git@github.com:org/backend.git(main)新环境 clone 工作台后,让 Claude 照单把子仓拉齐即可;子仓一旦就位,每个目录都是完整 git 仓库,remote、分支这些信息 git 本身就带着,不需要额外维护。想要不依赖 AI 的确定性引导,再升级成
repos.yaml+setup.sh(Androidrepo工具的 manifest.xml 干的就是这件事)。脚本化还有个副产品,穷人版版本锁定:联调到里程碑时,把各子仓当前 SHA 写进 lockfile 提交,“组合版本审计"的需求就覆盖了大半。方案一的适用面被进一步压缩到只剩"需要 git 原生强制锁定"一种。AI 容易搞错目标仓:嵌套 git 仓库下,在工作台根目录跑
git add/git commit,作用的是工作台仓库而不是子仓。除了在工作台 CLAUDE.md 写明「每个子目录是独立 git 仓库,进入子目录分别提交」,更硬的兜底是用 PreToolUse hook 直接拦截工作台根目录的 git 提交。约定不如机制。集成 CI 跑不起来:CI checkout 工作台得到的同样是空壳。跨端集成测试要么靠 manifest 驱动多仓 checkout,要么退回各子仓自己的 CI(对选型的影响见 1.5)。
适用:个人 / 小团队 AI 开发、前后端紧耦合日常联调。
1.3 方案三:Monorepo(新项目默认)
所有端放同一个仓库。AI 时代它不只是"最简单”,更大的红利是跨栈原子提交:改一个 API,同时改掉服务端实现和所有客户端调用,接口漂移在物理上不可能发生。
- 优点:跨栈原子提交,一个 commit、一次 review、一套 CI 验证全链路;没有任何跨仓机制要维护,上手心智成本最低;上下文对 AI 完整连续
- 缺点:部署与发布耦合;访问权限只能到目录粒度;项目扩大后仓库内容持续膨胀,CI/CD 越跑越重,任何一端的改动默认触发全仓构建发布,不做增量裁剪时流水线时长随仓库体积线性上涨,这也是团队和代码量长大后必须付构建工具链(turborepo / nx 的 affected 增量构建)门槛的原因;仓库大了 AI 上下文有压力
最后一条的直接回应是 git sparse-checkout:只检出在做的子树,Claude 的可见范围和上下文随检出范围收缩。各端约束用嵌套 CLAUDE.md 分层管理(见 4.3 节)。
适用:新项目、有选择权 → 默认选它。
1.4 轻量变体:平级目录 + additionalDirectories
不建工作台,各仓平级摆放,在主仓 .claude/settings.json 里持久声明兄弟仓(写法见 2.2 节)。四个方案里最轻的一个,连工作台仓库都不用建,配一次每次启动自动生效。代价:跨仓胶水无处版本化;辅仓 CLAUDE.md 默认不加载(见 4.3 节);../ 相对路径依赖每个人本地目录摆放一致,仓库本身无法强制。
适用:“一主一辅"形态,比如主要在后端开发,偶尔看前端怎么调用。
1.5 选型一览
| 方案 | 一句话判据 |
|---|---|
| 一:工作台 + submodule | 需要组合版本审计、可复现的集成点 |
| 二:工作台 + ignore 文件夹 | 紧耦合多仓日常联调,跨仓胶水多到值得一个仓库来管 |
| 三:Monorepo | 新项目、有选择权,默认选它 |
| 四:平级目录 + additionalDirectories | 一主一辅,偶尔跨仓看看 |
方案二和方案四的分界线就一句话:跨仓的胶水(脚本、契约、共享规范)多到值得专门一个仓库来管吗?
还有一条隐形判据是 CI:如果跨端集成测试必须在 CI 里强制执行,可选项基本只剩方案一(submodule 可以直接 checkout)和方案三(本来就是一个仓库);方案二要靠 manifest 驱动多仓 checkout 才能补上这块。反过来,集成验证只在本地联调时做的团队,方案二没有任何损失。
二、/add-dir 命令
2.1 定位
把 Claude Code 从「单仓库工具」扩展为「多仓库协调器」:将当前会话之外的目录动态纳入工作上下文。
2.2 三种加入方式
claude --add-dir <path>...:启动即生效,适合本次任务的已知工作集/add-dir <path>:会话内动态追加,适合临时引用参考实现,重启即清settings.json 持久化:固定不变的工作集(如前后端两仓永远一起开)写进仓库级
.claude/settings.json,每次启动自动生效:1 2 3 4 5{ "permissions": { "additionalDirectories": ["../frontend-repo"] } }
2.3 权限与边界
额外目录与主工作目录共享同一权限模型:自动可读,编辑权限沿用 settings.json
不会出现额外的权限提示 ⇒ 不要随手加敏感目录(如
~/.ssh、家目录、根目录)工作目录内的敏感文件(
.env、密钥、凭据)用permissions.deny挡住。这是安全防线,搜索效率上并不需要它(.gitignore已经覆盖,见 7.1 节):1 2 3 4 5{ "permissions": { "deny": ["Read(./.env*)", "Read(./secrets/**)"] } }额外目录的 CLAUDE.md 默认不加载,开启方式见 4.3 节
三、多仓库协作核心决策
3.1 同会话 vs 多会话
| 仓库关系 | 仓库数量 | 推荐策略 |
|---|---|---|
| 紧耦合(共享库、接口实时联调) | ≤ 3 | 同会话 + /add-dir |
| 松耦合(独立功能、各自上线) | 任意 | 每仓一会话 |
| 主仓 + 临时参考仓 | ≤ 5 | 主仓会话 + 临时 /add-dir 参考仓 |
| 大型 Monorepo(单仓 > 50MB) | 1 | 单会话 + 嵌套 CLAUDE.md + sparse-checkout 收缩可见范围 |
| 跨语言异构仓库 | ≥ 3 | 多会话 + MCP server / 共享工具桥接 |
3.2 跨仓代码引用策略
| 引用类型 | 推荐方案 | 避免 |
|---|---|---|
| 类型 / 接口契约 | 抽共享包(npm / go module / proto) | 在多仓重复声明 |
| 工具函数 / 通用库 | 独立仓库 + 包管理器引用 | copy-paste |
| 紧耦合代码(前后端同步演进) | git submodule | 长期 /add-dir |
| 一次性参考别人怎么写 | 临时 /add-dir + 注释标注来源 | 不留痕直接 copy |
3.3 契约优先:比拓扑更高杠杆
3.2 里"抽共享包"解决的是复用,真正消灭接口漂移的是契约优先(schema-first):OpenAPI / proto 文件作为唯一事实源,两端代码由生成器产出。AI 改接口时改的是 schema,改完跑 codegen,两端实现跟着走,“前端以为的接口"和"后端实际的接口"物理上就是同一份文件。
它对拓扑选型也有影响:接口漂移这个多仓最大的痛点被工具链消掉之后,拓扑本身的权重就下降了,松耦合方案的最大缺陷不复存在。落点上它和拓扑方案二配合最好:契约文件正好放工作台仓库里版本化。
四、CLAUDE.md 与 settings.json 组织
4.1 分层原则
用户级
~/.claude/:个人编码风格、全局偏好与 permissions 兜底仓库级
<repo>/CLAUDE.md+.claude/settings.json:该仓库的技术栈、构建/测试命令、编码规范、领域知识,以及仓库特定权限、hooks跨仓共通规范单独放一个文件,用
@import语法引进各仓库的 CLAUDE.md(支持相对/绝对路径,最多递归 4 层):1 2# 本仓库 CLAUDE.md @../shared/team-standards.md这比"抄一份进每个仓库"或"放 wiki 里指望人去看"都可靠:规范只维护一份,各仓库引用同源。不要把跨仓库的内容写进单一仓库的 CLAUDE.md,会随仓库漂移。
4.2 权限管理
跨仓共享的 Bash / MCP 权限放用户级 settings.json,仓库特有的放仓库级;.claude/settings.local.json 是个人临时配置,不要提交进 git。
4.3 多仓 CLAUDE.md 加载
关键分界是子仓在不在主工作目录之内,两边机制完全不同:
在主工作目录之内(方案一的 submodule、方案二的 ignore 文件夹、Monorepo 的子包):嵌套 CLAUDE.md 按需自动加载,Claude 读到某个子目录下的文件时,该目录的 CLAUDE.md 才被拉进上下文。不需要任何配置,还顺便省 token:没碰过的子仓,其约束也不占上下文。
在主工作目录之外(
--add-dir/additionalDirectories加入的目录):CLAUDE.md 默认不加载,需显式开启:1export CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1注意两个限制:值是
1而不是true;且只对--add-dir//add-dir加入的目录生效,对 settings.json 里additionalDirectories配置的目录不生效。
⚠️ 多份 CLAUDE.md 同时加载可能产生约束冲突,提前对齐风格。
五、git worktree 场景
同一仓库多分支并行的首选:物理隔离、无需切换 / stash,每个 worktree 一个 Claude 会话(~/code/demo/、demo-feat-x/、demo-fix-y/ 平级摆放),CLAUDE.md 共用同源,任务上下文互不污染。
不想手动 git worktree add 的话,Claude Code 已把这套流程产品化:
claude --worktree(-w):启动时自动创建/进入一个隔离 worktree,任务做完没有改动会自动清理.worktreeinclude:声明哪些未跟踪文件(如.env、本地配置)要拷贝进新 worktree,解决"新 worktree 缺本地配置跑不起来"的经典问题
跨 worktree 对比代码:/add-dir ../demo-feat-x,临时查看后清理。
六、git submodule 场景
这是拓扑方案一(工作台 + submodule)的日常形态:
- 在外层仓启动 Claude,submodule 路径自动在工作目录内,无需
/add-dir - 但需注意:
- submodule 更新需手动
git submodule update --remote - 修改 submodule 内文件 → 需要在 submodule 仓提交,再到外层仓更新引用
- 在工作台 CLAUDE.md 写明「这是 submodule,提交分两步」,写一次每个会话自动加载,不用口头反复提醒
- submodule 更新需手动
七、上下文管理
多仓本身并不额外费上下文:搜索按需进行,嵌套 CLAUDE.md 按需加载,没碰过的子仓几乎是免费的。真正要管理的是长会话的上下文污染:跨仓任务战线长,读过的文件、跑过的命令残留累积,稀释注意力、拉低回答质量。下面的动作说到底就两件事:少装进来,及时卸载。
7.1 缩小搜索面
- 使用 Grep / Glob 工具精准定位,禁止整仓扫描
- 构建产物、依赖目录不需要任何额外配置:搜索工具默认尊重
.gitignore。顺带辟个谣:Claude Code 没有.claudeignore文件,网上不少文章以讹传讹
7.2 主动卸载上下文
- 切换任务时用
/clear清空上下文,效果等同重启,不用退出进程 - 长任务中途上下文臃肿但还不能丢,用
/compact智能压缩(保留结论、丢弃过程)
7.3 Agent Teams 分工
仓库多到单会话装不下时,改用分工:主 agent 只做调度与结果汇总,每个 teammate 负责一个仓(其会话只持有该仓上下文),避免单会话上下文爆炸。团队无需手动创建或销毁:通过 Agent 工具派生 teammate 时自动建团,任务结束自动回收。
八、决策清单(Quick Reference)
启动多仓任务前,先回答:
- 仓库拓扑定了吗?(四选一,见第一节;新项目默认 Monorepo)
- 这些仓库是紧耦合还是松耦合?数量是否 ≤ 3?若否,多会话或 agent teams
- 接口契约是否 schema 化(OpenAPI / proto + codegen)?共享代码是否抽包?
- 跨端集成测试要不要在 CI 强制执行?(直接影响拓扑选择,见 1.5 节)
- 每个仓库是否都有自己的 CLAUDE.md?跨仓规范是否用
@import同源引用? - 敏感文件(
.env、密钥、凭据)是否用permissions.deny挡住?
九、反模式总结
| 反模式 | 后果 | 正确做法 |
|---|---|---|
拓扑没想清楚,靠 /add-dir 缝合 | 结构问题被会话技巧掩盖,越缝越乱 | 先按第一节选定拓扑 |
| 把 5 个以上仓库 add 进同一会话 | 上下文爆炸 | agent teams 分工 |
用 /add-dir 替代版本化的共享包 | 接口漂移、回归风险 | 抽包 + 包管理器 |
| 接口靠口头 / 文档同步 | 两端各写各的类型,必然漂移 | schema 单一事实源 + codegen |
| 跨仓库写在同一 CLAUDE.md | 仓库迁移 / 拆分时失效 | 仓库各自 CLAUDE.md + @import |
| 把敏感目录 add 进会话 | 凭据 / 密钥泄露给模型 | 严格审查 add 路径 |
| 长会话不清理,反复 add / 切任务 | token 浪费、回答质量下降 | 任务边界 = 会话边界,切任务先 /clear |