AI 开发中的 Git Submodule 父子仓库模式:前后端分仓管理与协作实践

AI 开发中的 Git Submodule 父子仓库模式:前后端分仓管理与协作实践

当一个项目同时包含前端、后端、接口文档和启动脚本时,团队通常会在两种方案之间选择:

  • 把所有代码放进一个 Monorepo;
  • 保留前后端独立仓库,再用一个父仓库统一组织。

Git Submodule 属于第二种方案。它既能保持前后端仓库独立,又能让父仓库记录一组经过验证的版本组合。

进入 AI 辅助开发阶段后,这种模式又多了一层价值:父仓库可以成为 AI 的任务编排入口,子仓库则是边界清晰的执行单元。

不过,Submodule 也会带来初始化、分支、提交顺序和版本指针等额外复杂度。如果没有明确规则,AI 很容易改错仓库、漏推子仓库,或者只提交一个无法被其他人拉取的父仓库指针。

本文将从项目结构、日常开发、AI 协作和仓库治理四个角度,整理一套可落地的父子仓库管理模式。

!summary 核心结论

  • 父仓库是项目的编排层和版本清单,不应堆放前后端业务代码。
  • 前端、后端子仓库是独立 Git 仓库,分别维护代码、分支、测试和发布。
  • AI 应从父仓库理解全局,但只在任务授权的子仓库中写代码。
  • 跨端需求应先确定接口契约,再分别实现前后端,最后更新父仓库指针。
  • 推送顺序必须是:先推子仓库,再推父仓库

一、父子仓库模式解决什么问题?

假设一个项目由三个部分组成:

text 复制代码
product-workspace/
├── web/       # 前端仓库
├── server/    # 后端仓库
└── docs/      # 父仓库维护的项目文档

团队希望实现下面这些目标:

  1. 前端和后端可以独立开发、发版和控制权限;
  2. 项目仍然有一个统一入口,方便初始化和联调;
  3. 父仓库能够准确记录"哪一个前端版本对应哪一个后端版本";
  4. AI 可以在一个工作区内理解完整业务,但不能随意跨越前后端边界;
  5. 新成员克隆项目后,可以快速获得一套可运行的前后端组合。

Git Submodule 的核心能力正好适合这个场景:

父仓库不保存子仓库的全部代码,而是记录子仓库路径、远程地址,以及当前应使用的 commit。

因此,父仓库本质上更像一份可执行的版本清单


二、整体架构与职责划分

flowchart TD P["父仓库:项目编排与版本组合"] W["web:前端业务仓库"] S["server:后端业务仓库"] D["docs:接口契约与联调文档"] A["AI 开发入口"] A --> P P --> W P --> S P --> D D -. "约束前后端实现" .-> W D -. "约束前后端实现" .-> S

1. 父仓库负责什么?

父仓库适合维护:

  • .gitmodules
  • 前后端子仓库的 commit 指针;
  • 项目总览和架构说明;
  • API 契约、联调约定和环境说明;
  • 一键初始化、启动、检查脚本;
  • 面向 AI 的全局规则,例如根目录 AGENTS.md
  • 一组已经联调验证过的前后端版本组合。

父仓库不适合直接放置:

  • 前端页面和组件业务代码;
  • 后端 Controller、Domain、Repository 等业务实现;
  • 只服务于单个子仓库的测试和构建配置。

2. 前端子仓库负责什么?

前端仓库独立维护:

  • 页面、组件和路由;
  • 状态管理;
  • API Client 和类型定义;
  • 前端单元测试、组件测试和 E2E;
  • 前端构建与发布流程;
  • 前端专属的 AGENTS.md 或项目规则。

3. 后端子仓库负责什么?

后端仓库独立维护:

  • API、Controller 或 BFF;
  • Domain、Service、Repository;
  • 数据库迁移;
  • 权限和鉴权逻辑;
  • 后端测试、构建和部署流程;
  • 后端专属的 AI 开发规则。

4. 父仓库记录的不是"最新版本"

这是理解 Submodule 最重要的一点:

text 复制代码
父仓库记录的是子仓库的一个确定 commit,
而不是自动跟随子仓库远程分支的最新代码。

