git 存储元素存储在哪里?数据结构真的是栈吗?

Where are the git stash elements stored? Is the data structure really a stack?

为了更好地理解 git stash,我想知道: git stash 的元素存储在哪里以及存储在什么数据结构中?在堆栈中?在有序集合中?


详情:

所有文档、文章和书籍(Git Internals 除外)都说 git 存储是一个 堆栈

最近,我发现您可以从存储中以任意顺序检索和删除元素——多么有用的功能。由于此功能和 A Hacker's Guide to Git,在我看来,藏匿处似乎是由某处 按时间顺序排列的引用集 组成的。但是,在 .git/refs/stash 中仅以合并提交的形式存储最新的存储元素(其中还包含创建存储元素的日期)。

是否有另一个 (top-secret pre-index stash-cache;) 数据结构保存所有存储元素?还是 git stash (list|pop|apply) 从其常规对象存储中检索元素?如何?

那么stash元素形成什么样的数据结构呢?合并提交的日期是否隐含地给出了元素的时间顺序?如果元素实际上存储在堆栈中,git 如何以任意顺序检索和删除元素?

, all but the current stash are stored in the reflog for the reference refs/stash. See the definitions of "ref" and "reflog" in the gitglossary。请注意,masterdevelop 等分支名称是一种引用,分别是全名 refs/heads/masterrefs/heads/develop 的缩写。标签名称是另一种参考;标签 v2.2 实际上是引用 refs/tags/v2.2.

大多数参考文献都以 refs/ 为前缀。事实上,各种 HEAD——HEAD 本身,以及 MERGE_HEADCHERRY_PICK_HEADORIG_HEAD 等等——是唯一的例外,大多数 那些 没有 重新记录。 HEAD 是唯一一个这样做的。

通常,reflog 条目只是线性编号:HEAD@{1}master@{1} 是 "the commit to which HEAD or master pointed before its most recent update",master@{2} 是两步前的提交,依此类推。 gitrevisions 中对此进行了描述。为方便起见,current 值可以用 @{0} 引用:master@{0}master 总是解析为相同的哈希 ID。如果 refs/stash 引用以与其他引用相同的方式使用,它将作为队列而不是堆栈工作——但它不是 used 那样。相反,git stash 代码明确地 删除了 个早期条目。

由于编号始终是连续的,删除一个条目会导致所有 更高 的数字下降一个。例如,如果您手动删除 master@{5},那么以前的 master@{6} 现在是 master@{5},以前的 master@{7} 现在是 master@{6},等等上。

当然,添加一个新条目会将所有内容推高一个。因此,当您创建一个新的 stash 时,以前 stash 又名 stash@{0} 的那个现在是 stash@{1}。原来是stash@{1}的现在是stash@{2},以此类推。对于其他 reflogs,比如 master,没有人会调用这个 "pushing",它只是一个普通的队列在起作用。

一旦您 删除 stash@{0} 又名 stash,但是,所有较高的条目——stash@{1}stash@{2} 和以此类推——下降一个,所以现在 stash 已经是 "popped" 而之前的 stash@{1} 只是 stash。当然,您也可以 git stash drop stash@{4} 删除该特定条目,保留 0 到 3 并重新编号 5 及以上。请注意,任何特定存储的 git stash pop 仅表示 "apply and, if that seems to succeed, drop".

请注意,并非完全偶然,每个 reflog 条目 附有时间戳。您可以写 master@{yesterday}master@{3.hours.ago} 并且 Git 将根据时间戳找到适当的 reflog 条目的哈希 ID。1 因为 stash标识符只是 reflog 条目,同样的语法在那里工作。 (我从来没有真正发现这在任何地方都有用,也许是因为我在工作时没有时间感,不记得现在是星期几现在,更不用说我工作的时候了早些时候做了一些事情。:-) ) 为了使用这些时间戳,most reflogs expire:默认情况下,旧的 reflog 条目将消失90 天后,或者仅 30 天,如果它命名的对象从同一引用的当前值不是 reachable2 但是,[=默认情况下,10=] 本身免于此过期。所有这些都是可配置的:请参阅 the git config documentation.

中的所有 gc.reflogExpire 设置

1如果您一天更新引用数次但每小时不超过一次,则 @{yesterday} 表示 @{24.hours.ago}。如果每小时更新不止一次,再乘以 60:@{1440.minutes.ago}。如果您每分钟更新多次,请再次乘以 60:@{86400.seconds.ago}。分辨率没有比这更好的了。

2这就是 Git 保留 30 天但最终清除的方式,例如,被 git rebase 放弃的旧提交。 可达性 是由存储库中的标记、提交和树对象形成的有向无环图或 DAG 提供的一个关键概念。 (Blob 是 DAG 中,但不参与扩展它,因为它们始终是叶节点。因此,blob 本身可能可达或不可达,但它永远不会影响任何 其他个对象。)