🚀 快速安装

复制以下命令并运行,立即安装此 Skill:

npx @anthropic-ai/skills install supercent-io/skills-template/git-submodule

💡 提示:需要 Node.js 和 NPM

何时使用此技能

  • 在主项目中包含外部 Git 仓库
  • 在多个项目之间管理共享库或模块
  • 将外部依赖锁定到特定版本
  • 使用包含独立组件的 monorepo 风格架构
  • 克隆包含子模块的仓库
  • 将子模块更新到新版本
  • 从项目中移除子模块

操作指南

步骤 1:了解子模块

Git 子模块是一种在主 Git 仓库中包含其他 Git 仓库的功能。

核心概念

  • 子模块通过引用特定提交来锁定版本
  • 子模块的路径和 URL 记录在 .gitmodules 文件中
  • 子模块内的变更作为单独的提交进行管理

步骤 2:添加子模块

基础添加

# 添加子模块
git submodule add <仓库-url> <路径>

# 示例:将库添加到 libs/lib 路径
git submodule add https://github.com/example/lib.git libs/lib

跟踪特定分支

# 添加并跟踪特定分支
git submodule add -b main https://github.com/example/lib.git libs/lib

添加后提交

git add .gitmodules libs/lib
git commit -m "feat: 添加 lib 作为子模块"

步骤 3:克隆包含子模块的仓库

全新克隆时

# 方法 1:克隆时使用 --recursive 选项
git clone --recursive <仓库-url>

# 方法 2:克隆后初始化
git clone <仓库-url>
cd <仓库>
git submodule init
git submodule update

一行命令初始化并更新

git submodule update --init --recursive

步骤 4:更新子模块

更新到最新的远程版本

# 更新所有子模块到最新的远程版本
git submodule update --remote

# 只更新特定的子模块
git submodule update --remote libs/lib

# 更新 + 合并
git submodule update --remote --merge

# 更新 + 变基
git submodule update --remote --rebase

检出到引用的提交

# 将子模块检出到主仓库引用的提交
git submodule update

步骤 5:在子模块内工作

在子模块内工作

# 进入子模块目录
cd libs/lib

# 检出分支(退出分离 HEAD 状态)
git checkout main

# 进行更改
# ... 做出更改 ...

# 在子模块内提交并推送
git add .
git commit -m "feat: 更新库"
git push origin main

在主仓库中反映子模块变更

# 回到主仓库
cd ..

# 更新子模块引用
git add libs/lib
git commit -m "chore: 更新 lib 子模块引用"
git push

步骤 6:批量操作

在所有子模块上运行命令

# 在所有子模块中拉取更新
git submodule foreach 'git pull origin main'

# 检查所有子模块的状态
git submodule foreach 'git status'

# 在所有子模块中检出分支
git submodule foreach 'git checkout main'

# 同时在嵌套子模块上运行命令
git submodule foreach --recursive 'git fetch origin'

步骤 7:移除子模块

完全移除子模块

# 1. 反初始化子模块
git submodule deinit <路径>

# 2. 从 Git 中移除
git rm <路径>

# 3. 从 .git/modules 中移除缓存
rm -rf .git/modules/<路径>

# 4. 提交更改
git commit -m "chore: 移除子模块"

示例:移除 libs/lib

git submodule deinit libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib
git commit -m "chore: 移除 lib 子模块"
git push

步骤 8:检查子模块状态

检查状态

# 检查子模块状态
git submodule status

# 详细状态(递归)
git submodule status --recursive

# 摘要信息
git submodule summary

解读输出

 44d7d1... libs/lib (v1.0.0)      # 正常(匹配引用的提交)
+44d7d1... libs/lib (v1.0.0-1-g...)  # 存在本地更改
-44d7d1... libs/lib               # 未初始化

使用示例

示例 1:向项目添加外部库

# 1. 添加子模块
git submodule add https://github.com/lodash/lodash.git vendor/lodash

# 2. 锁定到特定版本(标签)
cd vendor/lodash
git checkout v4.17.21
cd ../..

# 3. 提交更改
git add .
git commit -m "feat: 添加 lodash v4.17.21 作为子模块"

# 4. 推送
git push origin main

示例 2:克隆包含子模块的仓库后的设置

# 1. 克隆仓库
git clone https://github.com/myorg/myproject.git
cd myproject

# 2. 初始化和更新子模块
git submodule update --init --recursive

# 3. 检查子模块状态
git submodule status

# 4. 检出子模块分支(用于开发)
git submodule foreach 'git checkout main || git checkout master'

示例 3:将子模块更新到最新版本

