我在我的存储库中有一些文件应该被忽略,我将它们添加到.gitignore,但是,当然,它们不会从我的存储库中删除。
所以我的问题是,是否有一个神奇的命令或脚本使用过滤器分支,可以重写我的历史,并轻松删除所有这些文件?或者只是一个创建提交并删除它们的命令?
我在我的存储库中有一些文件应该被忽略,我将它们添加到.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 -r。 以递归方式删除所有缓存
Git添加。 添加所有未包含在.gitignore中的文件
你将不得不提交一些在文件系统上没有真正删除的已删除文件
使用一个命令就可以了 Git rm——cached -r。&& git添加。
您可以手动从存储库中删除它们:
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 clean”(man)和git ls-files -i(man)在处理或显示被忽略目录中的被忽略路径时会混淆,这已在git 2.32 (Q2 2021)中得到更正。
这意味着2021年版本的公认答案将是:
git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached
^^
参见Elijah Newren (Newren)的commit b548f0f, commit dd55fc0, commit aa6e1b2, commit a97c7a8, commit 2e4e43a, commit b338e9f, commit 7fe1ffd, commit 7f9dd87(2021年5月12日)。 参见Derrick Stolee提交的4e689d8(2021年5月12日)。 (由Junio C Hamano—gitster—在commit 33be431中合并,2021年5月20日)
Ls-files:除非指定了-o或-c,否则在-i上出错 署名:以利亚·纽伦
ls-files --ignored(man) can be used together with either --others or --cached. After being perplexed for a bit and digging in to the code, I assumed that ls-files -i was just broken and not printing anything and I had a nice patch ready to submit when I finally realized that -i can be used with --cached to find tracked ignores. While that was a mistake on my part, and a careful reading of the documentation could have made this more clear, I suspect this is an error others are likely to make as well. In fact, of two uses in our testsuite, I believe one of the two did make this error. In t1306.13, there are NO tracked files, and all the excludes built up and used in that test and in previous tests thus have to be about untracked files. However, since they were looking for an empty result, the mistake went unnoticed as their erroneous command also just happened to give an empty answer. -i will most the time be used with -o, which would suggest we could just make -i imply -o in the absence of either a -o or -c, but that would be a backward incompatible break. Instead, let's just flag -i without either a -o or -c as an error, and update the two relevant testcases to specify their intent.
这意味着没有-c,你会得到(从Git 2.32开始,Q2 2021):
fatal: ls-files -i must be used with either -o or -c
注意:这仍然是一项正在进行的工作,因为它在Git 2.32-rc2中被恢复,但由Junio C Hamano (gitster)在commit 2c9f1bf, commit 1df046b(2021年5月27日)中修复。 参见Elijah Newren (Newren)提交的906fc55(2021年5月27日)。 参见Derrick Stolee (derrickstolee)的commit eef8148(2021年5月27日)。 (由Junio C Hamano—gitster—在commit 329d63e中合并,2021年5月28日)
Dir:引入readdir_skip_dot_and_dotdot() helper 署名:以利亚·纽伦