Git标签的存储是否低效?

7
我在想git标签的存储是否效率低下。
我认为标签只是指向变更集的“指针”,在磁盘使用方面应该非常高效且占用空间较小。
但是,对于我的新git仓库:
1. 推送所有分支(8)和所有变更集(20489),共计大约110MB (在Gitlab中显示) 2. 推送所有标签(1444),不添加任何其他变更集(还是20489),突然共计大约150MB
这很奇怪。 我没有预料到仅仅因为“指针”就会出现如此巨大的增加。
有人有任何线索或可能的解释吗?
谢谢。

5
平均约为40 * 1024 / 1444 = 28KB的标签过大,无论是轻量级标签还是带注释的标签都是如此。您的标签可能覆盖的提交比您的分支多。git rev-list --tags --count返回的数字是多少? - ElpieKay
27569,所以肯定出了什么问题。 - Crazyjavahacking
5
那就说得通了。一些提交可以从标签中访问,但不能从分支中访问。 - ElpieKay
2个回答

5

简述

标签通常是相当高效的。正如ElpieKay在评论中总结的那样,您必须有一些对象(可能是提交,但任何对象都可以),这些对象可从标签中访问,但不能从分支中访问。

详细

标签-无论是轻量级标签还是带注释的标签-指向任意Git对象,而不是变更集。当我们在这里说指向时,我们真正意思是包含哈希ID:所有Git对象都有一个哈希ID作为其“真实名称”,该哈希ID用作所有Git对象的key-value store中的键。

这个Git主数据库中有四种对象类型。它们是提交(commit)树(tree)blob带注释的标签对象(annotated tag objects)。提交(commit)充当快照,但它们本身只包含少量元数据,包括提交者的姓名和电子邮件地址以及时间戳;提交的父(parent)提交的哈希ID(s),日志消息和存储的树(tree)对象的哈希ID。树对象最终通过子树和blob对象提供快照。

