我正在开设一个git课程并想提一下,在运行git gc
之前,丢失的参考文件并没有真正丢失。但验证这一点,我发现事实并非如此。即使在运行git gc --prune=all --aggressive
之后,丢失的裁判仍在那里。
显然我误解了一些事情。在说出不正确的课程之前,我想直截了当地了解事实!这是一个示例脚本说明效果:
#!/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的哈希值。
运行CURRENT
后,我期望在git gc --prune=all
中丢失对象。但是,我仍然可以在该哈希上运行git show
。
我明白在运行git reset
并添加新提交后,我基本上创建了一个新分支。但我的原始分支不再有任何引用,因此它不会显示在git log --all
中。它也不会被推到我想的任何遥控器上。
我对git gc
的理解是删除了这些对象。情况似乎并非如此。
为什么呢? 当确实git gc
删除对象时
答案 0 :(得分:15)
对于要修剪的对象,它必须符合两个条件。一个是与日期/时间相关的:它必须在足够长的时间之前创建 1 以便收集成熟。 “足够久以前”部分是您使用--prune=all
设置的部分:您将覆盖正常的“至少两周”设置。
第二个标准是您的实验出错的地方。要进行修剪,该对象必须 无法访问。作为twalberg noted in a comment,您通过Git的“reflog”条目实际引用了每个表面上被放弃的提交(以及它们对应的树和blob)。
每个此类提交有两个reflog条目:一个用于HEAD
,另一个用于HEAD
在提交时自己引用的分支名称(在本例中为reflog)对于refs/heads/master
,即分支master
)。每个reflog条目都有自己的时间戳,git gc
也会为你过期reflog条目,尽管有一套更复杂的规则,而不是对象到期的简单“14天”默认值。 2
因此,git gc
可以首先删除所有保留旧对象的reflog条目,然后修剪对象。这不会发生在这里。
要手动查看甚至删除reflog条目,请使用git reflog
。请注意,git reflog
通过使用git log
/ -g
选项运行--walk-reflogs
来显示条目(以及一些其他显示格式选项)。您可以运行git reflog --all --expire=all
来清除所有内容,但是当手术刀可能更合适时,这是一个大棒。使用--expire-unreachable
可获得更多选择性。有关详情,请参阅the git log
documentation,当然还有the git reflog
documentation。
1 有些Unix-y文件系统根本不存储文件创建(“出生”)时间:st_ctime
结构的stat
字段是 inode更改时间,而不是创建时间。如果有创建时间,则它在st_birthtime
或st_birthtimespec
。 3 但是,每个Git对象都是只读的,因此文件的创建时间也是其修改时间。因此, 始终可用的st_mtime
为对象提供了创建时间。
2 the git gc
documentation中描述了确切的规则,但我认为默认情况下,无法访问提交30天,可访问提交90天是一个不错的选择摘要。这里 reachable 的定义是不寻常的:它意味着可以从引用的当前值到达,此reflog包含旧值。也就是说,如果我们正在寻找在master
的reflog中,我们找到master
标识的提交(例如,1234567
),然后查看master
的每个reflog条目(例如master@{27}
可以从该特定提交(<{em>}再次发送。)
3 这个特殊的名称混淆是由POSIX标准化人员提出的。 :-) 1234567
字段是st_birthtimespec
,它记录秒和纳秒。