git gc: 尽管还有3GB可用空间且tmp_pack只有16MB,但设备上没有足够的空间。

18
> git gc --aggressive --prune=now
Counting objects: 68752, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (66685/66685), done.
fatal: sha1 file '.git/objects/pack/tmp_pack_cO6T53' write error: No space left on device

叹气,好的

df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        19G   15G  3.0G  84% /
udev            485M  4.0K  485M   1% /dev
tmpfs            99M  296K   99M   1% /run
none            5.0M     0  5.0M   0% /run/lock
none            494M     0  494M   0% /run/shm
cgroup          494M     0  494M   0% /sys/fs/cgroup

看起来并不那么糟糕

ls -lh .git/objects/pack/
total 580M
-r--r--r-- 1 foouser root  12K Oct 30 05:47 pack-0301f67f3b080de7eb0139b982fa732338c49064.idx
-r--r--r-- 1 foouser root 5.1M Oct 30 05:47 pack-0301f67f3b080de7eb0139b982fa732338c49064.pack
-r--r--r-- 1 foouser root 5.1K Oct 14 10:51 pack-27da727e362bcf2493ac01326a8c93f96517a488.idx
-r--r--r-- 1 foouser root 100K Oct 14 10:51 pack-27da727e362bcf2493ac01326a8c93f96517a488.pack
-r--r--r-- 1 foouser root  11K Oct 25 10:35 pack-4dce80846752e6d813fc9eb0a0385cf6ce106d9b.idx
-r--r--r-- 1 foouser root 2.6M Oct 25 10:35 pack-4dce80846752e6d813fc9eb0a0385cf6ce106d9b.pack
-r--r--r-- 1 foouser root 1.6M Apr  3  2014 pack-4dcef34b411c8159e3f5a975d6fcac009a411850.idx
-r--r--r-- 1 foouser root 290M Apr  3  2014 pack-4dcef34b411c8159e3f5a975d6fcac009a411850.pack
-r--r--r-- 1 foouser root  40K Oct 26 11:53 pack-87529eb2c9e58e0f3ca0be00e644ec5ba5250973.idx
-r--r--r-- 1 foouser root 6.1M Oct 26 11:53 pack-87529eb2c9e58e0f3ca0be00e644ec5ba5250973.pack
-r--r--r-- 1 foouser root 1.6M Apr 19  2014 pack-9d5ab71d6787ba2671c807790890d96f03926b84.idx
-r--r--r-- 1 foouser root 102M Apr 19  2014 pack-9d5ab71d6787ba2671c807790890d96f03926b84.pack
-r--r--r-- 1 foouser root 1.6M Oct  3 10:12 pack-af6562bdbbf444103930830a13c11908dbb599a8.idx
-r--r--r-- 1 foouser root 151M Oct  3 10:12 pack-af6562bdbbf444103930830a13c11908dbb599a8.pack
-r--r--r-- 1 foouser root 4.7K Oct 20 11:02 pack-c0830d7a0343dd484286b65d380b6ae5053ec685.idx
-r--r--r-- 1 foouser root 125K Oct 20 11:02 pack-c0830d7a0343dd484286b65d380b6ae5053ec685.pack
-r--r--r-- 1 foouser root 6.2K Oct  2 15:38 pack-c20278ebc16273d24880354af3e395929728481a.idx
-r--r--r-- 1 foouser root 4.2M Oct  2 15:38 pack-c20278ebc16273d24880354af3e395929728481a.pack
-r--r--r-- 1 root          root  16M Feb 27 08:19 tmp_pack_cO6T53

所以,即使我的磁盘上有3GB可用空间,git gc在一个只有16MB大小的临时包上就会停止工作。我错过了什么?我该如何让git gc更可靠地工作?我尝试过不使用-aggressive选项,使用--prune而不是--prune=now,结果相同。

更新

在重新打包操作期间执行df -h显示它现在正在使用我所有的磁盘(100%使用率)。一段时间后,重新打包操作失败并在.git/objects/pack/文件夹中留下另一个14MB的文件。因此,总结一下,我的包总共使用580MB。 git repack以某种方式使用3GB来重新打包。完成后我有约800MB的可用RAM。也许它使用了太多的工作内存导致交换空间被堵塞?我的问题可能归结为:是否有选项可以使git repack减少资源占用?

