如何移除一个子模块?

4473

如何删除Git子模块?为什么不能直接使用git submodule rm module_name命令?


15
那其实不是真的。那个回答没有解决从.git/config中删除子模块条目的问题。被采纳的回答展示了完全移除子模块的最新方法。这个回答也在这里更简洁地解释了:https://dev59.com/QHM_5IYBdhLWcg3wt1k0#36593218 - fvgs
我发现这篇文章在删除子模块方面非常有帮助。它包括有关删除 .gitsubmodules 和 .git/config 文件中条目的信息 链接 - Ri_
22
请直接前往适用于2017年的答案,以节省您的时间:https://dev59.com/QHM_5IYBdhLWcg3wt1k0#36593218。 - Vincenzo Pii
我为子模块问题苦斗了两天。突破口在于我找到了这个链接:https://forums.developer.apple.com/thread/13102。基本上,Xcode和其他应用程序很难扩展包含“~”的URL。一旦我将ssh://username@server.remoteHost.com/~/git/MyRepo.git更改为ssh://username@server.remoteHost.com/home/username/git/MyRepo.git(查找您服务器上的实际路径),所有奇怪的问题都在十分钟内消失了。另请参见https://dev59.com/2o_ea4cB1Zd3GeqPQJJZ#33985629。 - Elise van Looij
38个回答

4544

在现代的 git 中(本文是在2022年编写,使用更新的 git 版本),这变得更加简单:

  • 运行git rm <path-to-submodule>并提交

这将删除 <path-to-submodule> 下的文件树以及子模块在 .gitmodules 文件中的条目。也就是说,你的代码库中所有与子模块相关的痕迹都会被删除。

然而,正如文档中所述,子模块的 .git 目录仍然会被保留(在主项目的 .git 目录下的 modules/ 目录中),以便可以在不需要从其他代码库中获取历史提交记录时回滚到过去的版本。
如果你仍想移除这些信息,则需手动删除 .git/modules/ 目录下的子模块目录,并在文件 .git/config 中删除子模块的条目。这些步骤可以通过以下命令自动化完成:

  • rm -rf .git/modules/<path-to-submodule>
  • git config --remove-section submodule.<path-to-submodule>


旧版社区维基教程:

通过页面 Git 子模块教程 中的说明:

