我不小心把一张 DVD 翻录到一个网站项目中,然后不小心git commit -a -m ...
,然后,这个 repo 被 2.2 gigs 膨胀了。下次我进行了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然存在于存储库中,在历史记录中。
我知道我可以从这些提交开始分支并将一个分支重新定位到另一个分支。但是我应该怎么做才能合并 2 个提交,以便大文件不会显示在历史记录中并在垃圾收集过程中被清理?
我不小心把一张 DVD 翻录到一个网站项目中,然后不小心git commit -a -m ...
,然后,这个 repo 被 2.2 gigs 膨胀了。下次我进行了一些编辑,删除了视频文件,并提交了所有内容,但压缩文件仍然存在于存储库中,在历史记录中。
我知道我可以从这些提交开始分支并将一个分支重新定位到另一个分支。但是我应该怎么做才能合并 2 个提交,以便大文件不会显示在历史记录中并在垃圾收集过程中被清理?
使用BFG Repo-Cleaner,这是一种更简单、更快的替代方案,git-filter-branch
专门用于从 Git 历史记录中删除不需要的文件。
仔细按照使用说明进行操作,核心部分就是这样:
$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git
任何超过 100MB 的文件(不在您的最新提交中)都将从您的 Git 存储库的历史记录中删除。然后,您可以使用git gc
清除死数据:
$ git gc --prune=now --aggressive
BFG 通常至少比running快10-50 倍git-filter-branch
,并且通常更易于使用。
全面披露:我是 BFG Repo-Cleaner 的作者。
为什么不使用这个简单而强大的命令呢?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
该--tree-filter
选项在每次签出项目后运行指定的命令,然后重新提交结果。在这种情况下,您从每个快照中删除一个名为 DVD-rip 的文件,无论它是否存在。
如果您知道哪个提交引入了大文件(例如 35dsa2),则可以将 HEAD 替换为 35dsa2..HEAD 以避免重写太多历史记录,从而避免在尚未推送时出现分歧提交。@alpha_989 的这条评论似乎太重要了,不能在这里省略。
请参阅此链接。
(我看到的这个问题的最佳答案是:https ://stackoverflow.com/a/42544963/714112 ,复制到这里,因为这个帖子在谷歌搜索排名中看起来很高,但另一个没有)
此 shell 脚本显示存储库中的所有 blob 对象,从小到大排序。
对于我的示例存储库,它的运行速度比此处找到的其他存储库快约100 倍。
在我信赖的 Athlon II X4 系统上,它只需一分钟多一点就可以处理包含 5,622,155 个对象的Linux 内核存储库。
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
当你运行上面的代码时,你会得到很好的人类可读的输出,如下所示:
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
假设您随后想要删除文件a
,并且b
从 可访问的每个提交中HEAD
,您可以使用以下命令:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
在尝试了 SO 中的几乎所有答案之后,我终于找到了这个可以快速删除并删除我的存储库中的大文件并允许我再次同步的 gem:http ://www.zyxware.com/articles/4027/how-to-delete -files-permanently-from-your-local-and-remote-git-repositories
CD 到您的本地工作文件夹并运行以下命令:
git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all
将 FOLDERNAME 替换为您希望从给定 git 存储库中删除的文件或文件夹。
完成此操作后,运行以下命令来清理本地存储库:
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now
现在将所有更改推送到远程存储库:
git push --all --force
这将清理远程存储库。
此线程中有很好的答案,但同时其中许多已过时。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 将更改所有已修改提交(当然)及其所有后代的提交哈希,直到最后一次提交!
这些命令在我的情况下有效:
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
根据 GitHub Documentation,只需按照以下步骤操作:
选项 1:您不想保留大文件:
rm path/to/your/large/file # delete the large file
选项 2:您希望将大文件保存到未跟踪的目录中
mkdir large_files # create directory large_files
touch .gitignore # create .gitignore file if needed
'/large_files/' >> .gitignore # untrack directory large_files
mv path/to/your/large/file large_files/ # move the large file into the untracked directory
git add path/to/your/large/file # add the deletion to the index
git commit -m 'delete large file' # commit the deletion
git filter-branch --force --index-filter \
"git rm --cached --ignore-unmatch path/to/your/large/file" \
--prune-empty --tag-name-filter cat -- --all
git push <remote> <branch>
我使用 bitbucket 帐户遇到了这个问题,我不小心在其中存储了我网站的大量 *.jpa 备份。
git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all
与MY-BIG-DIRECTORY
有问题的文件夹相对,以完全重写您的历史记录(包括标签)。
请注意,此命令可能非常具有破坏性。如果有更多的人在进行回购,他们都必须拔出新树。如果您的目标不是减小大小,则不需要三个中间命令。因为过滤器分支创建了被删除文件的备份,并且它可以在那里保留很长时间。
$ 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
如果您知道您的提交是最近的,而不是遍历整个树,请执行以下操作:
git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD
这会将其从您的历史记录中删除
git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
使用Git Extensions,它是一个 UI 工具。它有一个名为“查找大文件”的插件,该插件可以在存储库中查找大型文件并允许永久删除它们。
在使用此工具之前不要使用 'git filter-branch',因为它无法找到被 'filter-branch' 删除的文件(尽管 'filter-branch' 不会从存储库包文件中完全删除文件) .
我基本上做了这个答案: 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 无法删除对这些文件的引用,因为引用了tree
指向这些文件的对象。我真正杀死它的最终解决方案是:
# 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 (the .git
) 从 32MB 变为 388KB,甚至 filter-branch 都无法清理。
git filter-branch
是一个强大的命令,您可以使用它从提交历史记录中删除一个大文件。该文件将保留一段时间,Git 将在下一次垃圾回收中将其删除。以下是从提交历史中删除文件的完整过程。为了安全起见,下面的过程首先在新分支上运行命令。如果结果是您需要的,则将其重置回您实际想要更改的分支。
# Do it in a new testing branch
$ git checkout -b test
# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD
# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test
# Remove test branch
$ git branch -d test
# Push it with force
$ git push --force origin master
您可以使用以下branch filter
命令执行此操作:
git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
当你遇到这个问题时,git rm
这还不够,因为 git 记得该文件在我们的历史中存在过一次,因此会保留对它的引用。
更糟糕的是,变基也不容易,因为对 blob 的任何引用都会阻止 git 垃圾收集器清理空间。这包括远程引用和 reflog 引用。
我整理了git forget-blob
一个尝试删除所有这些引用的小脚本,然后使用 git filter-branch 重写分支中的每个提交。
一旦你的 blob 完全没有被引用,git gc
就会摆脱它
用法很简单git forget-blob file-to-forget
。你可以在这里获得更多信息
感谢 Stack Overflow 的回答和一些博客文章,我把这些放在一起。归功于他们!
除了git filter-branch
(缓慢但纯粹的 git 解决方案)和BFG(更简单且非常高效)之外,还有另一个过滤工具具有良好的性能:
https://github.com/xoofx/git-rocket-filter
从它的描述来看:
git-rocket-filter 的目的与命令类似,git-filter-branch
同时提供以下独特功能:
git reset --soft HEAD~1
它将保留更改但删除提交,然后您可以重新提交这些更改。