我认为标签只是指向变更集的“指针”,在磁盘使用方面应该非常高效且占用空间较小。
但是,对于我的新git仓库:
1. 推送所有分支(8)和所有变更集(20489),共计大约110MB (在Gitlab中显示) 2. 推送所有标签(1444),不添加任何其他变更集(还是20489),突然共计大约150MB
这很奇怪。 我没有预料到仅仅因为“指针”就会出现如此巨大的增加。
有人有任何线索或可能的解释吗?
谢谢。
标签通常是相当高效的。正如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 也以其他方式禁止循环。
结果是,如果我们选择存储库中的任何对象,我们可以从该对象跟踪到所有可达的其他对象并获得一个子图。如果我们将名称数据库(分支和标记名称以及所有其他Git引用-其中一些特别狡猾,例如存储在索引中的blob哈希ID)用作进入对象数据库的入口点,并临时将所有可达对象涂成绿色,然后我们可以让Git遍历整个对象数据库并且“丢弃”任何不可达的对象(然后删除着色,这在Git中实际上保存在内存中,而不是在磁盘上)。
然而,对象的“可达集合”取决于我们使用的名称!如果我们省略所有“标签”名称,则可能有一些对象-通常是一些提交链-否则无法访问。
git fetch
和git push
以及由git clone
运行的初始fetch仅复制那些从正在使用的名称中可达的对象。涉及传输的两个Git实例在初始对话中相互告知它们具有和/或想要的哈希ID,经过查看某些名称/ID对后,发送方和接收方Git实例根据需要遍历对象DAG,以确定哪些对象是必需的,以使这些名称/ID对完整。然后,发送方发送对象;接收方Git将这些对象添加到其对象数据库中,传输完成。git rev-parse
和git branch --contains
),但没有清晰打包为面向用户的解决方案。
这两个键值数据库都以多种不同方式存储在Git中。对象数据库中的对象可以存储为loose(松散),其中它们被zlib压缩但是独立存在,或者存储为packed(紧凑),其中它们delta-compressed相对于其他对象。Delta链类似于变更集,但有一个关键的区别——至少对于实现者来说是关键的;用户根本不需要关心!—这里:任何对象理论上都可以与任何其他类型的对象压缩,即使是不同类型的对象。(实际上,Git只会将对象与同一类型的对象进行压缩。)即使是针对blob的压缩也没有要求某个文件必须是前一个版本的同一文件的增量:它可以是来自同一文件的未来版本的增量,或者是当前版本的不同文件的增量,或者其他任何东西。
Pack文件通常是自包含的:在一个pack文件中进行增量压缩的对象必须在同一个pack文件中提供链中的下一个对象,一直到不再进行增量压缩的基础对象。 thin packs 在git fetch
和git push
中构建时故意违反了这个假设; thin pack的接收者有义务“修复”它(git index-pack --fix-thin
)或以其他方式纠正该问题。但所有这些也都是仅限于内部的细节。
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
git rev-list --tags --count
返回的数字是多少? - ElpieKay