Answers:
使用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通常比运行速度快至少10-50倍git-filter-branch
,并且通常更易于使用。
完全公开:我是BFG Repo-Cleaner的作者。
git push --force
在您执行步骤之后进行,否则远程仓库仍然不会更改。
git push --force
。另外值得注意的是:远程可能不允许强制推送(默认情况下,gitlab.com不允许。必须“取消保护”分支)。
如果您已将历史记录发布给其他开发人员,那么您想要做的就是极具破坏性的。有关修复历史记录后的必要步骤,请参阅文档中的“从上游变基恢复”git rebase
。
您至少有两个选择:git filter-branch
和交互式变基,这两个都在下面说明。
git filter-branch
我从Subversion导入中获取庞大的二进制测试数据时遇到了类似的问题,并写了关于从git存储库中删除数据的信息。
说您的git历史记录是:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
请注意,这git lola
是一个非标准但非常有用的别名。使用该--name-status
开关,我们可以看到与每个提交相关联的树修改。
在“ Careless”提交中(SHA1对象名称为ce36c98),该文件oops.iso
是偶然添加的DVD-rip,并在下一个提交cb14efd中删除。使用上述博客文章中描述的技术,要执行的命令是:
git filter-branch --prune-empty -d /dev/shm/scratch \
--index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
--tag-name-filter cat -- --all
选项:
--prune-empty
删除由于过滤操作而变为空的提交(即,不更改树)。在典型情况下,此选项会产生更清晰的历史记录。-d
命名一个尚不存在的临时目录,以用于构建过滤的历史记录。如果您在现代Linux发行版上运行,则在其中指定树/dev/shm
将加快执行速度。--index-filter
是主要事件,并且会在历史记录的每个步骤中与索引相对应。您想删除oops.iso
找到的所有内容,但并非所有提交中都包含该内容。该命令git rm --cached -f --ignore-unmatch oops.iso
删除存在的DVD-rip,否则不会失败。--tag-name-filter
描述了如何重写标签名称。过滤器cat
是标识操作。您的存储库与上面的示例一样,可能没有任何标签,但是出于全面考虑,我包括了此选项。--
指定选项的结尾 git filter-branch
--all
以下--
是所有裁判的简写。像上面的示例一样,您的存储库可能只有一个ref(主文件),但是出于全面考虑,我包括了此选项。经过一番搅拌,现在的历史是:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A login.html
| * cb14efd Remove DVD-rip
| | D oops.iso
| * ce36c98 Careless
|/ A oops.iso
| A other.html
|
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
请注意,仅添加了新的“ Careless”提交other.html
,并且“ Remove DVD-rip”提交不再位于master分支上。标记为分支的分支refs/original/refs/heads/master
包含您的原始提交,以防万一您出错。要删除它,请按照“收缩存储库清单”中的步骤进行操作。
$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now
对于更简单的选择,克隆存储库以丢弃不需要的位。
$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo
使用file:///...
克隆URL复制对象而不是仅创建硬链接。
现在您的历史记录是:
$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A login.html
* e45ac59 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
前两个提交(“索引”和“管理页面”)的SHA1对象名称保持不变,因为过滤操作未修改这些提交。“无忧无虑”丢失了oops.iso
,“登录页面”有了新的父代,因此他们的SHA1 确实发生了变化。
具有以下历史:
$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A login.html
* cb14efd Remove DVD-rip
| D oops.iso
* ce36c98 Careless
| A oops.iso
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
您想oops.iso
从“无忧无虑”中删除就好像从未添加它一样,然后“删除DVD-rip”对您无用。因此,我们计划进行交互式基础调整的是保留“管理页面”,编辑“无忧无虑”并丢弃“删除DVD-rip”。
运行将$ git rebase -i 5af4522
启动具有以下内容的编辑器。
pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
执行我们的计划,我们将其修改为
edit ce36c98 Careless
pick f772d66 Login page
# Rebase 5af4522..f772d66 onto 5af4522
# ...
也就是说,我们用“ Remove DVD-rip”删除该行,并将“ Careless”上的操作更改为edit
而不是pick
。
保存退出编辑器后,在命令提示符处显示以下消息。
Stopped at ce36c98... Careless
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
消息告诉我们,我们处于我们要编辑的“ Careless”提交中,因此我们运行两个命令。
$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue
第一个从索引中删除有问题的文件。第二个将“ Careless”修改或修改为更新的索引,并-C HEAD
指示git重用旧的提交消息。最后,git rebase --continue
进行其余的rebase操作。
这提供了以下历史记录:
$ git lola --name-status
* 93174be (HEAD, master) Login page
| A login.html
* a570198 Careless
| A other.html
* 5af4522 Admin page
| A admin.html
* e738b63 Index
A index.html
这就是你想要的。
-f
(或--force
)选项git push
:“通常,该命令拒绝更新不是用于覆盖它的本地引用的祖先的远程引用。该标志禁用检查。这可能导致远程存储库丢失提交。小心使用。”
... "git rm --cached -rf --ignore-unmatch path/to/dir"...
为什么不使用这个简单但功能强大的命令?
git filter-branch --tree-filter 'rm -f DVD-rip' HEAD
该--tree-filter
选项在每次签出项目后运行指定的命令,然后重新提交结果。在这种情况下,请从每个快照中删除一个名为DVD-rip的文件,无论该文件是否存在。
如果您知道哪个提交引入了巨大的文件(例如35dsa2),则可以将HEAD替换为35dsa2..HEAD,以避免重写过多的历史记录,从而避免在尚未推送的情况下分散提交。@ alpha_989提供的此评论似乎太重要了,不能在此处省略。
请参阅此链接。
fatal: bad revision 'rm'
,我使用"
而不是修复了该问题'
。总体指挥官:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
commit
将文件放入的位置(例如35dsa2
),则可以替换HEAD
为35dsa2..HEAD
。tree-filter
比index-filter
这种方式慢得多,它不会尝试检出所有提交并重写它们。如果您使用HEAD,它将尝试这样做。
(我见过的关于此问题的最佳答案是:https : //stackoverflow.com/a/42544963/714112,请复制到此处,因为该线程在Google搜索排名中排名很高,而其他线程则没有)
此shell脚本显示存储库中的所有blob对象,从最小到最大排序。
对于我的示例存储库,它的运行速度比此处找到的其他存储库快100倍。
在我值得信赖的Athlon II X4系统上,它在短短一分钟内处理了Linux Kernel存储库及其5,622,155个对象。
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
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD
蝙蝠的工作命令权
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 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
在尝试了SO中的几乎所有答案之后,我终于找到了这个宝石,该宝石可以快速删除并删除存储库中的大文件,并允许我再次同步:http : //www.zyxware.com/articles/4027/how-to-delete永久地从您的本地文件和远程git存储库中下载文件
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 --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 rm --cached files
。格雷格·培根(Greg Bacon)的命题比较完整,与我的命题完全一样,但是在您多次使用过滤分支的情况下,他错过了--force索引,并且他写了很多信息,我的版本就像简历它的。
-f
不只是选择-rf
在这里git rm --cached -rf --ignore-unmatch oops.iso
,而不是git rm --cached -r --ignore-unmatch oops.iso
按照@ lfender6445如下
请注意,此命令可能具有很大的破坏性。如果更多的人在回购上工作,他们都将不得不拉新的树。如果您的目标不是减小大小,则不需要三个中间命令。由于filter分支会创建已删除文件的备份,因此可以在其中保留很长时间。
$ 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 --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
而不是您的代码中的第一个命令
git filter-branch --tree-filter 'rm -f path/to/file' HEAD
尽管我遇到了与此处描述的问题相同的问题,但我通过遵循此建议解决了问题,但对我来说效果很好。
这本pro-git书包含一整章有关重写历史的内容 -查看“ filter-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
有问题的文件夹进行重新整理,以完全重写您的历史记录(包括标签)。
来源:https : //web.archive.org/web/20170727144429/http : //naleid.com : 80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/
这会将其从您的历史记录中删除
git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
我基本上做了这个答案的事情: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
我的存储库(.git
)从32MB更改为388KB,即使过滤器分支也无法清除。
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
使用Git Extensions,它是一个UI工具。它有一个名为“查找大文件”的插件,可以在存储库中找到标记文件,并允许永久删除它们。
在使用此工具之前,请勿使用“ git filter-branch”,因为它无法找到被“ filter-branch”删除的文件(全部“ filter-branch”无法从存储库包文件中完全删除文件) 。
在这个线程中有很好的答案,但是与此同时,许多答案已经过时了。使用git-filter-branch
不再推荐,因为它是很难使用和大库非常缓慢。
git-filter-repo
更快,更简单地使用。
git-filter-repo
是Python脚本,可在github:https : //github.com/newren/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-repo在4分钟内完成了一项任务,该任务在我的计算机上通过filter-branch花费了大约9个小时。您可以使用filter-repo做更多的事情。请参阅该文档。
警告:在您的存储库副本上执行此操作。filter-repo的许多操作无法撤消。filter-repo将更改所有修改的提交(当然)及其所有后代的提交哈希值,直到最后一次提交!
当您遇到此问题时,这是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
同时提供以下独特功能: