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

git gc --aggressive --prune=all does not remove big file from repository

有很多关于 "how to remove an accidentally added big file from repo" 的问题,其中许多建议使用 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,目前一切顺利。然后我在 repo 中添加了一个大文件,目录膨胀到 77M。然而,在我尝试删除大文件(git reset --hardgit rebase -i)之后,无论我如何 运行 git gc 有不同的选项。

谁能告诉我为什么 git gc 对我的情况不起作用?我应该如何恢复磁盘space?是否可以使用 git gc 而不是 git filter-branch 来恢复磁盘 space?

谢谢。

正如 Andrew C 所建议的,在 git gc 能够回收松散对象之前,需要使 reflog 过期以取消引用对象。所以恢复磁盘 space 被意外添加的大文件占用的正确方法是:

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

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

Git 2.18(2018 年第 2 季度)可以帮助避免任何打字错误的一个技巧是避免 gc prune 具有不存在的引用(此处称为:“nonsense”)

"git gc --prune=nonsense" 花了很长时间重新打包,然后当底层 "git prune --expire=nonsense" 无法解析其命令行时静默失败。
这已得到纠正。

参见 commit 96913c9 (23 Apr 2018) by Junio C Hamano (gitster)
帮助:Linus Torvalds (torvalds).
(由 Junio C Hamano -- gitster -- in commit 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().