HOWTO · Git

在 Git 中删除子模块

本教程是关于在 Git 中删除子模块

本页内容

在 Git 中删除子模块,需要在超级项目的根目录运行两条命令:先用 git submodule deinit <path> 在本地注销它,再用 git rm <path> 删除受跟踪的条目并暂存更改。子模块是一个独立的 Git 仓库,超级项目通过索引中的 gitlink 和 .gitmodules 中的一项来跟踪它,因此删除操作会同时触及工作树、索引和该配置文件。

项目经常会把外部库或共享组件作为子模块引入,同时让其历史与主仓库保持独立。当不再需要该依赖时,可按下面的步骤干净地删除它;示例使用名为 good-ext-lib 的子模块。

在 Git 中删除子模块

在超级项目的根目录运行以下命令,并将 path/to/good-ext-lib 替换为子模块在 .gitmodules 中显示的路径:

  • git submodule deinit path/to/good-ext-lib
  • git rm path/to/good-ext-lib

这就是完整的删除过程。正常情况下这两条命令都不需要强制选项:只有在要丢弃子模块内未提交的改动时才给 git submodule deinit 加 -f,只有在要删除仍含有本地修改或已暂存内容的子模块时才给 git rm 加 -f。这两种用法都具有破坏性并会丢弃工作成果,因此只有确实打算这样做时才应使用。

运行下面这个自包含示例,即可在一个临时仓库中尝试完整命令序列(示例中的提示文字和命令输出保持英文原文):

本节中的代码以及显示的标签或输出仍为英文。

set -e
demo=$(mktemp -d)

git init -q -b main "$demo/lib"
git -C "$demo/lib" config user.email demo@example.com
git -C "$demo/lib" config user.name Demo
echo "demo library" > "$demo/lib/README.md"
git -C "$demo/lib" add README.md
git -C "$demo/lib" commit -qm "Initial library commit"

git init -q -b main "$demo/super"
git -C "$demo/super" config user.email demo@example.com
git -C "$demo/super" config user.name Demo
git -C "$demo/super" -c protocol.file.allow=always submodule add -q "$demo/lib" good-ext-lib
git -C "$demo/super" commit -qm "Add good-ext-lib submodule"

git -C "$demo/super" submodule deinit good-ext-lib
git -C "$demo/super" rm -q good-ext-lib
git -C "$demo/super" status --short

该脚本会构建一个小型库仓库,把它作为子模块嵌入一个临时超级项目,然后将其取消初始化并删除;最后一条 status 命令会列出已暂存的结果。要把最后三行 git -C "$demo/super" 应用到真实仓库,请在项目根目录运行相同的子命令——submodule deinit、rm 和 status——但不要带 -C。

了解每条命令会改变什么

git submodule deinit path/to/good-ext-lib 只影响本地克隆。它会从 .git/config 中删除 submodule.good-ext-lib 注册项,并清空该子模块的工作树,但不会暂存任何内容,也不会改动 .gitmodules。

git rm path/to/good-ext-lib 执行的是受跟踪条目的删除:它会删除 gitlink——即记录子模块固定到哪个提交的索引项——从 .gitmodules 中移除该子模块的对应小节,删除工作树目录,并将所有这些更改暂存到下一次提交。

子模块背后真正克隆下来的仓库位于 .git/modules 之下,本例中为 .git/modules/path/to/good-ext-lib。上面两条命令都不会删除它,这正是以相同名称重新添加子模块时可以复用缓存克隆的原因。只有在需要回收空间或强制获得一份全新克隆时才手动删除该目录,并且必须在删除已提交之后进行,因为该操作会永久丢弃本地副本。

验证并提交删除

提交之前,先检查已暂存的内容:git status 会列出被删除的 gitlink 和 .gitmodules 的更改,git diff --cached 则以补丁形式显示同样的修改。然后像提交其他任何更改一样,用 git commit -m "Remove submodule good-ext-lib" 记录这次删除。

提交是唯一会被其他克隆看到的步骤——它是超级项目里表明该子模块已被移除的记录。删除 .git/modules 下残留的元数据纯属本地清理,永远不会进入提交;拉取此更改的克隆会自动停止跟踪该子模块。

延伸阅读

  1. git-submodule
  2. Git 子模块教程