即使 .gitmodules 中配置了:

ini 复制代码
branch = main

普通的 git submodule update 仍然会检出父仓库记录的 commit。只有显式执行 git submodule update --remote,才会尝试按照配置分支向前更新,并让父仓库出现新的指针变化。


三、推荐的目录结构

text 复制代码
product-workspace/
├── .gitmodules
├── AGENTS.md
├── README.md
├── docs/
│   ├── architecture.md
│   ├── api-contract.md
│   └── integration-checklist.md
├── scripts/
│   ├── bootstrap.sh
│   ├── dev.sh
│   └── status.sh
├── web/                 # Git Submodule
│   ├── AGENTS.md
│   ├── package.json
│   └── src/
└── server/              # Git Submodule
    ├── AGENTS.md
    ├── pom.xml
    └── src/

对应的 .gitmodules 可以是:

ini 复制代码
[submodule "web"]
  path = web
  url = git@example.com:team/product-web.git
  branch = main

[submodule "server"]
  path = server
  url = git@example.com:team/product-server.git
  branch = main

!tip 地址选择 团队成员认证方式一致时,建议统一使用 SSH 或 HTTPS,不要在 .gitmodules 中混用个人账号、Token 或带凭证的 URL。


四、初始化与状态识别

1. 推荐的克隆方式

第一次获取项目时,直接递归克隆:

bash 复制代码
git clone --recurse-submodules <父仓库地址>

如果已经普通克隆了父仓库,再补充执行:

bash 复制代码
git submodule update --init --recursive

这条命令会:

  1. 读取 .gitmodules
  2. 初始化尚未注册的子模块;
  3. 下载子仓库;
  4. 检出父仓库记录的 commit;
  5. 递归初始化更深层的子模块。

2. 为什么子目录是空的?

普通执行:

bash 复制代码
git clone <父仓库地址>

只会获取父仓库。此时 webserver 可能只是空的占位目录,并不代表代码丢失。

先检查:

bash 复制代码
git submodule status

常见状态前缀:

前缀 含义
- 子模块尚未初始化
+ 当前子模块 commit 与父仓库记录不一致
空格 子模块与父仓库记录一致

3. 一次查看父子仓库状态

bash 复制代码
git status --short --branch
git submodule status --recursive
git -C web status --short --branch
git -C server status --short --branch

也可以批量检查:

bash 复制代码
git submodule foreach --recursive 'git status --short --branch'

这组命令非常适合放进 AI 开发任务的第一步。AI 在改代码前,应该先确认:

  • 父仓库当前分支;
  • 两个子仓库当前分支;
  • 是否处于 detached HEAD;
  • 是否存在用户未提交的改动;
  • 子模块指针是否已经偏离父仓库记录。

五、AI 开发中的仓库边界

传统开发中,人通常知道自己正在修改哪个仓库;AI 却容易把整个目录树视为一个项目。

如果没有明确规则,可能出现这些问题:

  • 前端需求顺手修改后端实现;
  • 后端字段变化后直接在前端硬编码兼容逻辑;
  • 在父仓库提交,却遗漏子仓库里的真实代码;
  • 在 detached HEAD 上产生无法追踪的 commit;
  • 将多个仓库的用户已有改动一起覆盖或提交;
  • 子仓库 commit 没有推送,父仓库却已经更新指针。

因此,AI 开发应该采用"全局理解、局部写入"的原则。

1. 全局理解

AI 从父仓库读取:

  • 项目由哪些仓库组成;
  • 前后端如何通信;
  • 启动和测试命令;
  • 接口契约在哪里;
  • 哪些修改属于跨仓库变更;
  • 提交和推送顺序。

2. 局部写入

根据任务类型限制写入范围:

任务类型 默认允许修改 默认禁止修改
前端页面或交互 web/ server/
后端接口内部实现 server/ web/
API 字段调整 契约文档、server/web/ 无关模块
联调脚本和版本组合 父仓库 docs/scripts/、子模块指针 子仓库业务代码

!warning 不要让 AI 自行扩大范围 如果需求原本只要求前端适配,AI 不应因为"后端改起来更方便"就修改后端。跨仓库变更必须在计划中明确列出影响和验证方式。


