如何防止 Git 对象被垃圾回收?

3
据我所理解,任何对象在没有引用时都会被垃圾回收。那么,防止数据库中需要保留的对象被回收的最佳方法是什么?
一个使用案例是,在拉取请求中进行更改(可能是根据代码审查),之前的提交变得分离,它们不会合并到存储库中,但是始终应该可用,以便跟踪拉取请求中的更改。
例如:
- CommitA 修复了一个 bug - 为此创建一个拉取请求 - 某人进行审核并建议更改,链接到代码中的特定行 - 更改代码,修改 CommitA 并重新提交为 CommitA2
现在,CommitA2 将成为更改历史记录中的内容,但拉取请求仍将有一个指向旧 CommitA 的链接。几年后,我们希望能够查看拉取请求的内容以及其评论所指的内容。
如何防止 GC 回收提交?

给它打上标签是我想到的第一个解决方案。


1
我非常确定你想保持提交可访问性。给它或者它的任意一个后代一个引用。我在本地仓库中故意使用非共享的分支名称来保留提交记录。 - evolutionxbox
是的,使用标签是最好的解决方案,因为如果涉及到归档提交,你不希望使用可能会被错误更新的分支引用。 - Philippe
2个回答

2
从Git 2.42(2023年第三季度)开始,"git pack-objects"(man) 学会了调用一个新的钩子程序,该程序枚举额外的对象,以作为锚定点,以保留杂乱包中本来无法访问的对象。
换句话说,在git gc之后,您可以记录您不希望消失的对象。
cat refs/pull/137/head > ./precious-objects
pr137=$(cat refs/pull/137/head)
rm -Rf refs/pull/137

git config gc.recentObjectsHook ./precious-objects
git prune --expire=now
git show -p ${pr137}

查看提交 4dc16e2提交 01e9ca4(2023年6月7日)由Taylor Blau (ttaylorr)
(由Junio C Hamano -- gitster --提交 58ecb2e中合并,2023年6月23日) gc:介绍gc.recentObjectsHook 帮助者:Jeff King 签署者:Taylor Blau
这个补丁引入了一个新的多值配置选项gc.recentObjectsHook,作为一种标记某些对象为最近的方式(因此不受垃圾回收的影响),而不考虑它们的年龄。
在对包含无法访问的对象的存储库执行垃圾回收操作时,Git根据对象的最近程度来决定如何处理这些对象。一般来说,无法访问但是最近的对象会保留在存储库中,而较旧的对象则会被丢弃。
然而,我们目前没有方便的方法来保留存储库中的某些宝贵的、无法访问的对象,即使它们已经过时并且将被修剪掉。我们目前的选择包括:
- 将指针引用指向您认为宝贵的任何对象的可达性提示,如果存在许多这样的对象,则可能不可取或不可行。 - 通过reflog跟踪它们,但这可能不可取,因为reflog的生命周期仅限于其跟踪的引用的生命周期(调用者可能希望将这些无法访问的对象保留更长时间)。 - 延长优雅期,这可能会保留调用者希望丢弃的其他对象。 - 手动修改要保留的对象的修改时间。如果这些对象已经是loose状态,这很容易做到(您只需枚举并对每个对象使用touch -m命令)。但如果它们是packed状态,您要么会修改该pack中所有对象的修改时间,要么被迫写出一个loose副本,这两种情况都可能不可取。更糟糕的是,如果它们在一个垃圾pack中,那就需要手动修改其*.mtimes文件,因为没有公开的接口可以进行此操作。 - 强制调用者自己构建他们想要保留的对象的pack,然后通过添加一个“.keep”文件来标记该pack为保留。这种方法有效,但对于调用者来说很麻烦,并且在滚动垃圾pack时拥有额外的pack会很尴尬。
这个补丁通过gc.recentObjectsHook配置引入了上述列表中的一个新选项,允许调用者指定一个程序(或一组程序),其输出被视为一组要作为最近对象处理的对象,而不考虑它们的真实年龄。
实现很简单。Git通过add_unseen_recent_objects_to_traversal()枚举最近的对象,该函数枚举loose和packed对象,并最终对满足want_recent_object()条件的任何对象调用add_recent_object()
这个补丁将最近性条件从简单的“此对象的修改时间是否比截止时间更近?”修改为“[...] 或者,此对象是否被至少一个gc.recentObjectsHook提及?”。
根据是否正在生成垃圾pack,这允许调用者执行以下两种操作之一:
- 如果正在生成垃圾pack,则调用者可以通过垃圾pack保留其他对象,即使由于它们的年龄而本应该被修剪掉。 - 如果不生成垃圾pack,则调用者同样可以将其他对象保留为loose状态。
一个潜在的替代方案是引入一种新模式来改

git config现在在其man page中包含以下内容:

gc.recentObjectsHook

在考虑是否删除对象(无论是在生成垃圾包还是存储不可达对象时)时,使用shell执行指定的命令。

将其输出解释为Git将视为“最近”的对象ID,而不考虑它们的年龄。通过将它们的修改时间视为“现在”,无论这些对象(及其后代)的真实年龄如何,都将保留在输出中提到的对象。

输出必须每行恰好包含一个十六进制对象ID,且不能包含其他内容。无法在存储库中找到的对象将被忽略。支持多个钩子,但所有钩子都必须成功退出,否则操作(无论是生成垃圾包还是解压不可达对象)将停止。



1

引用不一定是分支或标签,您可以保留对任何内容的本地引用。

这里是一个简单的“为拉取请求137创建另一个快照引用”的示例:

next=$((`git rev-list --no-walk --count --glob=refs/snap/pull/137/head-v*`+1))
git update-ref refs/snap/pull/137/head-v$next refs/pull/137/head

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