PowerShell 快捷配置指南
PowerShell 提供多种快捷配置机制,功能更强大。
PowerShell 提供多种快捷配置机制,功能更强大。
graphify 是一款面向代码仓库的知识图谱生成工具,能够分析项目代码结构、调用关系,生成可视化图谱,并与 AI 编码助手(Claude Code)深度集成,实现智能上下文理解与自动增量更新。
在 AI 辅助开发场景下,前端与后端往往需要同时查看和修改、联动调试。将前后端拆分为独立 Git 仓库,再通过子模块组合成一个父仓库,可以让 AI 工具(如 CodeMaker)在同一工作区同时感知前后端代码上下文,无需频繁切换目录,显著提升 AI 开发效率。
父仓库 = 只包含 .gitmodules 和 openspec 配置,不存业务代码
子模块 = 前端仓库 + 后端仓库,各自独立 CI/CD 和权限管理
AI 工具 打开父仓库根目录,即可同时索引前后端代码,实现跨仓库联动分析和修改
以下是本项目的 .gitmodules 配置,包含前端和后端两个子模块,均追踪 master 分支:
[submodule "xxx-platform"]
path = xxx-platform
url = ssh://git@git.com:10022/xxx/xxx/xxx-platform.git
branch = master
[submodule "xxx-react"]
path = xxx-react
url = ssh://git@git.com:10022/xxx/xxx/xxx-react.git
branch = master
| 子模块 | 类型 | 说明 |
|---|---|---|
| xxx-platform | 后端 | Spring Boot 多模块 Maven 项目 |
| xxx-react | 前端 | React + TypeScript 项目 |
# 首次克隆父仓库(同时拉取前后端子模块)
git clone --recurse-submodules ssh://git@git.com:10022/xxx/xxx/xxx-all.git
# 已克隆但子模块为空时
git submodule update --init --recursive
# 日常拉取(前后端一起更新)
git submodule foreach git pull origin master
# 添加子模块
git submodule add ssh://git@git.com:10022/xx/xx/xx-xx.git
用 AI 工具打开 xxx-all 根目录,即可同时获得前端(xxx-react/)和后端(xxx-platform/)的完整代码上下文,实现前后端联动开发。
Git 子模块允许将一个 Git 仓库嵌套到另一个 Git 仓库中,作为其子目录存在。主仓库只记录子模块的仓库地址和特定 commit SHA,不追踪子模块的具体文件内容。
适用场景:共享公共库、第三方依赖管理、多项目协同开发。
git submodule add <repo-url> [本地路径]
# 示例
git submodule add https://github.com/example/lib.git libs/lib
执行后会生成 .gitmodules 文件,记录子模块配置。
# 一步完成(推荐)
git clone --recurse-submodules <repo-url>
# 已克隆但未拉取子模块时补救
git submodule update --init --recursive
git submodule status
输出格式:[-/+/空格][SHA] 路径 (分支/标签),前缀含义:
空格 — 已初始化且与主仓库记录一致- — 尚未初始化+ — 子模块 HEAD 与主仓库记录的 commit 不同# 更新所有子模块到主仓库记录的 commit
git submodule update --recursive
# 拉取子模块远程最新(不受主仓库 commit 约束)
git submodule update --remote --recursive
git pull
git submodule update --init --recursive
执行 git pull 后若子模块有变更,必须手动运行 submodule update,否则子模块仍停在旧 commit。
# 对所有子模块执行任意 git 命令
git submodule foreach git pull origin main
git submodule foreach git status
cd libs/lib
git checkout main # 子模块默认是 detached HEAD,先 checkout 分支
# ... 修改代码 ...
git add .
git commit -m "feat: xxx"
git push
cd ../../ # 回到主仓库根目录
git add libs/lib
git commit -m "chore: update submodule lib to latest"
git push
子模块的 commit 必须先推送到远程,主仓库才能让其他人 checkout 到该 commit。先推子模块,再推主仓库。
Git 没有一键删除命令,需要手动四步:
.gitmodules 文件中删除对应配置段.git/config 文件中删除对应配置段git rm --cached .git/modules/ 目录# 快捷脚本(以 libs/lib 为例)
git submodule deinit -f libs/lib
git rm -f libs/lib
rm -rf .git/modules/libs/lib
git commit -m "chore: remove submodule lib"
# 每次 git pull 自动更新子模块(Git 2.15+)
git config --global submodule.recurse true
git clone --recurse-submodules --shallow-submodules <repo-url>
只拉取子模块最新 1 个 commit,大幅减少 CI/CD 场景的拉取时间。
# 在 .gitmodules 中设置追踪分支
git submodule set-branch --branch main libs/lib
git submodule update --remote libs/lib
git diff --submodule=diff
默认 git diff 只显示 commit SHA 变化,加 --submodule=diff 可展示子模块内的文件级变更。
git submodule update --init libs/lib
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
| 子模块目录为空 | 未执行 init/update | git submodule update --init --recursive |
| 子模块处于 detached HEAD | update 默认不 checkout 分支 | 进入目录执行 git checkout |
| push 后别人拉不到子模块新 commit | 子模块未先 push | 先在子模块目录 push,再 push 主仓库 |
| clone 后子模块内容缺失 | 未加 --recurse-submodules | 执行 git submodule update --init --recursive |
| 子模块 commit 冲突 | 多人同时更新同一子模块引用 | 像解决普通文件冲突一样,手动选择正确的 commit SHA |
克隆:git clone --recurse-submodules
补救初始化:git submodule update --init --recursive
更新到主仓库记录版本:git submodule update --recursive
拉取子模块最新:git submodule update --remote --recursive
批量操作:git submodule foreach <命令>
删除子模块:git submodule deinit -f → git rm -f → 删 .git/modules/
自动同步配置:git config --global submodule.recurse true
基于 ai-knowledge 项目实际升级经验整理,适用于团队其他 Spring Boot 项目参考。
在 SpringBoot 项目中,集成测试(Integration Test)依赖真实数据库、外部服务或网络资源。一旦引入 CI 流水线,这类测试极容易因环境不具备而导致构建失败。
常见的两种处理手段是:用 @Disabled 直接禁用,或用 @Tag 分组后在 CI 中排除。两者看起来都能解决问题,但背后的设计理念截然不同,适用场景也有明显差异。
本文从实际使用角度出发,系统梳理两种方案的用法、优劣以及如何根据项目情况做出合理取舍。
@Disabled 是 JUnit 5 提供的注解,标注后该测试类或测试方法将被跳过,不会执行。
禁用整个测试类:
import org.junit.jupiter.api.Disabled;
import org.springframework.boot.test.context.SpringBootTest;
@Disabled("依赖真实数据库,CI 环境无法运行")
@SpringBootTest
class UserServiceIntegrationTest {
@Test
void testQueryUser() {
// 不会执行
}
}
禁用单个测试方法:
@SpringBootTest
class OrderServiceTest {
@Test
void testCreateOrder() {
// 正常运行
}
@Disabled("依赖外部支付接口,暂时禁用")
@Test
void testPayOrder() {
// 不会执行
}
}
被 @Disabled 标注的测试在报告中会显示为 SKIPPED,不算失败,构建不会中断。
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 1
@Disabled("原因说明") 支持附带说明文字,方便代码审查时理解意图。@Disabled 的测试也不会自动运行,需要手动移除注解。@Disabled 只传达「跳过」的意思,并不表达这个测试属于哪一类、为什么需要特殊处理。@Tag 的思路不是「禁用」,而是「分组」。给测试打上标签,在不同场景下选择运行哪些分组,本质上是一种测试分类管理机制。
集成测试依然存在、依然有效,只是在 CI 环境中被选择性跳过。
给测试类打标签:
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@Tag("integration")
@SpringBootTest
class UserServiceIntegrationTest {
@Test
void testQueryUser() {
System.out.println(userService.findById(1L));
}
}
给单个方法打标签:
@SpringBootTest
class OrderServiceTest {
@Test
void testCreateOrder() {
// 单元级别,不需要标签
}
@Tag("integration")
@Test
void testPayOrder() {
// 依赖外部接口,标记为集成测试
}
}
仅有注解本身没有意义,需要在构建工具中配置如何响应标签。
在 pom.xml 中默认排除 integration 标签(CI 环境安全运行):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- 默认构建跳过集成测试 -->
<excludedGroups>integration</excludedGroups>
</configuration>
</plugin>
添加 profile,支持本地按需运行集成测试:
<profiles>
<profile>
<id>integration-test</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- 清除默认排除规则,只跑 integration 标签 -->
<excludedGroups/>
<groups>integration</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
不同场景对应的命令:
# CI / 日常构建:跳过集成测试
mvn test
# 本地开发:只跑集成测试
mvn test -P integration-test
# 本地开发:运行所有测试(含集成测试)
mvn test -Dgroups=integration -DexcludedGroups=
// build.gradle
test {
// 默认排除 integration 标签
useJUnitPlatform {
excludeTags 'integration'
}
}
task integrationTest(type: Test) {
useJUnitPlatform {
includeTags 'integration'
}
}
@Tag 支持在一个类上打多个标签,实现更细粒度的分组:
@Tag("integration")
@Tag("slow") // 标记为慢速测试,可在 PR 检查中也跳过
@Tag("database") // 标记依赖数据库,方便按资源类型过滤
@SpringBootTest
class ReportServiceTest {
// ...
}
在 Maven 中可以按需组合:
# 只跑数据库相关的集成测试
mvn test -Dgroups="integration & database" -DexcludedGroups=
# 跳过慢速测试(包括集成测试)
mvn test -DexcludedGroups=slow
@Tag("integration") 明确表达了测试的分类属性,代码自解释能力强。@Tag 的本意就是解决测试分类问题,这是官方推荐的做法。@Tag 注解没有效果,必须在 Maven 或 Gradle 中正确配置排除规则。@Disabled 大。| 维度 | @Disabled |
@Tag("integration") |
|---|---|---|
| 实现复杂度 | 极低,一个注解搞定 | 中等,需要注解 + 构建配置 |
| 测试是否可运行 | ❌ 永久禁用 | ✅ 本地 / 按需可运行 |
| CI 构建安全 | ✅ | ✅ |
| 测试代码价值 | ❌ 等同死代码 | ✅ 保持有效 |
| 语义表达能力 | 弱(只表达「跳过」) | 强(表达「分类」) |
| 长期可维护性 | 差(容易被遗忘) | 好(标签系统可持续演进) |
| 团队协作成本 | 低(不需要约定) | 中(需要团队统一规范) |
| 适合场景 | 临时禁用、功能未完成 | 环境依赖型测试的长期管理 |
测试代码本身有问题,需要暂时禁用等待修复,属于短暂的「技术债标记」。建议同时创建 Issue 或 TODO 注释,避免遗忘:
// TODO: ISSUE-1234 修复后移除 @Disabled
@Disabled("等待依赖服务 API 变更,预计下个迭代修复")
@Test
void testExternalApiIntegration() { }
测试功能尚未实现,先写好测试框架占位,等实现完成后再启用。
一次性探索性测试,本身没有长期维护价值,禁用比删除更保留上下文。
集成测试数量较多,且长期需要维护,希望区分「单元测试」和「集成测试」的执行策略。
CI 流水线有分层需求:PR 提交时只跑轻量级单元测试,合并主干后再跑完整集成测试。
团队有明确的测试分类规范,希望通过标签系统实现精细化的测试治理。
项目质量要求较高,需要保证集成测试代码始终有效、随主代码同步更新。
临时的问题用
@Disabled,长期的分类用@Tag。
如果你预期一个测试「以后还会运行」,就不应该用 @Disabled。如果你预期一个测试「在某些环境下不能运行」,就应该用 @Tag 配合构建配置来管理。
推荐在团队内制定标签命名约定,例如:
| 标签 | 含义 |
|---|---|
integration |
依赖真实数据库或外部服务 |
slow |
执行时间超过 1 秒的测试 |
smoke |
冒烟测试,每次部署必须通过 |
flaky |
不稳定测试,已知偶发失败 |
对于需要按顺序执行的集成测试场景,可以结合 @TestMethodOrder 使用:
@Tag("integration")
@SpringBootTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class UserFlowIntegrationTest {
@Test
@Order(1)
void testRegister() { }
@Test
@Order(2)
void testLogin() { }
}
# .github/workflows/ci.yml 示例
jobs:
unit-test:
name: 单元测试(快速验证)
runs-on: ubuntu-latest
steps:
- run: mvn test # excludedGroups=integration 已在 pom.xml 配置
integration-test:
name: 集成测试(合并主干后执行)
runs-on: ubuntu-latest
needs: unit-test
if: github.ref == 'refs/heads/main'
steps:
- run: mvn test -P integration-test
@Disabled 和 @Tag 并不是非此即彼的关系,它们解决的是不同层面的问题:
@Disabled 是测试状态管理工具,用于标记暂时不可用的测试;@Tag 是测试分类管理工具,用于建立可持续的测试分层体系。对于依赖真实数据库的 SpringBoot 集成测试,@Tag("integration") 配合 Maven Profile 是更符合工程实践的选择。它让测试代码保持活力,同时给 CI 环境提供安全屏障,是一种「有取有舍」而非「一刀切」的解决方案。
最后,无论选择哪种方案,最重要的是团队内保持一致的约定,并在代码审查中严格执行。好的测试策略不是写在文档里的规范,而是体现在每一行代码注解里的默契。