从 git commit 中永久删除某些文件
Howard Yin 2025-03-16 00:53:05 编程语言Git实操
# 背景
最近在帮别人看他的一个项目,项目通过 git 管理。在我 git clone
到本地的时候,发现这个工程目录十分大。经过分析,他将很多无谓的文件也 commit 上去,例如:
venv/
*.pyc
.idea/
1
2
3
2
3
等等目录,这些文件以及目录并没有必要上传,于是我便和他沟通,征得他同意后开始大改...
一般情况下,并不建议执行 git push -f
这个操作
# 删除指定文件/文件夹
如果我新增一个 commit 把这些不必要的文件删掉,并且使用 .gitignore
来主动忽略,这是可行的。不过这些文件依然会在 commit 历史中出现,别人 git clone
的时候也会被下载下来,因此打算从 commit 历史中完全删掉这些文件,就像他们从来没有出现过一样。
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch venv/ -r' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now
git push --force --verbose --dry-run
git push --force
1
2
3
4
5
6
2
3
4
5
6
其中,要删去的是目录 venv/
,由于是目录所以我加上了 -r
参数。
# 删除除指定的文件/文件夹外的所有文件
要删除所有文件,一个个的删麻烦不说还容易漏掉在历史里标记为删除HEAD里不显示的文件。 所以换个思路,导出要保存的文件的历史记录:
git fast-export HEAD -- `find code/ venv/ -type f` > ../his.fi
1
然后在别的地方新建一个库导入之:
git init
git fast-import < ../his.fi
1
2
2
# 缺陷
正如上面所提到的,一定要尽可能少用这个功能,虽然他可以减少我们 git 目录的大小,但是这会对其他协作者产生极大的影响。
所有受到影响的 commit 的 ID 都会被重写,另外如果 commit 像我这样有 GPG 签名的话,就无法执行这个操作了。