要删除子模块,需要执行以下步骤:

  1. .gitmodules 文件中删除相应的条目。
  2. 将更改提交至暂存区:
    git add .gitmodules
  • .git/config文件中删除相关部分。
  • 从工作目录和索引中删除子模块文件:
    git rm --cached 子模块路径(不要加尾部斜杠)
  • 删除子模块的.git目录:
    rm -rf .git/modules/子模块路径
  • 提交更改:
    git commit -m“移除子模块 <name>”
  • 删除未跟踪的子模块文件:
    rm -rf 子模块路径
  • 另请参阅: 下面的替代步骤


    505
    顺便问一下,我为什么不能简单地使用“git submodule rm”命令删除某个子模块? - abernier
    53
    一个简短的回答可能是“因为没有这样的命令”。我猜他们试图明确子模块文件与子模块配置的删除,以避免意外数据丢失。也许有人会认为git submodule rm只是删除子模块注册,并会惊讶于该命令还会删除本地存储库。任何本地更改都将无法恢复。另一个人可能认为只有文件被删除。 - John Douthat
    140
    坦白说,我不知道为什么。虽然我希望他们能添加一个命令。这四个步骤太复杂了。 - John Douthat
    27
    这是一个删除子模块的Bash脚本,只需要为submodule-rm创建一个git别名即可 ;) https://gist.github.com/2491147 - Capi Etheriel
    35
    您需要删除 rm -rf .git\modules\子模块名称 的内容吗?我会将其翻译为:您是否需要删除 rm -rf .git\modules\子模块名称 的文件或目录?请注意,这是一个命令行指令,如果您不了解如何使用它,请谨慎操作。 - rogerdpack
    显示剩余34条评论

    2637
    git1.8.3(2013年4月22日) 起:

    之前,没有一种简单的方法来表达“我不再对这个子模块感兴趣”,一旦你通过 "git submodule init" 表示了你的兴趣。
    "git submodule deinit" 就是这样的方式。

    删除过程还使用了 git rm (自 git1.8.5 于2013年10月起)。

    总结

    因此,三步删除过程如下:

    0. mv a/submodule a/submodule_tmp
    
    1. git submodule deinit -f -- a/submodule    
    2. rm -rf .git/modules/a/submodule
    3. git rm -f a/submodule
    # Note: a/submodule (no trailing slash)
    
    # or, if you want to leave it in your working tree and have done step 0
    3.   git rm --cached a/submodule
    3bis mv a/submodule_tmp a/submodule
    

    解释

    rm -rf:这在Daniel Schroederanswer中提到,并由Eonilthe comments中总结:

    这不会更改.git/modules/<path-to-submodule>/。因此,如果您使用此方法删除子模块并再次添加它们,则将无法进行操作,因为存储库已经被损坏。


    git rm:请参阅commit 95c16418

    目前在子模块上使用 "git rm" 命令会将子模块的工作树从超级项目中移除,并从索引中移除 gitlink。
    但是,.gitmodules 文件中子模块的部分不会被修改,这是已删除子模块的残留物,可能会让用户感到困惑(与 .git/config 中的设置相反,这必须保留作为提醒,表明用户对此子模块有兴趣,因此当检出旧提交时,它将重新填充)。

    让 "git rm" 帮助用户,不仅从工作树中删除子模块,还从 .gitmodules 文件中删除 "submodule.<submodule name>" 部分,并将其暂存。


    git submodule deinit: 它源于 此补丁

    使用 "git submodule init",用户可以告诉 Git 他们关心一个或多个子模块,并希望在下一次调用 "git submodule update" 时填充它。
    但是目前没有简单的方法可以告诉 Git 不再关心子模块,并希望摆脱本地工作树(除非用户对子模块内部有很多了解,并从 .git/config 中删除 "submodule.$name.url" 设置以及工作树)。

    帮助那些用户提供一个“deinit”命令。这将.git/config中删除整个submodule.<name>部分,无论是针对给定的子模块(如果给出“.”则为所有已初始化的子模块),还是对于所有已初始化的子模块。除非强制执行,否则在当前工作树包含修改时会失败。当命令行上给定的子模块的url设置在.git/config中找不到时,会发出警告,但仍不会失败。

    这将处理(取消)初始化步骤(.git/config.git/modules/xxx

    自git1.8.5以来,git rm也会处理:

    • 'add' 步骤会在 .gitmodules 文件中记录子模块的url: 需要将其删除。
    • 子模块 特殊条目 (如 此问题 所示): git rm 会从索引中移除它:
      git rm --cached path_to_submodule (没有斜杠)
      这将删除存储在索引中的目录,它具有特殊的模式“160000”,标记它为子模块根目录。

    如果你忘记了最后一步,试图将一个子模块添加为常规目录,你将会得到以下错误信息:

    git add mysubmodule/file.txt 
    Path 'mysubmodule/file.txt' is in submodule 'mysubmodule'
    

    注意:自Git 2.17(2018年第二季度)以来,git submodule deinit不再是一个shell脚本,它是对C函数的调用。
    请参见commit 2e61273commit 1342476(2018年1月14日),作者为Prathamesh Chavan (pratham-pc)(由Junio C Hamano -- gitster --commit ead8dbe中合并,2018年2月13日)
    git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit \
      ${GIT_QUIET:+--quiet} \
      ${prefix:+--prefix "$prefix"} \
      ${force:+--force} \
      ${deinit_all:+--all} "$@"
    

    21
    你能举一个使用submodule deinit的例子吗? - zakdances
    5
    这是一个例子,说明有人成功地使用了它:https://dev59.com/snHYa4cB1Zd3GeqPJkZ5#16161950。但请注意,与我最初的想法相反,1.8.3版本尚未发布!在Unix上,你可以从源代码编译它。 - VonC
    2
    @HamishDowner 特殊条目应该已经消失了(该目录不再是子模块),.gitmodules 应该没问题,但我仍然建议双重检查 .git 目录中的任何内容(即本地存储库中的 本地 配置:这不会被 git pull 修改)。 - VonC
    2
    @Jayen 是的,如果你提交了删除 .gitmodules 条目和索引中特殊条目的操作,并推送该仓库,其他人就可以拉取它并且子模块将被删除。 - VonC
    5
    在当前的git(v1.9+)中,正如其他人已经说过的那样,旧式的“git rm submodule”命令正好做你想做的事情。 - Pete Peterson
    显示剩余39条评论

    926

    这个问题的大多数答案已经过时、不完整或者过于复杂。

    使用 git 1.7.8 或更新版本克隆的子模块最多会在您的本地仓库中留下四条痕迹。以下三个命令是用于删除这四条痕迹的过程:

    # Remove the submodule entry from .git/config
    git submodule deinit -f path/to/submodule
    
    # Remove the submodule directory from the superproject's .git/modules directory
    rm -rf .git/modules/path/to/submodule
    
    # Remove the entry in .gitmodules and remove the submodule directory located at path/to/submodule
    git rm -f path/to/submodule
    

    73
    为什么这个答案点赞数这么少?所有那些受欢迎的答案都遗漏了一些东西,而这个答案是唯一一个以最简单的方式真正删除所有子模块痕迹的。请注意:命令的顺序很重要。 - mbdevpl
    6
    @mbdevpl在被接受的回答之后三年才出现,我猜没有人成功地说服问问题者接受这个回答。 - Andy
    30
    这是2018年的简单答案吗? - Warren P
    14
    运行这些命令后,* .gitmodules *文件似乎仍未受到影响。 - Fractalf
    8
    2019年这种方法行不通。最后一行实际上是试图从上面已经删除的.git/modules文件夹中删除。像被接受的答案那样在第一行加上“--”似乎可以解决这个问题。 - Fmstrat
    显示剩余9条评论

    629

    仅作说明。自git 1.8.5.2以来,只需两个命令即可:

    git rm -r the_submodule
    rm -rf .git/modules/the_submodule
    

    正如@Mark Cheverton的回答所指出的那样,如果不使用第二行,即使现在删除了子模块,剩余的.git/modules/the_submodule文件夹也会阻止将来添加或替换相同的子模块。此外,正如@VonC所提到的,git rm命令将处理子模块上的大部分工作。

    --更新(07/05/2017)--

    只是为了澄清,the_submodule是项目内子模块的相对路径。例如,如果子模块位于子目录subdir中,则为subdir/my_submodule

    正如评论和其他答案所指出的那样,这两个命令(虽然在功能上足以删除子模块),但确实会在.git/config文件的[submodule "the_submodule"]部分留下痕迹(截至2017年7月),可以使用第三个命令删除:

    git config -f .git/config --remove-section submodule.the_submodule 2> /dev/null
    

    5
    我使用的是Git版本2.4.9(Apple Git-60),我所做的只是删除子模块。然后我将其推送,重新添加了一个与子模块同名的文件夹,这样就可以正常工作了。 - David Silva Smith
    21
    这不会从.git/config中移除子模块条目。请访问https://dev59.com/QHM_5IYBdhLWcg3wt1k0#36593218获取完整的移除子模块方式。 - fvgs
    3
    @drevicko 我刚刚用 Git 2.11.1 进行了测试,观察到与之前相同的行为。git init && git submodule add <repository> && git rm <name>会留下.git/config条目和.git/modules/<name>目录及其内容。也许您在删除之前没有初始化子模块? - fvgs
    3
    我觉得先运行这个更安全... git submodule deinit -f the_submodule - danday74
    1
    @danday74 如果子模块已经检出,那么您实际上需要执行该命令。否则,您将会收到一个错误:取消链接文件 '...' 失败 - Adam Burley
    显示剩余7条评论

    236

    简单步骤

    1. 删除配置条目:
      git config -f .git/config --remove-section submodule.$submodulename
      git config -f .gitmodules --remove-section submodule.$submodulename
    2. 从索引中删除目录:
      git rm --cached $submodulepath
    3. 提交
    4. 删除未使用的文件:
      rm -rf $submodulepath
      rm -rf .git/modules/$submodulename

    请注意:$submodulepath 不包含前导或尾随斜杠。

    背景

    当您执行 git submodule add 时,它只添加到 .gitmodules, 但一旦执行 git submodule init,它就会添加到 .git/config 中。

    因此,如果您希望删除模块,但能够快速恢复它, 则只需执行以下操作:

    git rm --cached $submodulepath
    git config -f .git/config --remove-section submodule.$submodulepath
    

    如果你把这些放在一个脚本里,最好先执行git rebase HEAD,最后再执行git commit

    还可以看一下如何取消 Git 子模块的关联


    1
    我有很多子模块(和更大的混乱),所以我必须通过for循环传递它们。由于它们中的大多数位于特定目录下,并且ls输出包含尾部斜杠,因此我做了类似“for dir in directory / *; do git rm --cached $dir; done”的操作。 - Pablo Olmos de Aguilera C.
    要获取可以用于递归删除脚本的列表 - git config -f .git/config -l | cut -d'=' -f1 | grep "submodule.$MODPATH" | sed 's/^submodule\.//' | sed 's/\.url$//' - 看起来你必须真正这样做,以防有什么混乱,否则只需使用 git submodule | grep -v '^+' | cut -d' ' -f3 - errordeveloper
    2
    要获取未进行本地更改的模块列表,请使用以下命令:git submodule | grep '^+' | cut -d' ' -f2 - errordeveloper
    请注意,我必须在双引号中包含“submodulename”,以引用“.git/config”文件。 - muon
    1
    简单高效。在2.25.0版本中,完成第一步后,需要在进行第二步之前暂存.gitmodules的更改。 - Michel Donais

    117

    要删除使用以下命令添加的子模块:

    REPOSITORY=blah@blah.com:repos/blah.git
    MOD_DIR=lib/blah
    git submodule add $REPOSITORY $MOD_DIR
    

    运行:

    git rm $MOD_DIR
    

    就是这样。

    对于旧版本的git(大约在1.8.5左右,实际上甚至在2.26.2中),请使用:

    git submodule deinit $MOD_DIR
    git rm $MOD_DIR
    git config -f .gitmodules --remove-section submodule.$MOD_DIR
    

    2
    确实是这样。从git 1.8.3版本开始,这是唯一正确的答案。应该被接受为正确答案。 - Xananax
    16
    git rm 命令仍会在 .git/modules/ 目录下留下一些东西。(2.5.4) - Rudolf Adamkovič
    1
    @RudolfAdamkovic 对我有效吗?请注意,它只会在确切路径匹配时删除子模块条目;如果您移动了一个子模块,然后使用 git rm,它不会生效。在我的 Mac 上进行了快速测试,使用 2.5.4 版本更新了 .gitmodules 文件,如此文档所述:https://git-scm.com/docs/git-rm#_submodules ... 但是,如果您发现某种平台/版本组合没有发生这种情况,您应该提交一个错误报告。 - Doug
    5
    这个答案并不完全正确。git rm 命令会在.git/modules/ 目录和 .git/config 文件中留下一些内容(在 Ubuntu 系统,git 版本是 2.7.4)。另一个回答是100%正确的:https://dev59.com/QHM_5IYBdhLWcg3wt1k0#36593218 - mbdevpl
    1
    deinit会移除子模块但是保留文件吗?我想要移除git中所有的残留,但是保留实际文件夹。 - justin.m.chase
    显示剩余2条评论

    86

    除了这些建议,我还需要执行rm -Rf .git/modules/path/to/submodule命令,以便能够添加一个同名的新子模块(在我的情况下,我正在用原始版本替换一个分支)。


    1
    我也遇到了这个问题。如果您尝试将子模块重新安装到相同的路径,则会在您提到的位置缓存分支信息,从而搞乱事情。 - jangosteve
    谢谢,我也需要这个。@Anton,我同意,并且我已经编辑了得票最高的答案以添加这些信息。 - William Denniss
    我使用了--name选项来使替换工作...请参见https://dev59.com/tmYq5IYBdhLWcg3wixFr - joseph.hainline

    68
    您需要从.gitmodules.git/config中删除条目,并从历史记录中删除该模块的目录。
    git rm --cached path/to/submodule
    
    如果你在Git的邮件列表上发帖,可能会有人为你编写一个Shell脚本。

    不需要任何shell脚本,其他答案中有删除子模块所有痕迹的命令:https://dev59.com/QHM_5IYBdhLWcg3wt1k0#36593218 - mbdevpl

    47

    我发现deinit对我很有效:

    git submodule deinit <submodule-name>    
    git rm <submodule-name>
    

    来自Git文档:

    deinit

    取消注册给定的子模块,即从.git/config文件中删除整个submodule.$name部分以及它们的工作树。


    同意找到了相同的解决方案。这是2018年最好的方法。 - woto
    1
    它没有删除 .git/modules/..。你应该将它们删除,参见 @fvgs 的答案。 - Vilém Kurz
    不知道为什么这个简单易行的解决方案不是排名第一。 - Marc Magon
    1
    据我所知,这似乎是对于较新的 git 最安全的答案,因为另一个答案会过早地删除 .git/modules/submodule 目录,这似乎会导致较新的 git 时不时失败。此外(请参见我的评论),删除 .git/modules/submodule 可能是错误的路径,因此这是一步危险的操作,最好只在 git 抱怨时(或者您确信这是您想要的、正确的路径并且真正需要时)才执行。 - Tino
    我还需要使用 git commit 命令来提交工作目录中暂存的更改: modified .gitmodulesdeleted <submodule-path> - Yuri Pozniak
    从git 2.31.0开始,如果您稍后在相同路径下添加另一个子模块,则git将愉快地不会使用新的子模块存储库路径更新.git/modules/submodule/config(也不会警告),因此未来的git push/pull将无法工作。这就是您需要在再次添加之前删除.git/modules/submodule的原因。 - Case Larsen

    46

    您可以使用别名来自动化其他人提供的解决方案:

    [alias]
      rms = "!f(){ git rm --cached \"$1\";rm -r \"$1\";git config -f .gitmodules --remove-section \"submodule.$1\";git config -f .git/config --remove-section \"submodule.$1\";git add .gitmodules; }; f"
    

    将其添加到你的git配置中,然后你可以执行:git rms path/to/submodule


    首先:这假设子模块的名称和路径是相同的,但这通常不是这种情况。例如:git clone https://github.com/hilbix/empty.git; cd empty; git submodule add https://github.com/hilbix/empty.git one; git mv one two; git rms two。 其次:您必须从正确的路径执行此操作。git别名应该在工作树中的任何位置都可以工作(或者优雅地失败)。 第三:git config -f .git/config在子模块中会失败,因为.git通常是一个文件。 - Tino

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接