一句话结论

大部分情况下,你只需要在主仓库目录执行:

git submodule update --init --recursive

这会自动初始化、拉取所有子模块,并递归处理嵌套子模块。


1. 刚 clone 完主仓库,子模块目录是空

如果你是:

git clone xxx.git
cd repo
ls 子模块目录/
# 里面是空的

这时 submodule 还没被拉下来,有两种方式:

方法 A:一步到位(推荐)

如果重新 clone 还不晚,建议直接用:

git clone --recurse-submodules xxx.git

这样 clone 主仓库的同时,自动初始化并拉取所有 submodule。

方法 B:已有仓库,再拉 submodule

已经 git clone 完了,执行:

# 在主仓库根目录
git submodule init          # 初始化本地配置
git submodule update        # 拉取并检出子模块到记录的提交

或者一步到位:

git submodule update --init --recursive

--recursive 会一并处理嵌套的子模块(子模块里还有子模块)。


2. 想要「重新拉」子模块到最新提交

注意:Git submodule 默认不会自动跟随远程分支,它只会固定在主仓库记录的那个 commit。

所以「更新到远程最新」分两步:先在子模块里拉取最新代码,再在主仓库更新指向的 commit。

步骤

  1. 在主仓库根目录,先更新子模块到远程最新:

    git submodule update --remote
    

    这会把子模块切换到它自己的远程分支最新提交(默认是 master,可在 .gitmodules 里配置)。

  2. 进入子模块目录,确认并切换到你想要的分支(例如 develop):

    cd 子模块路径
    git checkout develop
    git pull origin develop
    
  3. 回到主仓库,把子模块的当前提交记录进主仓库:

    cd 主仓库根目录
    git add 子模块路径
    git commit -m "chore: update submodule to latest"
    git push
    

之后别人拉取你的主仓库并执行:

git pull
git submodule update --init --recursive

就能得到你刚才更新后的子模块提交。


3. 子模块目录已经乱套,想「完全重来」

如果你遇到:

  • 子模块目录状态异常(冲突、detached HEAD、损坏等)
  • 想彻底重新拉取

可以这样做:

  1. 先删除子模块目录(注意不要误删重要文件):

    # 在主仓库根目录
    rm -rf 子模块目录
    
  2. 然后重新初始化并拉取:

    git submodule deinit -f 子模块路径   # 可选,清理 submodule 缓存
    git submodule update --init --recursive
    

这相当于把子模块重新检出一次。


4. 团队协作时,别人更新了 submodule,你如何同步

典型场景:同事更新了子模块提交并推送到远程,你 git pull 之后,发现 submodule 目录还是旧代码。

正确做法:

git pull
git submodule update --init --recursive

这样会把子模块更新到主仓库现在指向的那个提交。


5. 常见问题

Q:执行 git submodule update --init --recursive 报错怎么办?

  • 先确认网络:子模块仓库可能在你公司内网 / 需要代理。
  • 有可能是某个子模块远程仓库临时挂掉,可以稍后重试。

Q:想只拉某一个 submodule?

git submodule update --init 子模块路径

6. 实际应用示例

以 Hugo 博客项目为例,假设你有一个使用 DoIt 主题的博客:

场景一:刚克隆博客项目

# 克隆博客项目
git clone https://github.com/yourname/blog.git
cd blog

# 初始化并拉取主题子模块
git submodule update --init --recursive

场景二:更新主题到最新版本

# 进入主题目录
cd themes/DoIt

# 拉取最新代码
git checkout main
git pull origin main

# 返回主仓库记录更新
cd ../..
git add themes/DoIt
git commit -m "更新 DoIt 主题到最新版本"
git push

场景三:子模块出现问题时重置

# 清除子模块缓存
git submodule deinit -f themes/DoIt

# 删除子模块目录
rm -rf themes/DoIt

# 重新拉取
git submodule update --init --recursive

总结

Git Submodule 虽然概念简单,但在实际使用中容易出错。记住以下关键点:

  1. 克隆时使用 --recurse-submodules 可以一次性拉取所有子模块
  2. 子模块不会自动跟随远程分支,需要手动更新并提交
  3. 团队协作时要记得同步子模块git submodule update --init --recursive
  4. 遇到问题时可以完全重置:删除子模块目录后重新拉取

掌握这些操作,你就能轻松应对 Git Submodule 的各种使用场景了!