关于 Git 子模块(submodule)

三葉Leaves Author

这两天做项目遇到了一个 git 仓库嵌套了另一个 git 仓库的问题,此时做项目管理,尤其是用 IDE 结合 git 使用时让我犯了难。
我和 Claude 进行了一些对话,AI 这次发挥的很好,对话大致覆盖了子模块的基本使用和答疑,贴在下面,等有时间整理

回合1:大致介绍

问:

我现在有一个 web 项目(example 文件夹),在example 文件夹其中包含前端(web文件夹)和后端(server 文件夹),前后端分别是从两个不同的作者的仓库 fork 来的,所以未来都有从原作者的仓库选择性合并代码的需求。
现在我的 example 是一个 git 仓库,web 文件夹似乎又是一个 git 仓库,server 似乎又又是一个 git 仓库。当我在 IDE 里把 example 作为项目目录时,IDE似乎只认识 example 这个 git 仓库,这一度使我困惑。
这种情况下,我该怎么去管理我的项目?如果使用git子模块,我在应该在 example 里执行 git 命令,还是 web 文件夹里执行 git 命令,我又该在 IDE 里打开哪个文件夹

答:

我理解您的困扰。这种情况下,使用 Git 子模块(submodule)确实是一个很好的解决方案。让我为您详细说明如何管理:

  1. 推荐的项目结构设置:
1
2
3
4
5
6
7
example/                  # 主仓库
.git/
.gitmodules # 子模块配置文件
web/ # 前端子模块
.git
server/ # 后端子模块
.git
  1. 设置步骤:
1
2
3
4
5
6
7
# 在 example 目录下初始化主仓库(如果还没有的话)
cd example
git init

# 添加子模块
git submodule add <前端仓库URL> web
git submodule add <后端仓库URL> server
  1. Git 命令执行位置:
  • 当您需要对主项目进行操作时,在 example 目录下执行 git 命令
  • 当需要对前端代码进行操作时,在 web 目录下执行 git 命令
  • 当需要对后端代码进行操作时,在 server 目录下执行 git 命令
  1. IDE 使用建议:
  • 建议在 IDE 中打开 example 根目录
  • 现代 IDE(如 VSCode、IntelliJ)都能很好地识别和处理 Git 子模块
  • 在 IDE 中,您可以:
    • 查看每个子模块的 Git 状态
    • 分别对子模块进行 Git 操作
    • 使用 IDE 的 Git 工具对主仓库进行操作
  1. 从原作者仓库更新代码:
1
2
3
4
5
6
7
8
9
10
11
# 在子模块目录下
cd web
git remote add upstream <原作者前端仓库URL>
git fetch upstream
git merge upstream/main # 或其他需要合并的分支

# 对后端也是类似操作
cd ../server
git remote add upstream <原作者后端仓库URL>
git fetch upstream
git merge upstream/main
  1. 克隆包含子模块的项目:
1
2
3
4
5
6
7
# 克隆主项目及其所有子模块
git clone --recursive <example项目URL>

# 或者先克隆主项目,然后初始化子模块
git clone <example项目URL>
cd example
git submodule update --init --recursive

一些建议:

  1. 在主仓库中,建议保存一些项目级的配置文件、说明文档等
  2. 子模块的具体版本信息会被主仓库记录,这样可以确保团队成员看到相同的代码版本
  3. 当需要更新子模块时,记得在主仓库中也提交一次,以记录子模块的新版本

您觉得这样的管理方式是否符合您的需求?如果有任何不清楚的地方,我很乐意为您进一步解释。

回合2:子模块修改

问:

那么假设我现在在 IDE 里把 example 文件夹作为项目的根目录,然后我直接修改了 web 文件夹里的东西,我现在应该进入 web 文件夹执行 git 系列操作,还是在 example 文件夹里执行 git 系列操作,还是该怎么办?

如果我在修改 web 文件夹里的内容后,直接在 example 文件夹里执行 git add .,git commit -m “”,git push ,那会发生什么?

答:

当您在 IDE 中修改了 web 文件夹(子模块)内的文件时,您需要分两步处理:

  1. 首先,进入 web 文件夹处理子模块的变更:
1
2
3
4
cd web
git add .
git commit -m "你的修改说明"
git push
  1. 然后,回到 example 文件夹更新主仓库对子模块的引用:
1
2
3
4
cd ..  # 回到 example 文件夹
git add web # 更新对子模块的引用
git commit -m "更新 web 子模块到最新版本"
git push

原因解释:

  • web 文件夹是一个独立的 Git 仓库,您对其内容的修改需要在该仓库内进行版本控制
  • example 仓库只存储了对 web 子模块特定提交的引用,当 web 子模块有新的提交时,需要更新这个引用

一个实用建议:
如果您经常需要这样操作,可以创建一个 shell 脚本来自动化这个过程,比如:

1
2
3
4
5
6
7
8
9
#!/bin/bash
cd web
git add .
git commit -m "$1"
git push
cd ..
git add web
git commit -m "更新 web 子模块: $1"
git push

如果您在修改 web 子模块后,直接在 example 目录下执行 git add .,实际上不会记录 web 文件夹内部的具体修改。这是因为:

  1. 对于主仓库(example)来说,它只会追踪子模块(web)的提交引用,而不是子模块内部的文件变化
  2. 您执行这些命令后:
    • 修改的文件会保持在"未提交"状态(在 web 子模块中)
    • example 仓库不会记录这些修改
    • 如果这时候去看 git status,您会看到 web 文件夹被标记为 “modified”,但具体修改的文件内容不会被提交

让我用一个具体例子说明:

假设您修改了 web/src/App.js

1
2
3
4
5
6
7
8
9
# 在 example 目录下执行
git status
# 您会看到类似这样的输出:
# modified: web (modified content)

git add .
git commit -m "尝试提交 web 的修改"
git push
# 这次提交实际上什么都没有保存,web 文件夹内的修改仍然存在但未被版本控制系统跟踪

正确的做法仍然是:

  1. 先进入 web 目录提交修改
  2. 然后在 example 目录提交子模块的更新
  • 标题: 关于 Git 子模块(submodule)
  • 作者: 三葉Leaves
  • 创建于 : 2024-12-05 00:00:00
  • 更新于 : 2025-01-05 18:48:33
  • 链接: https://kiss1314.top/489a7a87bc7c/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论