git gc --aggressive --prune=all无法从存储库中删除大文件

18

有许多关于“如何从存储库中删除意外添加的大文件”的SO问题,其中许多建议使用git gc命令。 但是,我发现它对我不起作用,我不知道出了什么问题。

以下是我所做的:

$ git init
Initialized empty Git repository in /home/wzyboy/git/myrepo/.git/
$ echo hello >> README
$ git add README 
$ git commit -a -m 'init commit'
[master (root-commit) f21783f] init commit
 1 file changed, 1 insertion(+)
 create mode 100644 README
$ du -sh .git
152K    .git
$ cp ~/big.zip .
$ git add big.zip 
$ git commit -a -m 'adding big file'
[master 3abd0a4] adding big file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 big.zip
$ du -sh .git
77M .git
$ git log --oneline 
3abd0a4 adding big file
f21783f init commit
$ git reset --hard f21783f
HEAD is now at f21783f init commit
$ git log --oneline 
f21783f init commit
$ git gc --aggressive --prune=all
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 0), reused 0 (delta 0)
$ git gc --aggressive --prune=now
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), done.
Total 6 (delta 0), reused 6 (delta 0)
$ du -sh .git
77M .git
$ git version
git version 2.2.2
在上面的控制台输出中,我创建了一个新的git仓库,添加了一个小文本文件,.git目录的大小为152K,一切都很好。然后我将一个大文件添加到仓库中,目录膨胀到77M。但是,在我的尝试中移除大文件(git reset --hardgit rebase -i)后,无论我如何使用不同选项运行git gc,都不能恢复大文件所占用的磁盘空间。
有人可以告诉我为什么git gc在我的情况下不起作用吗?我应该怎么做才能恢复磁盘空间?是否可能使用git gc而不是git filter-branch来恢复磁盘空间?
谢谢。

5
git reflog expire --expire=now --all 可以翻译为:清除所有 Git 引用日志记录,包括过期的记录。 - Andrew C
@AndrewC 它可以工作了!谢谢! - Zhuoyun Wei
2个回答

31

如Andrew C所建议的那样,在git gc可以回收松散对象之前,需要过期reflog以取消引用这些对象。 因此,恢复因意外添加大文件而占用的磁盘空间的正确方法是:

按照以下步骤操作:

git reflog expire --expire=now --all
git gc --aggressive --prune=now

这将删除所有引用日志,请谨慎使用。


1
这将删除所有的引用日志。你能解释一下为什么这可能会成为一个问题吗? - eri0o
2
@eri0o 如果您意外地搞砸了您的git历史记录(例如删除未合并的分支),您可以使用reflogs进行恢复。 - Zhuoyun Wei
我们之后应该执行 git push -f 吗? - alper
@alper 不需要。GC 命令会处理 "garbages",它们无论如何都不会被推送到远程。此外,大多数 Git 远程端 (GitHub、GitLab 等) 会自动处理 GC,因此您无需担心它们的垃圾。 - Zhuoyun Wei

1

一个小技巧可以帮助避免任何打字错误,在Git 2.18(2018年第二季度)中,就是避免使用不存在的引用(此处称为:“nonsense”)来执行gc prune

git gc --prune=nonsense”花费了很长时间进行重打包,并在基础的“git prune --expire=nonsense”无法解析其命令行时悄悄失败。
这已经得到了纠正。

请看 提交 96913c9(2018年4月23日),作者是Junio C Hamano (gitster)
协助者:Linus Torvalds (torvalds)
(由Junio C Hamano -- gitster --提交 3915f9a中合并,于2018年5月8日)

parseopt: handle malformed --expire arguments more nicely

A few commands that parse --expire=<time> command line option behave sillily when given nonsense input.
For example

$ git prune --no-expire
Segmentation falut
$ git prune --expire=npw; echo $?
129

Both come from parse_opt_expiry_date_cb().

The former is because the function is not prepared to see arg==NULL (for "--no-expire", it is a norm; "--expire" at the end of the command line could be made to pass NULL, if it is told that the argument is optional, but we don't so we do not have to worry about that case).

The latter is because it does not check the value returned from the underlying parse_expiry_date().


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接