六、用 AGENTS.md 告诉 AI 如何工作

父仓库根目录可以放置一份全局 AGENTS.md,描述仓库地图和协作规则。

示例:

md 复制代码
# 项目仓库说明

本项目由一个父仓库和两个 Git Submodule 组成:

- `web/`:前端仓库,负责页面、组件、状态和 API Client。
- `server/`:后端仓库,负责 API、业务领域、数据访问和数据库迁移。
- 父仓库:只负责文档、脚本和子模块版本组合。

## 修改边界

- 前端需求默认只修改 `web/`。
- 后端需求默认只修改 `server/`。
- 涉及接口字段时,先更新接口契约,再分别修改后端和前端。
- 不覆盖其他仓库中的用户已有改动。
- 不自动部署,不主动提交或推送。

## 开始任务前

1. 检查父仓库和两个子仓库的分支与工作区状态。
2. 确认是否存在 detached HEAD。
3. 明确本次任务需要修改哪些仓库。
4. 跨仓库修改必须列出实现顺序和验证计划。

## 验证

- 前端运行类型检查、相关测试和构建。
- 后端运行相关测试和构建。
- 跨端需求完成后执行联调验证。

## 提交顺序

先提交并推送子仓库,再提交父仓库中的子模块指针。

然后在 web/AGENTS.mdserver/AGENTS.md 中分别补充技术栈、架构边界、测试命令和目录约定。

这种分层有两个好处:

  1. 父仓库规则负责全局协作;
  2. 子仓库规则负责具体技术实现。

七、分支管理策略

1. 避免直接在 detached HEAD 上开发

执行 git submodule update 后,子模块通常会处于 detached HEAD,因为它检出的是一个确定 commit,而不是本地分支。

开始开发前应进入子仓库并切换分支:

bash 复制代码
git -C web switch main
git -C server switch main

开发新功能时,推荐父仓库和相关子仓库使用同名分支:

bash 复制代码
git switch -c feat/user-profile
git -C web switch -c feat/user-profile
git -C server switch -c feat/user-profile

同名分支不是 Git Submodule 的强制要求,但能明显降低沟通和联调成本。

2. 不是所有任务都要创建三个分支

如果任务只涉及前端,可以:

text 复制代码
父仓库:保持当前集成分支,或创建对应编排分支
web:创建 feat/user-profile
server:保持原有稳定分支,不产生修改

关键不是分支数量一致,而是父仓库最终记录的版本组合必须清晰、可解释。

3. AI 开始开发前的分支确认

建议让 AI 明确输出一张影响表:

仓库 当前分支 是否修改 目标分支
父仓库 main feat/user-profile
web main feat/user-profile
server main feat/user-profile

这样可以在真正写代码前发现分支不一致、未提交改动和范围误判。


八、跨前后端需求的推荐执行顺序

以"新增用户资料字段"为例,推荐按下面的顺序处理。

第一步:定义接口契约

先明确:

  • 请求和响应字段;
  • 字段类型、是否必填和默认值;
  • 错误码;
  • 权限要求;
  • 旧客户端兼容策略;
  • 是否涉及数据库迁移。

契约可以放在父仓库 docs/api-contract.md,也可以来自 OpenAPI 等独立契约源。

第二步:实现后端

后端依次处理:

  1. 数据库和领域模型;
  2. Repository 或数据访问层;
  3. Domain、Service 或业务逻辑;
  4. Controller、BFF 或 API;
  5. 后端自动化测试;
  6. 接口文档或 Schema 更新。

第三步:实现前端

前端依次处理:

  1. API 类型和请求封装;
  2. 表单或展示组件;
  3. loading、empty、error 和 disabled 状态;
  4. 表单回填和提交参数;
  5. 组件测试或页面测试;
  6. 浏览器联调。

第四步:联调验证

至少确认:

  • 前端请求字段与契约一致;
  • 后端返回值与前端类型一致;
  • 空值和错误分支可用;
  • 旧数据可以正常展示;
  • 前后端分别通过自己的测试和构建。

