作为ElpieKay wrote in a comment,除了当前 stash 之外的所有存储都存储在reference refs/stash 的reflog 中。请参阅the gitglossary 中“ref”和“reflog”的定义。请注意,像 master 和 develop 这样的分支名称是一种引用,分别是全名 refs/heads/master 和 refs/heads/develop 的缩写。标签名称是另一种参考;标签v2.2 确实是引用refs/tags/v2.2。
大多数引用都以refs/ 为前缀。事实上,各种 HEAD——HEAD 本身,以及 MERGE_HEAD、CHERRY_PICK_HEAD、ORIG_HEAD 等等——是唯一的例外,而大多数 那些 并不 有 reflogs。 HEAD 是唯一一个这样做的。
通常,reflog 条目只是线性编号:HEAD@{1} 或 master@{1} 是“HEAD 或 master 在其最近更新之前指向的提交”,master@{2} 是两步前的提交, 等等。这在gitrevisions 中有描述。为方便起见,current 值可以用@{0} 引用:master@{0} 和master 始终解析为相同的哈希 ID。如果refs/stash 引用的使用方式与其他引用相同,那么它将作为队列而不是堆栈工作——但它不是使用那样的。相反,git stash 代码明确删除早期条目。
由于编号始终是连续的,删除一个条目会导致所有更高的数字下降一个。例如,如果您手动删除master@{5},那么以前的master@{6} 现在是master@{5},以前的master@{7} 现在是master@{6},以此类推。
当然,添加一个新条目会将所有内容都提升一个。因此,当您创建一个新的存储时,以前是 stash 又名 stash@{0} 现在是 stash@{1}。以前是stash@{1},现在是stash@{2},以此类推。对于其他 reflog,例如 master,没有人称其为“推送”,它只是普通的队列在行动。
一旦你删除 stash@{0} aka stash,所有更高的条目——stash@{1}、stash@{2} 等等——都会下降一个,所以现在stash已被“弹出”,之前的 stash@{1} 只是 stash。当然,您也可以git stash drop stash@{4} 删除该特定条目,保留 0 到 3 并重新编号 5 及以上。请注意,任何特定存储的git stash pop 仅表示“应用,如果似乎成功,则放弃”。
请注意,并非完全是偶然的,每个 reflog 条目也附有一个时间戳。您可以编写 master@{yesterday} 或 master@{3.hours.ago},Git 将根据时间戳找到相应 reflog 条目的哈希 ID。1 因为存储标识符只是 reflog 条目,所以相同的语法在那里有效. (我从来没有真正发现这一切在任何地方都有用,也许是因为我在工作时没有时间感,不记得现在是一周中的哪一天现在,更不用说我之前做某事的时候了. :-) ) 使用这些时间戳,大多数 reflogs expire:默认情况下,旧的reflog 条目将在 90 天后消失,或者只有 30 天,如果它命名的对象从同一引用的当前值中可访问。2 但是,默认情况下,refs/stash 本身不受此到期限制。所有这些都是可配置的:查看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 本身可能是可访问的或不可访问的,但它不会影响任何 其他 对象。)