# 1. 将所有子模块更新到最新的远程版本
git submodule update --remote --merge

# 2. 检查更改
git diff --submodule

# 3. 提交更改
git add .
git commit -m "chore: 将所有子模块更新到最新版本"

# 4. 推送
git push origin main

示例 4:在多个项目间使用共享组件

# 在项目 A 中
git submodule add https://github.com/myorg/shared-components.git src/shared

# 在项目 B 中
git submodule add https://github.com/myorg/shared-components.git src/shared

# 更新共享组件时(在每个项目中)
git submodule update --remote src/shared
git add src/shared
git commit -m "chore: 更新共享组件"

示例 5:在 CI/CD 中处理子模块

# GitHub Actions
jobs:
  build:
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive  # 或 'true'

# GitLab CI
variables:
  GIT_SUBMODULE_STRATEGY: recursive

# Jenkins
checkout scm: [
  $class: 'SubmoduleOption',
  recursiveSubmodules: true
]

高级工作流

嵌套子模块

# 初始化所有嵌套子模块
git submodule update --init --recursive

# 更新所有嵌套子模块
git submodule update --remote --recursive

更改子模块 URL

# 编辑 .gitmodules 文件
git config -f .gitmodules submodule.libs/lib.url https://new-url.git

# 同步本地配置
git submodule sync

# 更新子模块
git submodule update --init --recursive

将子模块转换为普通目录

# 1. 备份子模块内容
cp -r libs/lib libs/lib-backup

# 2. 移除子模块
git submodule deinit libs/lib
git rm libs/lib
rm -rf .git/modules/libs/lib

# 3. 恢复备份(排除 .git)
rm -rf libs/lib-backup/.git
mv libs/lib-backup libs/lib

# 4. 作为普通文件添加
git add libs/lib
git commit -m "chore: 将子模块转换为普通目录"

使用浅克隆节省空间

# 使用浅克隆添加子模块
git submodule add --depth 1 https://github.com/large/repo.git libs/large

# 将现有子模块更新为浅克隆
git submodule update --init --depth 1

最佳实践

  1. 版本锁定:为了可重现性,始终将子模块锁定到特定的提交或标签
  2. 文档说明:在 README 中说明子模块的初始化步骤
  3. CI 配置:在 CI/CD 流水线中使用 --recursive 选项
  4. 定期更新:定期更新子模块以获取安全补丁等
  5. 分支跟踪:为方便开发,在开发期间配置分支跟踪
  6. 权限管理:验证对子模块仓库的访问权限
  7. 浅克隆:对于大型仓库使用 --depth 选项以节省空间
  8. 状态检查:提交前使用 git submodule status 验证状态

常见陷阱

  • 分离 HEAD 状态:子模块默认处于分离 HEAD 状态。工作时请检出分支
  • 缺少初始化:克隆后需要执行 git submodule update --init
  • 引用不匹配:子模块更改后,必须在主仓库中更新引用
  • 权限问题:私有子模块需要配置 SSH 密钥或令牌
  • 相对路径:在 .gitmodules 中使用相对路径可能在复刻时引起问题
  • 移除不彻底:移除子模块时也必须删除 .git/modules 缓存

故障排除

子模块未初始化

# 强制初始化
git submodule update --init --force

子模块冲突

# 检查子模块状态
git submodule status

# 解决冲突后,检出所需的提交
cd libs/lib
git checkout <期望的提交哈希>
cd ..
git add libs/lib
git commit -m "fix: 解决子模块冲突"

权限错误(私有仓库)

# 使用 SSH URL
git config -f .gitmodules submodule.libs/lib.url git@github.com:org/private-lib.git
git submodule sync
git submodule update --init

子模块处于脏状态

# 检查子模块内的更改
cd libs/lib
git status
git diff

# 丢弃更改
git checkout .
git clean -fd

# 或者提交更改
git add .
git commit -m "fix: 解决更改"
git push

配置

有用的配置

# 在 diff 中显示子模块更改
git config --global diff.submodule log

# 在状态中显示子模块摘要
git config --global status.submoduleSummary true

# 推送时检查子模块更改
git config --global push.recurseSubmodules check

# 获取时也获取子模块
git config --global fetch.recurseSubmodules on-demand

.gitmodules 示例

[submodule "libs/lib"]
    path = libs/lib
    url = https://github.com/example/lib.git
    branch = main

[submodule "vendor/tool"]
    path = vendor/tool
    url = git@github.com:example/tool.git
    shallow = true

参考链接

📄 原始文档

完整文档(英文):

https://skills.sh/supercent-io/skills-template/git-submodule

💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。