第五步:形成版本组合

前后端验证完成后,分别提交子仓库,再由父仓库记录两个新 commit。

text 复制代码
接口契约
   ↓
后端实现与测试
   ↓
前端适配与测试
   ↓
联调验证
   ↓
推送 server
   ↓
推送 web
   ↓
更新并推送父仓库指针

九、正确的提交与推送顺序

子模块里的业务改动必须在子仓库中提交。

1. 提交后端

bash 复制代码
git -C server status
git -C server add <本次相关文件>
git -C server commit -m "feat: 增加用户资料字段"
git -C server push -u origin feat/user-profile

2. 提交前端

bash 复制代码
git -C web status
git -C web add <本次相关文件>
git -C web commit -m "feat: 支持编辑用户资料"
git -C web push -u origin feat/user-profile

3. 提交父仓库

子仓库成功推送后,再回到父仓库:

bash 复制代码
git status
git diff --submodule=log
git add web server docs/api-contract.md
git commit -m "chore: 更新用户资料功能前后端版本"
git push -u origin feat/user-profile

父仓库中的 webserver 并不是普通文件变更,而是 Gitlink 指针变化。

!danger 为什么必须先推子仓库? 如果父仓库先推送,而子仓库的新 commit 只存在于你的本地,其他人拉取父仓库后会得到"找不到目标 commit"的错误。这类父仓库提交是不可复现的。


十、拉取更新的正确方式

1. 获取父仓库已经确认的版本组合

bash 复制代码
git pull --ff-only
git submodule sync --recursive
git submodule update --init --recursive

这会让子仓库回到父仓库记录的 commit,适合部署、测试和复现环境。

2. 主动跟随子仓库远程分支

bash 复制代码
git submodule update --remote --merge web server

这条命令会尝试把子模块更新到 .gitmodules 配置分支的远程版本,并让父仓库产生新的指针变化。

因此,它不应该作为所有场景下的无脑初始化命令。只有当你明确要"升级子模块版本"时才使用,并在升级后完成测试和父仓库提交。


十一、AI 任务提示词模板

1. 前端单仓任务

text 复制代码
这是一个父仓库 + Git Submodule 项目。

本次任务只修改 web 前端子仓库,不修改 server 和父仓库业务配置。
开始前检查父仓库与 web 的分支和工作区状态,保留用户已有改动。
完成后运行前端相关测试、类型检查和构建,不提交、不推送、不部署。

2. 后端单仓任务

text 复制代码
本次需求只修改 server 后端子仓库。

请保留现有 Controller、Domain、Repository 和 Adapter 边界。
不要为了配合实现而修改 web。
完成后运行相关后端测试与构建,并报告是否需要前端后续适配。

3. 跨前后端任务

text 复制代码
这是一个跨 web 与 server 的功能需求。

请先检查父仓库和两个子仓库的状态,然后给出:
1. 接口契约变化;
2. 后端实现范围;
3. 前端适配范围;
4. 测试与联调计划;
5. 三个仓库的分支和提交顺序。

确认范围后再实现。分别验证前端和后端,最后检查父仓库的 Submodule 指针变化。
不要自动提交、推送或部署。

十二、常见问题与事故模式

问题 原因 处理方式
webserver 是空目录 子模块未初始化 git submodule update --init --recursive
子仓库显示 detached HEAD 按父仓库记录的 commit 检出 开发前切换或创建本地分支
父仓库显示 modified content 子仓库存在未提交文件 进入对应子仓库检查 git status
父仓库显示 new commits 子仓库 HEAD 与记录指针不同 确认是否需要提交新的指针
其他人无法拉取子模块 commit 子仓库 commit 没有推送 先推子仓库,再推父仓库
AI 只提交了父仓库 误把 Submodule 当普通目录 分别在子仓库提交真实代码
拉取父仓库后子仓库没更新 只执行了 git pull 再执行 git submodule update --init --recursive
branch = main 但代码没到最新 父仓库仍固定 commit 需要时显式执行 update --remote 并提交指针
前后端分支对不上 多仓库分支没有统一管理 开始任务前输出仓库影响表
切换父仓库分支后代码混乱 子模块指针没有同步 执行 git submodule update --init --recursive