版本:Ubuntu 12.04上的git版本1.7.9.5

更新2

我已将git更新到2.3。遗憾的是,这没有改变任何事情。

> git --version
git version 2.3.0
> git repack -Ad && git prune
Counting objects: 68752, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (36893/36893), done.
fatal: sha1 file '.git/objects/pack/tmp_pack_N9jyVJ' write error: No space left on device

更新3

好的,我刚刚注意到一件有趣的事情:实际上.git目录使用的磁盘空间比之前报告的508MB要多得多。

> du -h -d 1 ./.git
8.0K    ./.git/info
40K ./.git/hooks
24M ./.git/modules
28K ./.git/refs
4.0K    ./.git/branches
140K    ./.git/logs
5.0G    ./.git/objects
5.0G    ./.git

仔细检查后,.git / objects / pack 实际上使用了4.5GB。区别在于我之前没有注意到的隐藏临时文件:

ls -lha ./.git/objects/pack/
total 4.5G
drwxr-xr-x   2 foouser root  56K Feb 27 15:40 .
drwxr-xr-x 260 foouser root 4.0K Oct 26 14:24 ..
-r--r--r--   1 foouser root  12K Oct 30 05:47 pack-0301f67f3b080de7eb0139b982fa732338c49064.idx
-r--r--r--   1 foouser root 5.1M Oct 30 05:47 pack-0301f67f3b080de7eb0139b982fa732338c49064.pack
-r--r--r--   1 foouser root 5.1K Oct 14 10:51 pack-27da727e362bcf2493ac01326a8c93f96517a488.idx
-r--r--r--   1 foouser root 100K Oct 14 10:51 pack-27da727e362bcf2493ac01326a8c93f96517a488.pack
-r--r--r--   1 foouser root  11K Oct 25 10:35 pack-4dce80846752e6d813fc9eb0a0385cf6ce106d9b.idx
-r--r--r--   1 foouser root 2.6M Oct 25 10:35 pack-4dce80846752e6d813fc9eb0a0385cf6ce106d9b.pack
-r--r--r--   1 foouser root 1.6M Apr  3  2014 pack-4dcef34b411c8159e3f5a975d6fcac009a411850.idx
-r--r--r--   1 foouser root 290M Apr  3  2014 pack-4dcef34b411c8159e3f5a975d6fcac009a411850.pack
-r--r--r--   1 foouser root  40K Oct 26 11:53 pack-87529eb2c9e58e0f3ca0be00e644ec5ba5250973.idx
-r--r--r--   1 foouser root 6.1M Oct 26 11:53 pack-87529eb2c9e58e0f3ca0be00e644ec5ba5250973.pack
-r--r--r--   1 foouser root 1.6M Apr 19  2014 pack-9d5ab71d6787ba2671c807790890d96f03926b84.idx
-r--r--r--   1 foouser root 102M Apr 19  2014 pack-9d5ab71d6787ba2671c807790890d96f03926b84.pack
-r--r--r--   1 foouser root 1.6M Oct  3 10:12 pack-af6562bdbbf444103930830a13c11908dbb599a8.idx
-r--r--r--   1 foouser root 151M Oct  3 10:12 pack-af6562bdbbf444103930830a13c11908dbb599a8.pack
-r--r--r--   1 foouser root 4.7K Oct 20 11:02 pack-c0830d7a0343dd484286b65d380b6ae5053ec685.idx
-r--r--r--   1 foouser root 125K Oct 20 11:02 pack-c0830d7a0343dd484286b65d380b6ae5053ec685.pack
-r--r--r--   1 foouser root 6.2K Oct  2 15:38 pack-c20278ebc16273d24880354af3e395929728481a.idx
-r--r--r--   1 foouser root 4.2M Oct  2 15:38 pack-c20278ebc16273d24880354af3e395929728481a.pack
-r--r--r--   1 root          root 1.1K Feb 27 15:37 .tmp-7729-pack-00447364da9dfe647c89bb7797c48c79589a4e44.idx
-r--r--r--   1 root          root  14M Feb 27 15:29 .tmp-7729-pack-00447364da9dfe647c89bb7797c48c79589a4e44.pack
-r--r--r--   1 root          root 1.1K Feb 27 15:32 .tmp-7729-pack-020efaa9c7caf8b792081f89b27361093f00c2db.idx
-r--r--r--   1 root          root  41M Feb 27 15:30 .tmp-7729-pack-020efaa9c7caf8b792081f89b27361093f00c2db.pack
-r--r--r--   1 root          root 1.1K Feb 27 15:37 .tmp-7729-pack-051980133b8f0052b66dce418b4d3899de0d1342.idx
(continuing for a *long* while). 

