我正在尝试使用filter-branch
从我的历史记录中删除大量大文件。我之前已成功使用此命令,但我目前遇到特殊边缘情况的问题。
问题是这些大型文件从未真正删除,但 被具有相同路径的较小版本取代 。
据我所知,我相信我有一个独特的问题。
详细说明,这是我的回购的基本代表:
----- A ------ B ----------- HEAD
其中:
A is the commit where the large files were introduced
B is the commit (about 30 later) where the large files were replaced with smaller ones
HEAD is thousands of commits forward of B (~2 years of active development)
理论上,我应该能够做到这样的事情:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' <parent of A>..B
我认为我应该使用<parent of A>
因为filter-branch
不具有包容性。 (我不确定我是否也需要使用B的父母,但这是我现在最不担心的事情。)
运行此操作会给我一个错误:
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' <parent of A>..B
Which ref do you want to rewrite?
所以我在命令的末尾包含了--glob="refs/heads/master*"
,这似乎可以解决问题(source)。
执行完成后,文件已被完全删除 - 似乎git忽略了我指定的上限。
所以我想知道这种方法是否可行?
我认为我应该列出一些其他的想法,以便将可能的答案集中在解决问题上。
git filter-branch ... HEAD
。但是,我的存储库在活动开发中有许多分支,我相信这种方法会非常混乱。create a temporary branch to point at HEAD^, filter-branch it, then add a graft to stitch the remaining commit on top of it, then filter-branch HEAD and then remove the graft.
希望有人之前遇到过这个问题并且可以提供他们的专业知识。
我要删除的文件总数 ~500MB 所以我很想知道要删除它们!它们在我加入公司之前就已经提交了,并且是我们从内部Mercurial服务器迁移到GitHub的剩余部分(我想将500MB推送到内部服务器将比GitHub更不明显......)。
我一直在关注twalberg的第二个答案(我想我正在以正确的方式使用它):
git filter-branch --index-filter '(( $(git rev-list <SHA-of-child-of-B> --not $GIT_COMMIT | wc -l) > 0 )) && git rm --cached --ignore-unmatch <filenames>'
这会产生我期望的那种输出:
...
Rewrite dc8a4b29463bfa43c2f3efe0c6e5a29a5cc6e0ef (1071/5680)rm 'file1'
rm 'file2'
rm 'file3'
rm 'file4'
...
在结束(预期?)错误之前:
Rewrite e6b712b57257e2edd0bb9fbbac59e4c9d7b5aa79 (1072/5680)index filter failed: (( $(git rev-list e6b712b --not $GIT_COMMIT | wc -l) > 0 )) && git rm -rf --ignore-unmatch <filename>
其中e6b712b
是B
的孩子。
此时我假设一切正常,所以我做了我的存储库的本地文件系统克隆来测试它:
git clone file://<repo> <new repo>
对象数量和packfile大小减少了很少 - 我不知道为什么。通过针对原始存储库运行git count-objects -v
而不是针对它运行filter-branch
的存储库:
原始存储库:
count: 0
size: 0
in-pack: 106640
packs: 1
size-pack: 815512
prune-packable: 0
garbage: 0
filter-branch
ed和文件系统克隆存储库:
count: 0
size: 0
in-pack: 96165
packs: 1
size-pack: 793656
prune-packable: 0
garbage: 0
我真的不确定为什么这仍然不起作用 - 也许我没有正确地遵循建议的答案?
答案 0 :(得分:1)
不幸的是,如果你真的想要从你的存储库中删除这些对象(而不是简单地从当前版本和将来的版本中删除它们),filter-branch
就是这样做的,如果你要重写commit { {1}},每次提交到其历史记录中包含A
的每个分支头部也必须重写,因为提交的提交哈希值取决于该提交的每个父级的提交哈希值。如果您不重写包含A
的所有分支,那么这些对象仍然是您可访问历史记录中某些提交的合法部分,并且它们不会被修剪。
对于其历史记录中包含A
的每个分支BR
,这应该有效:
A
将从git filter-branch --index-filter 'git rm --cached --ignore-unmatch filenames' BR --not A~1
(通过修剪A
父级的分支)重写到分支A
的当前提示。它会删除所有这些提交中的文件,即使它们被较新的较小版本替换。要将它们删除只提交BR
,您可以像这样展开过滤器脚本:
B
这使用rev-list列出当前正在重写的提交之后的所有修订,直到... --index-filter '(( $(git rev-list <SHA-of-child-of-B> --not $GIT_COMMIT | wc -l) > 0 )) && git rm ...' ...
的子项,计算这些行,并且只有B
如果一个或多个修订属于该范围(当git rm
时,将打印一行 - 因此需要使用$GIT_COMMIT == B
的孩子。
即使对于单个分支来说,这是一个相当大的变化,如果你有许多在B
之后或之后产生的分支,那么很多工作,所以你必须决定它是否最终值得,或者如果你只需要一个更大的磁盘(你没有提到这些文件的大小)。
答案 1 :(得分:0)
A is the commit where the large files were introduced
B is the commit (about 30 later) where the large files were replaced
with smaller ones
HEAD is thousands of commits forward of B (~2 years of active development)
你说过这个我会强烈反对filter-branch
,因为我相信它会重写2年的承诺SHA。也许另一个解决方案是git revert
git revert SHA_A..SHA_B
Revert the changes done by commits from commit SHA_A (included) to
SHA_B (included)