您是对的:树过滤器或索引过滤器将是使用git filter-branch 执行此操作的方法。
树形过滤器更容易,但速度要慢得多(慢 10 到 100 倍)。树过滤器的工作方式是您提供的命令在一个临时目录中运行,该目录包含所有且仅包含原始(现在正在复制)提交中存在的文件。您的命令留下的任何文件都保留在复制的提交中。您的命令在临时目录中创建的任何文件也在复制的提交中。 (您可以在临时目录中创建或删除目录,但无论哪种方式都无效,因为 Git 只存储文件。)因此,要删除所有 除了 A 和 B,请编写一个删除所有文件的命令不在 A 或 B 中:
find . -name A -prune -o -name B -prune -o -print0 | xargs -0 rm
例如。
索引过滤器更难,但更快,因为 Git 不必将所有文件复制到文件树中,然后重新扫描文件树以构建新索引,以复制原始提交。相反,它只提供一个索引,然后您可以使用诸如git rm -rf --cached --ignore-unmatch 之类的命令进行操作,或者在大多数情况下使用git update-index。但是,现在您拥有的唯一工具是 Git 中用于操作索引的工具。没有花哨的 Unix find 命令。
当然,你有git ls-files,它会读出索引的当前内容。因此,您可以用任何您喜欢的语言编写程序(我可能会先在这里使用 Python,其他人可能会从 Perl 开始),本质上就是这样:
for (all files in the index)
if (file name starts with 'A/' or 'B/')
do nothing
else
add to removal list
invoke "git rm --cached" on paths in removal list
如果您愿意相信没有文件名具有嵌入的换行符,则可以在常规 shell 中按以下方式完成上述操作:
git ls-files | IFS=$'\n' while read path; do
case "$path" in A/*|B/*) continue;; esac
git rm --cached "$path"
done
这不是非常有效(每个路径一个git rm --cached!)但应该像--index-filter一样“开箱即用”。
(未经测试,但可能有效并且应该更有效:通过管道git ls-files 输出到grep -v 以删除所需的文件,并将grep 输出管道到git update-index --force-remove --stdin。这仍然假定路径名中没有换行符。 )