Git所称的名称,即引用refs,分为各种名称空间。 其中最大的两个是分支名称,例如master,实际上位于refs/heads/*名称空间(refs/heads/master),以及标签名称,例如v1.2,实际上位于refs/tags/*名称空间(refs/tags/v1.2)。 名称空间使得即使它们拼写相同,名称也不会发生冲突。 每个名称包含一个哈希ID,名称到哈希ID键值存储是组成Git存储库的另一个主要数据库。

分支名称仅限于指向提交对象。 标签名称可以直接指向提交对象。 这样的标签称为轻量级标签。 或者,标签名称可能指向一个注释标签对象。 该对象本身指向其他(任意)对象,尽管标签名称通常指向提交。 指向注释标签对象的标签名称是注释标签

对象数据库形成一个有向无环图

提交包含指向其他提交哈希 ID 的指针。一旦创建了对象,就不能更改它,也无法预测任何对象的哈希 ID。1因此,新提交只会指向现有提交。每个提交还被赋予唯一的哈希 ID(即没有重复的提交)。这意味着提交图本身通常是一次一个提交增长,永远不会有任何循环:所有提交箭头都“向后指”到先前的提交。

提交还包含树哈希 ID,而树包含进一步的树哈希 ID 和 blob 哈希 ID。这些也是有向无环的,尽管树哈希 ID 不需要是唯一的(例如,两个不同的提交可以共享相同的快照)。

注释标签对象可以包含任何其他对象的 ID,但与提交一样,注释标签对象具有唯一的哈希 ID,并且只允许指向现有对象。因此,这些也不会向图中添加循环。


1哈希 ID 是对象内容的加密校验和,包括对象的类型。从技术上讲,如果你在这个问题上花足够的计算能力,它是可以被预测或故意产生哈希冲突的。然而,Git 也以其他方式禁止循环。


名称数据库作为进入DAG的入口,允许进行垃圾回收

结果是,如果我们选择存储库中的任何对象,我们可以从该对象跟踪到所有可达的其他对象并获得一个子图。如果我们将名称数据库(分支和标记名称以及所有其他Git引用-其中一些特别狡猾,例如存储在索引中的blob哈希ID)用作进入对象数据库的入口点,并临时将所有可达对象涂成绿色,然后我们可以让Git遍历整个对象数据库并且“丢弃”任何不可达的对象(然后删除着色,这在Git中实际上保存在内存中,而不是在磁盘上)。

然而,对象的“可达集合”取决于我们使用的名称!如果我们省略所有“标签”名称,则可能有一些对象-通常是一些提交链-否则无法访问。

获取和推送副本仅限于可达对象

作为一般规则,git fetchgit push以及由git clone运行的初始fetch仅复制那些从正在使用的名称中可达的对象。涉及传输的两个Git实例在初始对话中相互告知它们具有和/或想要的哈希ID,经过查看某些名称/ID对后,发送方和接收方Git实例根据需要遍历对象DAG,以确定哪些对象是必需的,以使这些名称/ID对完整。然后,发送方发送对象;接收方Git将这些对象添加到其对象数据库中,传输完成。
在您的情况下,这意味着一些对象仅从标签中可访问,这使得推送变得更大。找到这些对象可能有点棘手--Git具有用于此的低级工具(例如git rev-parsegit branch --contains),但没有清晰打包为面向用户的解决方案。
The new wire protocol (v2—the old one is v0 which is the same as v1)—alters the way the name/ID pairs are listed, as it turns out that in some repositories, the name database has grown to the point where simply listing everything every time, as v0 does, takes too long.
The sending Git typically uses its knowledge of what's in the receiving Git's object database, as determined by the hash IDs the receiver must have, to build a thin pack in which the sender's objects are delta-compressed against the objects the receiver already has. See the compression aside below.

关于压缩的说明

这两个键值数据库都以多种不同方式存储在Git中。对象数据库中的对象可以存储为loose(松散),其中它们被zlib压缩但是独立存在,或者存储为packed(紧凑),其中它们delta-compressed相对于其他对象。Delta链类似于变更集,但有一个关键的区别——至少对于实现者来说是关键的;用户根本不需要关心!—这里:任何对象理论上都可以与任何其他类型的对象压缩,即使是不同类型的对象。(实际上,Git只会将对象与同一类型的对象进行压缩。)即使是针对blob的压缩也没有要求某个文件必须是前一个版本的同一文件的增量:它可以是来自同一文件的未来版本的增量,或者是当前版本的不同文件的增量,或者其他任何东西。

Pack文件通常是自包含的:在一个pack文件中进行增量压缩的对象必须在同一个pack文件中提供链中的下一个对象,一直到不再进行增量压缩的基础对象。 thin packsgit fetchgit push中构建时故意违反了这个假设; thin pack的接收者有义务“修复”它(git index-pack --fix-thin)或以其他方式纠正该问题。但所有这些也都是仅限于内部的细节。


我相信这很全面,但是需要一个tl;dr(太长不看),因为它太长了,我读不下去。 - Liam
1
@Liam: 我确实加粗了一个关键短语:“某些对象仅可从标记访问”。那是回答 OP 的问题,但为了 理解 答案,你可能需要阅读其余内容 :-) - torek
如果是我,我会把加粗的短语放在顶部(也许重复一遍?)。阅读时我完全错过了它,到那个点之前我已经有点放弃了(但我很懒)。我可以理解其中的意思,所以我猜其他人也可以。 - Liam
@Liam:好的,我会添加 TL;DR 部分! - torek

3
您有一些仅通过标签可到达的提交。如果使用git log --decorate --oneline --graph --all命令查看历史记录,您可以看到它们。
请寻找“以标签结尾”的历史记录行。
* 4d60a50b0 (HEAD -> master, origin/master, origin/HEAD) Latest commit
* 123d19df2 More Stuff
* 158f2091b Removed bogus quote.
| * 413d140f4 (tag: 6.4.1_76119) line endings
| * c3fa7ee03 getting the branch to autobuild and make installer
| |  * bda836a25 (tag: 7.0.0) more credits changes 
| |  * 3cab6e792 for autobuilds, launch seed7.0.0 so it gets the branch
| |_/  
|/|   
* | 11b2165f5 formatting
* | 4af66cc59 changed version numbers to 7.0.0 

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