十三、什么时候不适合使用 Submodule?

Submodule 并不是前后端拆分的唯一答案。

下面这些场景可能更适合 Monorepo:

  • 前后端几乎每个需求都必须原子提交;
  • 共享类型和公共包修改非常频繁;
  • CI 必须对全部模块做统一依赖分析;
  • 团队很难接受多仓库分支和提交管理;
  • 发布流程要求一个 commit 同时表示全部业务代码变化;
  • AI 经常需要执行大规模、跨前后端的同步重构。

下面这些场景更适合 Submodule:

  • 前后端仓库需要独立权限和生命周期;
  • 两端大多数需求可以独立交付;
  • 父仓库主要承担客户交付、集成测试或环境编排;
  • 一个后端需要服务多个前端;
  • 团队需要长期保留经过验证的版本组合;
  • 不同子仓库有独立 CI、发布节奏和负责人。

选择的关键不是"哪个方案更先进",而是跨仓库变更频率、团队边界和发布模型。


十四、团队落地检查清单

初始化

  • 父仓库存在正确的 .gitmodules
  • 新成员使用 --recurse-submodules 克隆
  • 启动脚本会检查子模块是否初始化
  • 子模块 URL 不包含个人凭证

开发

  • 开始任务前检查父子仓库分支和工作区
  • 不在 detached HEAD 上直接开发
  • 明确本次任务影响哪些仓库
  • 跨端需求先更新接口契约
  • 前后端分别运行自己的测试和构建

AI 协作

  • 父仓库有全局 AGENTS.md
  • 子仓库有各自的技术栈和验证规则
  • AI 默认只修改任务授权的仓库
  • AI 不覆盖其他仓库中的用户已有改动
  • AI 不自动部署或扩大修改范围

提交与交付

  • 子仓库代码分别提交
  • 子仓库 commit 已成功推送
  • 父仓库只记录可访问的子模块 commit
  • 使用 git diff --submodule=log 检查版本变化
  • 最终版本组合已经完成前后端联调

总结

Git Submodule 父子仓库模式的价值,不只是"把多个仓库放进一个目录"。

它真正建立的是三层关系:

  1. 父仓库负责组织和版本组合
  2. 子仓库负责独立业务实现和交付
  3. 接口契约负责连接前后端边界

在 AI 开发中,父仓库还可以承担第四个角色:为 AI 提供全局上下文、任务边界和协作规则。

只要团队始终遵守下面四条原则,Submodule 就能保持稳定:

text 复制代码
先检查仓库状态
先明确修改边界
先推子仓库
最后更新父仓库指针

反过来,如果团队长期忽略 detached HEAD、分支对应关系和子模块指针,Submodule 就会从版本治理工具变成额外负担。

因此,是否采用父子仓库模式并不取决于命令是否复杂,而取决于团队能否把仓库边界、AI 规则和交付顺序固化成稳定流程。

相关推荐
牧艺2 小时前
cos-design WeatherBackground:用 Canvas 做一个「会变天」的背景引擎
前端·canvas·视觉设计
OpenTiny社区2 小时前
深度解析 LSP 如何为 AI 装上“眼睛”
前端·ai编程
布列瑟农的星空2 小时前
流程类SVG画布的通用开发范式
前端
fsssb2 小时前
Chromium 源码学习笔记(七):那些跨进程的调用,底下都是同一个东西——Mojo
前端
MichaelJohn3 小时前
从零星白屏到“启发式缓存”,记录一次刚接手屎山的惊险排查
前端
程序员黑豆3 小时前
鸿蒙应用开发:6种图片加载方式详解
前端·华为·harmonyos
半个落月3 小时前
用 React 搭一个 WebGPU 模型加载页:从状态驱动到可复用进度条
前端·react.js
雪隐4 小时前
个人电脑玩AI-13让5060 Ti给你打工——我用 0.9B 小模型终结了"谁来记会议纪要"这个世纪难题
前端·人工智能·后端
橘子星4 小时前
我一个前端切图仔,凭什么能在浏览器里跑大模型?
前端·javascript·前端框架