如何删除在.gitignore中列出但仍然在存储库上的文件?

我在我的存储库中有一些文件应该被忽略,我将它们添加到.gitignore,但是,当然,它们不会从我的存储库中删除。

所以我的问题是,是否有一个神奇的命令或脚本使用过滤器分支,可以重写我的历史,并轻松删除所有这些文件?或者只是一个创建提交并删除它们的命令?

274799 次浏览

当你将。gitignore模式添加到。gitignore后,git会忽略匹配的文件。

但是存储库中已经存在的文件仍然存在。

使用git rm files_ignored; git commit -m 'rm no use files'删除被忽略的文件。

您可以手动从存储库中删除它们:

git rm --cached file1 file2 dir/file3

或者,如果你有很多文件:

git rm --cached `git ls-files -i -c --exclude-from=.gitignore`

但是这在Windows上的Git Bash中似乎不起作用。它产生一个错误消息。以下方法效果更好:

git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached

在Windows上的PowerShell工作得更好(处理路径和文件名中的空格):

git ls-files -i -c --exclude-from=.gitignore | %{git rm --cached $_}
关于在没有这些文件的情况下重写整个历史,我非常怀疑是否有自动的方法来做到这一点。
我们都知道改写历史不好,不是吗?:) < / p >

不管操作系统是什么,一个更简单的方法是

git rm -r --cached .
git add .
git commit -m "Drop files from .gitignore"

您基本上删除并重新添加所有文件,但git add将忽略.gitignore中的文件。

使用--cached选项将文件保存在文件系统中,因此不会从磁盘中删除文件。

< p >注意: 有些人在评论中指出,你将失去所有文件的历史记录。我在MacOS上用git 2.27.0测试了这个,它是的情况。如果你想检查发生了什么,在你提交之前检查你的git diff HEAD~1

由于.gitignore中的文件没有被跟踪,您可以使用git清洁命令递归地删除不受版本控制的文件。

使用git clean -xdn来执行一个演练,看看什么将被删除 然后使用git clean -xdf来执行它。< / p >

基本上,git clean -hman git-clean(在unix中)将提供帮助。

注意,该命令还将删除暂存区域中不存在的新文件

希望能有所帮助。

通过使用sed操作.gitignore语句的输出,我做了一个非常简单的解决方案:

# EYZ0

解释:

  1. 打印.gitignore文件
  2. 从打印中删除所有注释
  3. 删除所有空行
  4. 在行首添加'git rm -r '
  5. 执行每一行。

在linux中,你可以使用这个命令:

例如,我想删除*.py~,所以我的命令应该是==>

# EYZ0

如果你真的想要删除.gitignored文件的历史,首先将.gitignore保存在repo之外,例如,保存为/tmp/.gitignore,然后运行

git filter-branch --force --index-filter \
"git ls-files -i -X /tmp/.gitignore | xargs -r git rm --cached --ignore-unmatch -rf" \
--prune-empty --tag-name-filter cat -- --all

注:

  • 我认为git filter-branch --index-filter.git目录下运行,也就是说,如果你想使用相对路径,你必须先前置一个../。显然你不能使用../.gitignore,实际的.gitignore文件,它会产生一个"致命的:不能使用../。Gitignore作为一个排除文件;出于某种原因(也许在git filter-branch --index-filter期间工作目录是空的?)
  • 我希望使用像git ls-files -iX <(git show $(git hash-object -w .gitignore))这样的东西来避免复制.gitignore到其他地方,但这本身已经返回一个空字符串(而cat <(git show $(git hash-object -w .gitignore))确实打印了.gitignore的内容如预期),所以我不能在git filter-branch中使用<(git show $GITIGNORE_HASH)
  • 如果您实际上只想要.gitignore-clean一个特定的分支,请将最后一行中的--all替换为它的名称。--tag-name-filter cat可能无法正常工作,也就是说,您可能无法正确地直接传输单个分支的标记

git clean"(man) "# EYZ4 & (man)在被忽略的目录中工作或显示被忽略的路径时出现混淆,这已在Git 2.32 (Q2 2021)中得到更正。

