我有一个复杂的git repo,我想删除其中的所有文件和历史记录,除了两个文件夹,比方说:
foo/a
bar/x/y
虽然git filter-branch --subdirectory-filter
可以让我选择一个文件夹,并将其作为新的根目录,但它似乎没有给我任何选择两个目录并保留其位置的选项。git filter-branch --tree-filter
或--index-filter
似乎可以让我迭代历史中的每个提交,这样我就可以在不需要的文件夹上使用git rm
。
我似乎找不到任何有效的方法来让这些命令只保留我想要的两个文件夹,同时清除所有其他。
谢谢!
4条答案
按热度按时间wkftcu5l1#
您是正确的:树过滤器或索引过滤器将是用
git filter-branch
完成此操作的方式。树过滤器更简单,但速度慢得多(很容易慢10到100倍)。树过滤器的工作方式是,您提供的命令在一个临时目录中运行,该目录包含且仅包含原始目录中存在的所有文件(现在被复制)提交。任何你的命令留下的文件,保留在复制的提交中。任何你的命令在临时目录中创建的文件,(你可以在临时目录中创建或删除目录,但这两种方式都不起作用,因为Git只存储文件。)因此,要删除除A和B之外的所有文件,请编写一个命令,删除除A或B之外的所有文件:
例如。
索引过滤器更难,但速度更快,因为Git不需要复制所有的文件到文件树,然后重新扫描文件树来建立一个新的索引,以复制原始提交。相反,它只提供一个索引,然后你可以使用
git rm -rf --cached --ignore-unmatch
或git update-index
等命令来操作它。现在你所拥有的工具只有Git中那些操作索引的工具,没有花哨的Unixfind
命令。当然,你有
git ls-files
,它读取索引的当前内容,因此你可以用任何你喜欢的语言编写一个程序(这里我可能会首先使用Python,其他人可能会从Perl开始),它本质上是:如果你愿意相信没有文件名有一个嵌入式换行符,以上可以在常规shell中完成:
这并不是非常高效(每个路径一个
git rm --cached
!),但应该作为--index-filter
"开箱即用"。(未经测试,但可能有效,而且效率应该更高:将
git ls-files
输出通过grep -v
以移除所需文件,并将grep
输出通过管道传输到git update-index --force-remove --stdin
。这仍然假设路径名中没有换行符。)xmakbtuz2#
对于 files,我已经用
git fast-export
做了这个,但是我不确定它是否能递归地工作在目录上,所以我建议使用git fast-export
和find
的组合。然后创建一个新的存储库,并导入流。
oaxa6hgo3#
一种更新且更好的实现方法是使用
filter-repo
。filter-branch
现在在其文档中建议不使用(此处为版本2.30.0):警告
git filter-branch
有太多的缺陷,可能会对预期的历史重写产生不明显的破坏(而且可能会让您几乎没有时间调查此类问题,因为它的性能如此糟糕)。这些安全和性能问题无法向后兼容修复,因此,不建议使用它。请使用其他历史记录筛选工具,如git filter-repo[1]
。如果仍需要使用git filter-branch
,请仔细阅读“安全”一节(以及“性能”一节),了解过滤器分支的地雷,然后警惕地尽可能避免其中列出的危险。wqnecbli4#
经过多次失败的尝试后,我终于用
git filter-repo
解决了这个问题,这要归功于@Ken的回答和方法3:https://www.baeldung.com/git-remove-file-commit-history#using-git-filter-repo其他的答案对我来说不合适,或者只是简单的混乱。特别是,没有一个
filter-branch
选项对我有效。请注意,根据官方git文档,
git filter-repo
比git filter-branch
更可取:https://git-scm.com/docs/git-filter-branch