现在我想知道:直接删除它们是安全的吗?


@VonC:感谢您的帮助。是的,就是您在我的ls输出中看到的那个。没有其他包含tmp文件的对象子文件夹。 - Michel Müller
在重新打包操作期间执行 df -h 命令,它显示现在正在使用我的所有磁盘(100%使用率)。过了一会儿,重新打包操作失败,并在 .git/objects/pack/ 文件夹中留下另一个 14MB 的文件。因此,总的来说,我的包使用了总共 580MB。git repack 不知何故使用了 3GB 来重新打包。完成后,我有大约 800MB 的空闲 RAM。也许它使用了太多的工作内存,导致交换区被堵塞?我的问题归结为:是否有选项可以使 git repack 占用更少的资源? - Michel Müller
你用的是哪个版本的Git?在什么操作系统上?-- 在Ubuntu 12.04 LTS上使用的是Git版本1.7.9.5。 - Michel Müller
单独使用git repack是不够的——如果repack已经因为爆炸性增长到3GB而失败,那么这仍然没有帮助,对吧?或者你的意思是我应该连续执行这两个命令10次,因为每次都会稍微变小,这样repack就可以进一步进行了? - Michel Müller
我错过了git prepack没有完成的事实。以防万一有什么变化,您能否尝试使用git 2.3.1查看错误是否仍然存在?(http://stackoverflow.com/a/24847953/6309) - VonC
显示剩余18条评论
4个回答

8
这是我迄今所了解到的:在.git/objects/pack文件夹中,我找不到关于这些隐藏的'.tmp-XXXX-pack'的任何文档说明。我发现的所有其他线程都是关于同一文件夹中带有tmp_前缀的非隐藏文件。隐藏文件显然也是在重新打包操作期间创建的,它们可能也会出现卡住的情况。我无法证实是否仍然可能在git 2.3.0(我已经更新)中发生,但至少磁盘空间要求似乎在这个更新版本中没有改变,还是不能完成gc/repack操作。通过删除这些.tmp文件,我能够恢复我的最后4GB,并且在此之后git看起来还表现良好--你的结果可能会有所不同,因此请确保在执行此操作之前备份数据。最后,即使4GB对于使用gc --aggressive进行重新打包也不足够。清理后,我的.git文件夹大小为1.1GB,整个仓库大小为1.7GB。因此,即使使用侵略性选项(应该节省空间),对于git gc来说,大约需要两倍于你的代码库大小的存储空间。因此,我必须先从其他地方回收更多的空间。
这是我用于清理的命令(再次提醒备份数据!):
git gc --aggressive --prune=now || rm -f .git/objects/*/tmp_* && rm -f .git/objects/*/.tmp-*

2
很有趣的反馈,不过我确认仅执行 git gc 是不够的。必须同时执行 git repack -Adgit prune,详见 https://dev59.com/xl4d5IYBdhLWcg3wCOyD#28563637。 - VonC
1
从这个答案中,我只想强调在执行此操作之前,请确保您已备份。删除文件.git/objects/*/.tmp-*使我的存储库无法修复:所有git命令现在都会给出“fatal: bad object HEAD.”,git fsck报告大量丢失的blob,并尝试将此损坏的repo克隆到其他地方也没有成功。我相信我刚刚失去了整个git历史记录。 - unagi
在这里,set -e 的价值是什么?它对已检查的操作没有影响,而唯一未经检查的操作是最后一个命令,该命令的退出状态将用作整个脚本的退出状态,无论如何。(另请参见BashFAQ#105,讨论为什么set -e使脚本的行为更难以预测,无论是作者还是读者)。我也不确定为什么您希望通过在上述gc失败后返回成功的退出状态来掩盖git gc的失败,如果第一个rm在此之后成功。 - Charles Duffy
@CharlesDuffy:同意,将其用作脚本存在健壮性问题。set -e 只是因为我在每个脚本的开头使用它。我不喜欢没有它的替代方案,然后在每个操作之后忘记检查返回代码。所以对我来说,这只是bash作为脚本语言存在缺陷的结果。不过,感谢您的反馈,我会修改答案。 - Michel Müller

7
类似的情况(大约有2.3G可用),但是git gc本身也会失败,并显示fatal: Unable to create '/home/ubuntu/my-app-here/.git/gc.pid.lock': No space left on device
解决方法是先运行git prune,然后再运行gc。

谢谢,这帮助我解决了问题。 - Ajith

2
我曾经遇到过这个问题。我成功地释放了大量的磁盘空间,但当然,这并没有解决.tmp-*文件的问题。我运行了git fsck,Git仓库没有遭受损坏。
我进行了传统的打包和垃圾回收操作。
git repack -Ad
git prune

但这并没有删除.tmp-*文件,尽管如果需要从过去崩溃的Git进程留下的临时文件中复制所有必要的对象到标准pack-*文件中,它将确保所有必要的对象都在其中。

最终我意识到我可以安全地将.tmp-*文件移动到一个临时目录,然后运行git fsck来查看.git目录中剩余的内容是否完整。结果证明它是完整的,所以我删除了临时目录和它包含的文件。如果git fsck报告有问题,我可以将.tmp-*文件移回.git目录并研究另一种解决方案。


谢谢,我认为那应该是一个比我以前所做的更为简洁的解决方案。 - Michel Müller

2
隐藏的文件在重新打包时会被创建,这些文件也可能会被卡住。"git repack"(man) 接收到信号时创建临时文件的方式容易发生死锁,Git 2.39 (Q4 2022) 已经进行了修正。
详情请见 commit 9b3fadf (2022 年 10 月 23 日),以及 commit 1934307commit 9cf10d8commit a4880b2commit b639606commit d3d9c51 (2022 年 10 月 21 日),由 Jeff King (peff) 提交。
(由 Taylor Blau -- ttaylorr -- 合并于 commit c88895e,2022 年 10 月 30 日)

repack:使用临时文件进行信号清理

报告者:Jan Pokorný
签署者:Jeff King

git-repack(手册)由于收到信号而退出时,它会尝试通过调用其remove_temporary_files()函数进行清理,该函数遍历包目录以查找要删除的“.tmp-$$-pack-*”文件(其中“$$”是当前进程的pid)。

这里最大的问题是remove_temporary_files()不安全,不能在信号处理程序中调用。
它使用opendir(),而这个函数不在POSIX async-signal-safe列表中。
具体细节会因平台而异,但一个可能的问题是需要分配内存;如果我们在malloc()等函数内部接收到信号,则会在分配器锁上发生冲突并与自己死锁。

我们可以通过直接清理文件而不遍历目录来解决这个问题。
我们已经知道生成的.tmp-*文件的完整列表,因为我们通过populate_pack_exts()记录了它们。
当我们在那里找到文件时,我们可以使用register_tempfile()记录文件名。
如果我们接收到信号,则临时文件API将为我们清理它们,而且它是异步安全和相当经过实战检验的。

以及:

repack:删除remove_temporary_files()

签署者:Jeff King

在成功完成重新打包之后,我们调用remove_temporary_files()函数,该函数查找并删除与".tmp-$$-pack-*"匹配的任何文件,其中$$是当前进程的pid。
但这是毫无意义的。
如果我们在此过程中成功了,我们已经将这些临时文件重命名,并且没有什么需要删除的了。

当我们不成功时,尝试调用它进行清理也没有意义。
它在信号处理程序中使用不安全,而之前的提交已经将该任务交给了tempfile API。

清理其他调用git-repack留下的杂散的.tmp文件似乎很有用。
但它不会清理这些文件;它只匹配具有其pid的文件,并保留其余文件。
幸运的是,这些文件会在后续对git-repack的调用中自然清理;我们将认为.tmp-*.pack与普通pack文件相同,因此"repack -ad"等命令将合并它们的内容并最终删除它们。


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