我不小心把一个dvd光盘放到了一个网站项目中,然后不小心提交-a -m…而且,快,回购膨胀了2.2 g。下次我做了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然在存储库中,在历史中。

我知道我可以从这些提交中启动分支,并将一个分支重置到另一个分支上。但是我应该怎么做才能合并两次提交,使大文件不显示在历史记录中,并在垃圾收集过程中被清理?


当前回答

当您遇到这个问题时,git rm是不够的,因为git会记住这个文件在我们的历史中曾经存在过一次,因此会保留对它的引用。

更糟糕的是,重基也不容易,因为任何对blob的引用都会阻止git垃圾收集器清理空间。这包括远程引用和reflog引用。

我把git forget-blob放在一起,一个尝试删除所有这些引用的小脚本,然后使用git filter-branch重写分支中的每个提交。

一旦你的blob完全没有被引用,git gc就会删除它

它的用法很简单,git forget-blob file-to-forget。你可以在这里获得更多信息

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

多亏了Stack Overflow和一些博客的回答,我把这些放在了一起。感谢他们!

其他回答

这些命令在我的案例中起作用:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

它与上面的版本没有什么不同。

对于那些需要把这个推到github/bitbucket的人(我只用bitbucket测试了这个):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work

除了git filter-branch(缓慢但纯粹的git解决方案)和BFG(更简单,性能非常好)之外,还有另一个性能良好的过滤工具:

https://github.com/xoofx/git-rocket-filter

从它的描述来看:

git-rocket-filter的目的类似于git-filter-branch命令,但提供了以下独特的功能:

快速重写提交和树(从x10到x100的顺序)。 内置支持使用——keep(保存文件或目录)的白名单和使用——remove选项的黑名单。 使用.gitignore类似的模式进行树过滤 快速和简单的c#脚本提交过滤和树过滤 支持每个文件/目录模式的树过滤脚本 自动修剪空的/不变的提交,包括合并提交

你可以使用branch filter命令:

git filter-branch -tree-filter 'rm -rf path/to/your/file' HEAD

我基本上按照这个答案做了: https://stackoverflow.com/a/11032521/1286423

(对于历史,我复制粘贴在这里)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

这并没有起作用,因为我喜欢重命名和移动东西。一些大文件在重命名的文件夹中,我认为gc不能删除对这些文件的引用因为树对象中的引用指向这些文件。 我最终的解决方法是:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

我的repo (.git)从32MB变成了388KB,即使过滤器分支也无法清理。

比git的filter-branch快100倍,更简单

在这个帖子里有很多很好的答案,但同时很多都过时了。不再推荐使用git-filter-branch,因为它很难使用,而且在大型存储库上非常慢。

Git-filter-repo使用起来更快更简单。

git-filter-repo是一个Python脚本,可以在github: https://github.com/newren/git-filter-repo上获得。安装时,它看起来像一个普通的git命令,可以由git filter-repo调用。

您只需要一个文件:Python3脚本git-filter-repo。将其复制到path变量中包含的路径。在Windows上,您可能需要更改脚本的第一行(请参阅INSTALL.md)。您需要在系统上安装Python3,但这不是什么大问题。

首先你可以跑

git filter-repo --analyze

这可以帮助你决定下一步要做什么。

你可以在任何地方删除你的DVD-rip文件:

git filter-repo --invert-paths --path-match DVD-rip
 

Filter-repo非常快。一个在我的电脑上用filter-branch花了9个小时的任务,用filter-repo只用了4分钟就完成了。你可以用filter-repo做更多的事情。请参阅相关文档。

警告:在存储库的副本上执行此操作。filter-repo的许多操作不能撤消。Filter-repo将更改所有修改过的提交(当然)及其所有后代直到最后一次提交的提交哈希值!