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

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


当前回答

“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 署名:以利亚·纽伦

其他回答

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

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

基本上,git clean -h或man git-clean(在unix中)将为您提供帮助。

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

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

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

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

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

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

Cat .gitignore | sed '/^#。* / d ' | sed ' / ^ \ s *美元/ d ' | sed ' s / ^ / git rm - r / | bash

解释:

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

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

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

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

如果你真的想要删除。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