git notes
在提交 73f77b9, Git v1.6.6-rc0中引入。扇出
的概念是在 2010 年 2 月与 Git v1.7.1-rc0 一起引入的,提交 73f77b9以确定注释树的这一部分的最佳磁盘扇出,在..git/refs/notes/commits
' fanout
' 变量的值:
0
:无扇出(所有笔记都直接存储在根笔记树中)
1
:2/38
扇出
2
:2/2/36
扇出
3
:2/2/2/34
扇出
在 Git 2.25.2(2020 年 3 月)中,自动缩小注释树中扇出的代码有一个错误,该错误已被杀死。
请参阅Johan Herland ( )的commit dbc2747和commit e1c5253 (03 Feb 2020 ) 。(由Junio C Hamano 合并 -- --在提交 8833260中,2020 年 2 月 14 日)jherland
gitster
t3305
:检查笔记扇出更仔细和健壮
总之,在这个补丁之前,这个测试脚本:
- 创建许多笔记
- 验证笔记树中的所有笔记的扇出为 1
- 删除大部分笔记
- 验证笔记树中的笔记现在的扇出为 0
扇出验证只发生了两次:在创建所有笔记之后,以及在删除大部分笔记之后。
此补丁通过在 _ 添加/删除注释后检查扇出来加强测试_each
:我们断言从扇出 0 -> 1 的切换在添加注释时仅发生一次(并且该切换遍及整个注释树)。同样,我们断言从扇出 1 -> 0 的切换在删除音符时只发生一次。
此外,我们将移除后留下的音符数量从 50 个减少到 15 个,以确保无论外部因素如何,扇出 1 -> 0 的转换都会持续发生 (1)。
(1):目前(使用 SHA1 哈希函数和测试环境的确定性对象 id)notes 代码中的扇出启发式恰好在 109 个 notes 从 0 -> 1 切换,在 59 个 notes 从 1 -> 0 切换。
但是,更改哈希函数或其他外部因素会改变这些数字,后者理论上可能会低至 15。
有关更多详细信息,请参阅讨论。
和:
如上一次提交中所述,注释代码中扇出启发式的性质导致我们增加或减少注释扇出的确切点随被注释的对象而变化。
由于测试环境生成的对象 id 是确定性的(通过设计),t3305 生成和测试的注释总是相同的,因此我们碰巧看到从一次运行到下一次运行的扇出行为相同。
巧合的是,如果我们稍微改变测试环境(比如在我们开始 t3305 测试之前在一个不相关的分支上进行测试提交),我们不仅会看到扇出切换发生在不同的点,我们还设法触发了一个错误在笔记代码中,扇出1 -> 0
开关没有在笔记树上统一应用,而是产生一个像这样的笔记树:
...
bdeafb301e44b0e4db0f738a2d2a7beefdb70b70
bff2d39b4f7122bd4c5caee3de353a774d1e632a
d3/8ec8f851adf470131178085bfbaab4b12ad2a7
e0b173960431a3e692ae929736df3c9b73a11d5b
eb3c3aede523d729990ac25c62a93eb47c21e2e3
...
当我们写出一个带有新减少的扇出的笔记树时,就会出现这个错误,并且笔记树包含未扩展的子树,由于扇出的减少,这些子树应该合并到父树中):
在内部音符 16 树结构中恰好位于偶数级别的子树(换句话说:d3
上例中路径 - " " 的子树 - 在第一个半字节中是唯一的 - 即没有其他音符路径以" d
")不会作为树写出的一部分进行解包。
这个错误会在后续的笔记树中重复出现,直到子树被强制解包。在 t3305 中,这只发生在d38ec8f8
音符本身从树中移除时。
错误并不严重(没有信息丢失,注释代码能够读取/解码此树并正确操作它),但这仍然是当前实现中的一个错误,应该修复。
也就是说,修复一个错误并非没有复杂性:
我们必须考虑到load_subtree()
调用 from for_each_note_helper()
(现在在我们写出注释树时正确解包子树)可能最终将解包的非注释插入到non_note
struct 保存的条目的链接列表中notes_tree
。
由于我们正在写出笔记树,所以这个链表目前正在被 遍历write_each_non_note_until()
。
解压后的非注释必须插入到我们写出的最后一个非注释和下一个要写入的非注释之间。
因此,我们不能简单地持有next_non_note
to write in struct write_each_note_data
(因为我们会默默地跳过这些新插入的注释),而是必须始终遵循->next
我们写的最后一个非注释的指针。(这部分被 t3304 中的现有测试捕获。)