如何删除Git子模块?为什么我不能git子模块rm模块名称?
当前回答
为了读者的利益,本文试图对其进行总结,并给出一个循序渐进的指导,说明如果事情没有按预期进行,该如何进行。以下是git 2.17及以上版本摆脱子模块的测试和安全方法:
submodule="path/to/sub" # no trailing slash!
git submodule deinit -- "$submodule"
git rm -- "$submodule"
如果这对您不起作用,请参见下文。没有选项。没有危险。甚至不要考虑做更多!使用Debian Buster 2.20.1和Ubuntu 18.04 2.17.1进行测试。“$submodule”只是为了强调名字的位置,并且你必须小心空格等如果在Windows上,则忽略第一行,并用正确指定的子模块路径的Windows方式替换“$submodule”。(我不是Windows)
警告千万不要自己触摸.git目录的内部!在内部编辑.git会进入黑暗面。不惜一切代价远离!是的,你可以把这归咎于git,因为过去git中缺少了很多有用的东西。就像再次移除子模块的正确方法。我认为git子模块的文档中有一个非常危险的部分。建议您自己删除$GIT_DIR/modules/<name>/。在我的理解中,这不仅是明显错误的,而且是极其危险的,并且会在未来引发严重的头痛!见下文。
请注意
git module deinit
与
git module init
but
git submodule deinit -- module
git rm -- module
也与
git submodule add -- URL module
git submodule update --init --recursive -- module
因为有些命令基本上需要做的不仅仅是一件事:
git子模块deinit—模块(1) 更新.git/config移除文件(2) 删除模块的文件(3) 从而递归地移除子模块的子模块(4) 更新.gitmodulesgit子模块加法将数据拉入.git/modules/NAME/(1) 执行git子模块初始化,因此更新git/config(2) git子模块是否更新,因此,非递归地检查模块(4) 更新.gitmodulesgit子模块更新--init--递归--module如有需要,可提取更多数据(3) 递归地检查子模块的子模块
这不可能是完全对称的,因为保持严格对称没有多大意义。根本不需要两个以上的命令。此外,“拉入数据”是隐式的,因为您需要它,但删除缓存的信息并没有完成,因为这根本不需要,而且可能会擦除宝贵的数据。
这确实让新手感到困惑,但基本上是一件好事:git只是做了明显的事情并做了正确的事情,甚至没有尝试做更多。git是一个工具,它必须做一个可靠的工作,而不仅仅是另一个“Eierlegende Wollmilchsau”(“Eierligende Wolmilchsaw”在我看来是“瑞士军刀的邪恶版本”)。
所以我理解人们的抱怨,说“为什么不为我做显而易见的事情”。这是因为这里的“明显”取决于观点。每种情况下的可靠性都要重要得多。因此,在所有可能的技术情况下,对你来说显而易见的事情往往不是正确的。请记住:AFAICS git遵循技术路径,而不是社会路径。(因此得名:git)
如果失败
上述命令可能因以下原因而失败:
你的吉特太老了。然后使用新的git。(请参见下面的操作方法。)您有未提交的数据,可能会丢失数据。那最好先把它们交给我。你的子模块在干净的意义上是不干净的。然后,首先使用该命令清理子模块。(见下文。)您在过去做了一些git不支持的事情。然后你处于黑暗面,事情变得丑陋和复杂。(也许使用另一台机器可以解决问题。)也许还有更多我不知道的失败方法(我只是一个git超级用户)
可能的修复方法如下。
使用新的git
如果您的机器太旧,git中没有子模块deinit。如果您不想(或者可以)更新您的git,那么只需使用另一台具有更新git的机器即可!git是完全分布式的,因此您可以使用另一个git来完成任务:
workhorse:~/path/to/worktree$git-status--瓷器不能输出任何内容!如果是这样,先清理一下!workhorse:~/path/to/worktree$sshaccount@othermachine其他计算机:~$git克隆--递归me@workhorse路径/to/worktree/.git TMPWORK&&cd TMPWORK现在做子模块的事情othermachine:~/TMPWORK$gitcommit-m和&出口workhorse:~/path/to/worktree$gitfetchaccount@othermachine:TMPWORK/.gitworkhorse:~/path/to/worktree$gitmerge--ff only FETCH_HEAD。如果这不起作用,请使用git reset--soft FETCH_HEAD现在清理东西,直到git状态再次清理。你能够做到这一点,因为你之前已经清洁过了,多亏了第一步。
另一台机器可以是一些VM,或者Windows下的一些Ubuntu WSL,等等。即使是chroot(但我假设您是非root用户,因为如果您是root用户,应该更容易更新到新的git)。
请注意,如果您不能ssh登录,那么有很多方法可以传输git存储库。您可以将工作树复制到某个U盘上(包括.git目录),然后从U盘上克隆。复制副本,只是为了再次以干净的方式获取内容。这可能是一个PITA,以防您的子模块无法直接从其他机器访问。但这也有一个解决方案:
git config --add url.NEWURLPREFIX.insteadOf ORIGINALURLPREFIX
您可以使用此乘法,并将其保存到$HOME/.gitconfig中
git config --add 'url./mnt/usb/repo/.insteadof' https://github.com/
重写URL,如
https://github.com/XXX/YYY.git
into
/mnt/usb/repo/XXX/YYY.git
如果你开始习惯于这样强大的git功能,这很容易。
先清理东西
手动清理是很好的,因为这样你可能会发现一些你忘记的东西。
如果git抱怨未保存的东西,请提交并将其推送到安全的地方。如果git抱怨一些剩菜,gitstatus和gitclean-ixfd就是你的朋友尽可能避免使用rm和deinit选项。如果你是专业人士,git的选项(如-f)很好。但当你来到这里时,你可能在子模块领域没有那么丰富的经验。所以,安全总比抱歉好。
例子:
$ git status --porcelain
M two
$ git submodule deinit two
error: the following file has local modifications:
two
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'two' contains local modifications; use '-f' to discard them
$ cd two
$ git submodule deinit --all
error: the following file has local modifications:
md5chk
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'md5chk' contains local modifications; use '-f' to discard them
$ cd md5chk
$ git submodule deinit --all
error: the following file has local modifications:
tino
(use --cached to keep the file, or -f to force removal)
fatal: Submodule work tree 'tino' contains local modifications; use '-f' to discard them
$ cd tino
$ git status --porcelain
?? NEW
$ git clean -i -f -d
Would remove the following item:
NEW
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers 4: ask each
5: quit 6: help
What now> 1
Removing NEW
$ cd ../../..
$ git status --porcelain
$ git submodule deinit two
Cleared directory 'two'
Submodule 'someunusedname' (https://github.com/hilbix/src.git) unregistered for path 'two'
你看,子模块deinit上不需要-f。如果事情是干净的,在一个干净的意义上。还要注意,不需要gitclean-x。这意味着git子模块无条件地删除未跟踪的文件,这些文件被忽略。这通常是你想要的,但不要忘记它。有时被忽略的文件可能很珍贵,比如缓存的数据,需要数小时到数天才能重新计算。
为什么从不删除$GIT_DIR/modules/<name>/?
人们可能希望删除缓存的存储库,因为他们害怕以后遇到问题。这是真的,但遇到那个“问题”才是解决问题的正确方法!因为修复很简单,而且做得好,你以后就能幸福地生活。这避免了比您自己删除数据更麻烦的麻烦。
例子:
mkdir tmptest &&
cd tmptest &&
git init &&
git submodule add https://github.com/hilbix/empty.git two &&
git commit -m . &&
git submodule deinit two &&
git rm two &&
git commit -m . &&
git submodule add https://github.com/hilbix/src.git two
最后一行输出以下错误:
A git directory for 'two' is found locally with remote(s):
origin https://github.com/hilbix/empty.git
If you want to reuse this local git directory instead of cloning again from
https://github.com/hilbix/src.git
use the '--force' option. If the local git directory is not the correct repo
or you are unsure what this means choose another name with the '--name' option.
为什么会出现此错误?因为.git/modules/two/先前是从https://github.com/hilbix/empty.git现在将从其他地方重新填充,即https://github.com/hilbix/src.git.如果您从https://github.com/hilbix/empty.git
现在该怎么办?好吧,就照吩咐做吧!使用--name someunusedname
git submodule add --name someunusedname https://github.com/hilbix/src.git two
.gitmodules看起来像
[submodule "someunusedname"]
path = two
url = https://github.com/hilbix/src.git
ls-1p.git/modules/gows
someunusedname/
two/
通过这种方式,将来您可以向前和向后切换分支/提交,再也不会遇到任何麻烦,因为两个/有两个不同的(可能不兼容的)上游存储库。最好的办法是:您也将两者都保存在本地。
这不仅适用于你。对于使用您的存储库的所有其他人也是如此。你不会失去历史。如果您忘记推送旧子模块的最新版本,您可以输入本地副本,稍后再这样做。请注意,很常见的情况是,有人忘记推送某些子模块(因为这是新来者的PITA,直到他们习惯了git)。
但是,如果您删除了缓存目录,两个不同的签出都会碰到对方,因为您不会使用--name选项,对吗?因此,每次执行签出操作时,您可能需要一次又一次地删除.git/modules/<module>/目录。这非常麻烦,很难使用像git平分这样的东西。
因此,保留此模块目录作为占位符是非常技术性的原因。建议删除.git/modules/以下内容的人可能不太清楚,或者忘记告诉你,如果这样做与子模块不兼容,那么像git平分这样的强大功能几乎无法使用。
上面显示了另一个原因。看看这些,你看到了什么?
模块two/的第二个变体不在.git/modules/two/下,它在.git/modules/someunusedname/下!比如git-rm$module;rm-f.git/module/$module完全错误!您必须查阅module/.git或.gitmodules才能找到要删除的正确内容!
因此,不仅大多数其他答案都落入了这个危险的陷阱,甚至非常流行的git扩展也有这个bug(现在已经修复了)!所以,如果你不知道你在做什么,最好还是掌握.git/目录!
从哲学的角度来看,抹杀历史总是错误的!除了量子力学,和往常一样,但这是完全不同的。
仅供参考,你可能猜到了:hilbix是我的GitHub帐户。
其他回答
在现代git中(我在2022年编写了这篇文章,并更新了git安装),这变得非常简单:
运行git-rm<path to submodule>并提交。
这将删除<path to submodule>处的文件树,以及.gitmodules文件中的子模块条目。即,删除存储库中子模块的所有痕迹。
然而,正如文档所指出的,子模块的.git目录(在主项目.git目录的模块/目录中)被保留了下来,“这样就可以在不需要从另一个存储库获取的情况下签出过去的提交”。如果您仍然想删除此信息,请手动删除.git/modules/中的子模块目录,并删除.git/config文件中的子模板条目
rm-rf.git/modules/<子模块路径>,以及git-config—删除节子模块<子模块路径>。
较旧的社区wiki说明:
通过Git子模块教程页面:
要删除子模块,您需要:
从.gitmodules文件中删除相关部分。暂存.gitmodules更改:git-add.gitmoduls从.git/config中删除相关部分。从工作树中删除子模块文件,并索引:git rm--cached path_to_submodule(无斜杠)。删除子模块的.git目录:rm-rf.git/modules/path_to_submodule提交更改:gitcommit-m“已删除子模块<name>”删除现在未跟踪的子模块文件:rm-rf path_to_submodule
另请参见:以下替代步骤。
如果您需要使用bash脚本在一行命令中执行此操作,如下所示:
$cd/path/to/your/rebo&&/bin/bash$HOME/remove_submodule.sh/path/to-the/submodule
在$HOME目录中创建bash脚本文件,名为remove_submodule.sh:
#!/bin/bash
git config -f .gitmodules --remove-section submodule.$1
git config -f .git/config --remove-section submodule.$1
git rm --cached $1
git add .gitmodules
git commit -m "Remove submodule in $1"
rm -rf $1
rm -rf .git/modules/$1
git push origin $(git rev-parse --abbrev-ref HEAD) --force --quiet
所有的答案看起来都过时了。我使用的是git版本2.28.0。一行回答是,
git rm path-to-submodule
然而,即使从源代码管理中删除了子模块,.git/modules/path到子模块仍然包含子模块存储库,.git/config包含其URL,因此您仍然需要手动删除这些子模块:
git config --remove-section submodule.path-to-submodule
rm -rf .git/modules/path-to-submodule
有时,必须使用-f标志:
$ git rm -f img2vec
例如,因为您可能会收到如下错误:
$ git rm img2vec/
error: the following file has changes staged in the index:
img2vec
(use --cached to keep the file, or -f to force removal)
简单的步骤
删除配置项:git-config-f.git/config--删除节子模块$submodulename git-config-f.gitmodules—删除部分子模块$子模块名称从索引中删除目录:git rm--cached$submodule路径犯罪删除未使用的文件:rm-rf$submodulepathrm-rf.git/modules/$submodulename
请注意:$submodulepath不包含前导或尾随斜杠。
出身背景
当您执行git子模块add时,它只将其添加到.gitmodules中,但是完成git子模块init后,它将添加到.git/config中。
因此,如果您希望删除模块,但能够快速恢复,那么就这样做:
git rm --cached $submodulepath
git config -f .git/config --remove-section submodule.$submodulepath
首先执行git rebase HEAD和git commit是一个好主意最后,如果你把这个写在脚本中。
还可以看看我可以取消填充Git子模块吗?的答案?。
以下是我认为必要或有用的4个步骤(首先是重要步骤):
git rm -f the_submodule
rm -rf .git/modules/the_submodule
git config -f .git/config --remove-section submodule.the_submodule
git commit -m "..."
理论上,第1步中的git rm应该处理它。希望OP问题的第二部分有一天能够得到肯定的回答(这可以在一个命令中完成)。
但从2017年7月起,第2步必须删除.git/modules/中的数据。否则,您不能在将来重新添加子模块。
正如tinlyx的回答所指出的那样,您可能可以省去上面的两个git1.8.5+步骤,因为所有的git子模块命令似乎都有效。
步骤3删除了.git/config文件中_submodule的部分。为了完整起见,应该这样做。(该条目可能会对较旧的git版本造成问题,但我没有要测试的条目)。
为此,大多数答案建议使用git子模块deinit。我发现使用git-config-f.git/config--remove部分更加明确,也不那么令人困惑。根据git子模块文档,git deinit:
注销给定的子模块。。。如果确实要删除存储库中的子模块,并使用gitrm[1]提交相反
最后但并非最不重要的是,如果您没有gitcommit,那么在执行git子模块摘要时(从git2.7开始),您可能会遇到错误:
fatal: Not a git repository: 'the_submodule/.git'
* the_submodule 73f0d1d...0000000:
这与执行步骤2或3无关。
推荐文章
- Visual Studio Code无法检测已安装的Git
- 强制LF eol在git的回购和工作副本
- Git:在裸库中更改活动分支的正确方法?
- 删除git中的分支是否会将其从历史记录中删除?
- 防止在GitHub上推送到master ?
- 根据Git,谁是“我们”,谁是“他们”?
- git如何合并后樱桃采摘工作?
- Git搜索单个文件历史记录中的字符串
- Git命令显示所有(轻量级)标签创建日期
- Gitignore并没有忽视文件夹
- 什么时候用。git/info/exclude代替。gitignore来排除文件?
- 如何告诉git忽略个别行,即gitignore为特定行代码
- “git diff——耐心”是什么意思?
- 我如何在GitHub中为其他人的代码做出贡献?
- 签出旧的提交并使其成为新的提交