git 什么时候修剪对象:为什么 "git gc" 不删除提交?

When exactly does git prune objects: why is "git gc" not removing commits?

我正在学习一门 git 课程,想提一下丢失的 ref 直到 运行 宁 git gc 才真正丢失。但是验证了一下,发现不是这样的。即使在 运行ning git gc --prune=all --aggressive 之后,丢失的 refs 仍然存在。

显然我误会了什么。在课程中说错话之前,我想弄清楚我的事实!这是一个示例脚本,说明了效果:

 #!/bin/bash

 git init

 # add 10 dummy commits
 for i in {1..10}; do
     date > foo.txt
     git add foo.txt
     git commit -m "bump" foo.txt
     sleep 1
 done;

 CURRENT=$(git rev-parse HEAD)
 echo HEAD before reset: ${CURRENT}

 # rewind
 git reset --hard HEAD~5

 # add another 10 commits
 for i in {1..10}; do
     date > foo.txt
     git add foo.txt
     git commit -m "bump" foo.txt
     sleep 1
 done;

此脚本将添加 10 个虚拟提交,重置为过去的 5 个提交并添加另外 10 个提交。就在重置之前,它将打印当前 HEAD 的哈希值。

期望在运行宁git gc --prune=all之后丢失CURRENT中的对象。然而,我仍然可以 运行 git show 在那个散列上。

我明白在 运行 宁 git reset 并添加新提交之后,我基本上创建了一个新分支。但是我的原始分支不再有任何引用,所以它不会出现在 git log --all 中。它也不会被推送到我想的任何远程。

我对 git gc 的理解是删除那些对象。好像不是这样。

为什么? 什么时候 git gc 删除对象?

对于要 p运行ed 的对象,它必须满足 两个 条件。一个是 date/time 相关的:它一定是在 1 之前足够长的时间创建的,可以收集了。 "long enough ago" 部分是您使用 --prune=all 设置的部分:您正在覆盖正常的 "at least two weeks old" 设置。

第二个标准是你的实验哪里出了问题。要被 p运行ed,对象必须 unreachable。作为 ,您的每个表面上被放弃的提交(以及它们相应的树和 blob)实际上都通过 Git 的 "reflog" 条目被引用。

每个这样的提交都有两个 reflog 条目:一个用于 HEAD,另一个用于 HEAD 本身在提交时引用的分支名称(在本例中, refs/heads/master 的 reflog,即分支 master)。每个 reflog 条目都有自己的时间戳,并且 git gc 也会为您使 reflog 条目过期,尽管与对象过期的简单“14 天”默认设置相比,规则集更复杂。2

因此,git gc 可以首先删除所有保留旧对象的reflog条目,然后 p运行e 对象。只是这里没有发生。

要手动查看甚至删除引用日志条目,请使用 git reflog。请注意 git reflog 显示 由 运行ning git log-g / --walk-reflogs 选项的条目(加上一些额外的显示格式化选项)。您可以 运行 git reflog --all --expire=all 清除所有内容,尽管在手术刀可能更合适的情况下这是一个大棒。使用 --expire-unreachable 以获得更多选择性。有关详细信息,请参阅 the git log documentation and of course the git reflog documentation


1一些 Unix-y 文件系统根本不存储文件创建 ("birth") 时间:[=27] 的 st_ctime 字段=]结构是inode变化时间,不是创建时间。如果有创建时间,则在 st_birthtimest_birthtimespec.3 但是,每个 Git 对象都是只读的,因此文件的创建时间也是它的修改时间。因此 st_mtime 始终可用,给出对象的创建时间。

2确切的规则在the git gc documentation中有描述,但我认为默认情况下,30天不可到达提交和90天可到达提交 是一个不错的总结。不过,这里 reachable 的定义是不寻常的:它意味着 可以从引用的当前值到达,该引用日志持有旧值。 那是,如果我们正在查看 master 的 reflog,我们会找到 master 标识的提交(例如,1234567),然后查看 master 的每个 reflog 条目(例如,master@{27}) 可以从那个特定的提交到达(再次1234567)。

3这个特殊的名称混淆是由 POSIX 标准化人员给您带来的。 :-) st_birthtimespec 字段是一个 struct timespec,它记录秒和纳秒。