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

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


当前回答

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


受到Scott解决方案的启发:

cat .gitignore | sed "s/\r//" | sed -r "/^(#.*|\s*)$/d" | sed -r "s/([()])/\\\\\1/g" | sed "s/^/git rm -r /" | bash

Remove: carriage returns (s/\r//). Remove lines containing: comments (/^#.*$/), empty line groups (/^\s*$/, matches whitespace or empty line). Notice the pipe | character, this is standard regex, and requires -r (although I believe -E also works). Replace: parenthesis /([()])/ with its escaped version \\\1, \1 matches the group, in this case it means [()], or ( or ), whatever was matched. Notice the g flag, this is to match (and replace) all parenthesis. Could be rewritten as "s/(\(|\))/\\\\\1/g" if you're into that. Prepend git rm -r

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


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

其他回答

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

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

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

如果你真的想要删除。gitignore文件的历史,首先将.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 runs in the .git directory I think, i.e. if you want to use a relative path you have to prepend one more ../ first. And apparently you cannot use ../.gitignore, the actual .gitignore file, that yields a "fatal: cannot use ../.gitignore as an exclude file" for some reason (maybe during a git filter-branch --index-filter the working directory is (considered) empty?) I was hoping to use something like git ls-files -iX <(git show $(git hash-object -w .gitignore)) instead to avoid copying .gitignore somewhere else, but that alone already returns an empty string (whereas cat <(git show $(git hash-object -w .gitignore)) indeed prints .gitignore's contents as expected), so I cannot use <(git show $GITIGNORE_HASH) in git filter-branch... If you actually only want to .gitignore-clean a specific branch, replace --all in the last line with its name. The --tag-name-filter cat might not work properly then, i.e. you'll probably not be able to directly transfer a single branch's tags properly

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

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 $_}

关于在没有这些文件的情况下重写整个历史,我非常怀疑是否有自动的方法来做到这一点。 我们都知道改写历史不好,不是吗?:)

Git rm——cached -r。 以递归方式删除所有缓存

Git添加。 添加所有未包含在.gitignore中的文件

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

使用一个命令就可以了 Git rm——cached -r。&& git添加。

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


受到Scott解决方案的启发:

cat .gitignore | sed "s/\r//" | sed -r "/^(#.*|\s*)$/d" | sed -r "s/([()])/\\\\\1/g" | sed "s/^/git rm -r /" | bash

Remove: carriage returns (s/\r//). Remove lines containing: comments (/^#.*$/), empty line groups (/^\s*$/, matches whitespace or empty line). Notice the pipe | character, this is standard regex, and requires -r (although I believe -E also works). Replace: parenthesis /([()])/ with its escaped version \\\1, \1 matches the group, in this case it means [()], or ( or ), whatever was matched. Notice the g flag, this is to match (and replace) all parenthesis. Could be rewritten as "s/(\(|\))/\\\\\1/g" if you're into that. Prepend git rm -r

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


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