这意味着2021年版本的公认的答案将是:

git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached
^^

参见伊利亚·纽伦(newren)提交b548f0f提交dd55fc0提交aa6e1b2提交a97c7a8提交2 e4e43a提交b338e9f提交7 fe1ffd提交7 f9dd87(2021年5月12日)。
参见德里克·斯托利(derrickstolee)提交4 e689d8(2021年5月12日)。
(由滨野朱尼奥——gitster——提交33 be431中合并,2021年5月20日)

ls-files:除非指定了-o或-c,否则在-i上出错

署名:以利亚·纽伦

ls-files --ignored(man)可以与--others--cached一起使用。

在困惑了一会儿并深入到代码中之后,我认为ls-files -i只是坏了,没有打印任何东西,当我最终意识到-i可以与--cached一起使用来查找跟踪忽略时,我已经准备好了一个很好的补丁。

虽然这是我的一个错误,仔细阅读文档可能会更清楚地说明这一点,但我怀疑这是其他人也可能犯的错误。
事实上,在我们测试套件中的两个使用中,我相信其中一个确实犯了这个错误。
在t1306.13中,没有跟踪文件,因此在该测试和以前的测试中构建和使用的所有排除都必须是关于未跟踪的文件。
然而,由于他们正在寻找一个空的结果,这个错误没有被注意到,因为他们的错误命令也恰好给出了一个空的答案 -i大部分时间将与-o一起使用,这意味着我们可以让-i在没有-o-c的情况下暗示-o,但这将是向后不兼容的中断。
相反,让我们只是标记-i而不是-o-c作为错误,并更新两个相关的测试用例以指定它们的意图

这意味着如果没有-c,你会得到(从Git 2.32开始,Q2 2021):

fatal: ls-files -i must be used with either -o or -c
注意:这仍在进行中,因为它是在Git 2.32-rc2中恢复,但由滨野朱尼奥(gitster)修复了提交2 c9f1bf提交1 df046b(2021年5月27日)。
参见提交906年fc55(2021年5月27日)。
参见提交eef8148(2021年5月27日)。
(由滨野朱尼奥——gitster——提交329年d63e中合并,2021年5月28日)

dir:介绍readdir_skip_dot_and_dotdot()助手

署名:以利亚·纽伦

这个解决方案增加了回车符(我是一个WSL用户,所以这很重要)和圆括号转义(有时对LaTeX用户很重要,例如*.synctex(busy))。


灵感来自斯科特的解决方案:

cat .gitignore | sed "s/\r//" | sed -r "/^(#.*|\s*)$/d" | sed -r "s/([()])/\\\\\1/g" | sed "s/^/git rm -r /" | bash
  1. 移除:回车(s/\r//)。
  2. 删除包含以下内容的行:注释(/^#.*$/),空行组(/^\s*$/,匹配空白或空行)。注意管道|字符,这是标准正则表达式,需要-r(尽管我相信-E也可以)。
  3. 将:圆括号/([()])/替换为它的转义版本\\\1\1匹配组,在这种情况下它意味着[()],或(),无论匹配的是什么。注意g标志,这是为了匹配(并替换)所有括号。如果你喜欢,可以重写为"s/(\(|\))/\\\\\1/g"
  4. 预先考虑# EYZ0

替换看起来像s/$old/$new/$flags。删除看起来像/$old/d。Prepending正在替换/^/。你可以通过替换/$/来追加。当然,有些字符是转义的,因为据我所知,你不能在bash中创建原始字符串。最后,这一行可以压缩,但为了可读性,我选择将其展开。


我看到有人质疑(在Scott的解决方案中)sed是直截了当的。我喜欢把这个方法看作是最基本最简单的方法,这很好,因为如果你需要它的变化,你可以当场做出来。如果有的话,这是一个练习正则表达式的好借口。

< p > # EYZ0 删除所有递归缓存

< p > # EYZ0 添加.gitignore

中未包含的所有文件

你将不得不提交一些在文件系统上没有真正删除的已删除文件

使用单个命令它将 # EYZ0 < / p >