代码之家  ›  专栏  ›  技术社区  ›  Crazyjavahacking

git标签的存储效率低吗?

git
  •  6
  • Crazyjavahacking  · 技术社区  · 6 年前

    我想知道git标签的存储是否效率低下。

    我认为标记只是指向变更集的“指针”,就磁盘使用而言,变更集应该非常高效和小。

    但是,使用我的新git存储库:

    1. 推送所有分支(8)和所有变更集(20489)总共需要约110MB(如Gitlab所示)
    2. 推送不添加任何额外变更集(仍然是20489)的所有标记(1444)会突然占用~150MB

    这很奇怪。我没想到仅仅因为“指针”就有这么大的增长。

    有没有人有任何线索或可能的解释?

    谢谢

    2 回复  |  直到 6 年前
        1
  •  3
  •   Liam Joshua    6 年前

    您有只能由标记访问的提交。如果你用 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 
    
        2
  •  5
  •   torek    6 年前

    TL;博士

    标签通常是相当有效的。作为 ElpieKay concluded in a comment ,您必须有一些对象——可能是提交的,但任何对象都会做——可以从标记访问,但不能从分支访问。

    标签——无论是轻量级的还是带注释的;我们将很快区分它们——指向 任意Git对象 ,而不是变更集。当我们说 指向 这里,我们真正的意思是 包含哈希ID: 所有Git对象都有一个hash ID作为它们的“真名”,它充当 key-value store 所有Git对象中。

    在这个主要的Git数据库中有四种类型的对象。他们是 提交 , , 团块 带注释的标记对象 . 提交充当快照,但它们本身只包含少量元数据,包括提交者的名称和电子邮件地址以及时间戳;提交的哈希ID 起源 提交、日志消息和存储的 反对。树对象是最终通过子树和blob对象提供快照的对象。

    Git调用的名称 参考文献 参考文献 分为不同的名字空间。两个大的是 分支名称 喜欢 master ,实际上是在 refs/heads/* 名称空间( refs/heads/master ),和 标记名 喜欢 v1.2 ,实际上是在 refs/tags/* 名称空间( refs/tags/v1.2 ). 名称空间防止名称碰撞,即使它们拼写相同。每个名称都包含一个hash ID,hash ID键值存储的名称是组成Git存储库的另一个原则数据库。

    分支名称被约束为仅指向提交对象。标记名可以直接指向提交对象。这样的标记称为 轻量级标记 . 或者,标记名可能指向 带注释的标记对象 . 该对象本身指向另一个(任意的)对象,尽管标记名指向提交非常典型。指向带注释标记对象的标记名是 带注释的标记 .

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

    提交包含,即指向,其他提交哈希ID。一旦创建对象,就不能对其进行更改,并且不能预测任何对象的哈希ID。 所以一个新的承诺只能指向现有的提交。每个提交也被赋予一个唯一的散列ID(即,任何提交都不会发生多次)。这意味着提交图本身,通常一次只生成一个提交,从来没有任何周期:所有的提交箭头“向后指向”以前的提交。

    提交还包含树哈希ID,树包含更多的树哈希ID和blob哈希ID。尽管树哈希ID不必是唯一的(例如,两个不同的提交可以共享同一个快照),但它们也是定向的和非循环的。

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


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


    名称数据库充当DAG的入口点,DAG允许垃圾收集

    结果是,如果我们选择存储库中的任何对象,我们可以从该对象跟踪到所有 可达成的 其他对象并得到一个子图。如果我们使用名称数据库(分支和标记名称以及所有其他Git引用,有一些是特别隐蔽的,例如存储在索引中的blob散列ID)作为进入对象数据库的入口点,并将所有可访问对象暂时涂成绿色,那么我们就可以让Git遍历整个对象数据库,然后 丢弃 任何 可访问(然后移除着色,在Git中,着色实际上保存在内存中,而不是磁盘上)。

    这个 可达集 但是,对象的数量取决于我们使用的名称!如果我们忽略所有 标签 名称,我们可能有一些对象——通常是一些提交链——否则无法访问。

    仅获取和推送可访问对象的副本

    一般来说, git fetch git push 以及 git clone 只复制那些 可达成的 从正在使用的名称。传输中涉及的两个Git实例有一个初始对话,在对话中,每个Git在阅读了一组名称/ID对之后,告诉另一个Git它拥有和/或想要哪个hash ID。 发送和接收Git实例根据需要遍历对象DAG,以确定需要哪些对象来完成这些名称/ID对。然后发送者发送对象; 接收Git将这些对象添加到其对象数据库中,并完成传输。

    这对你来说意味着 某些对象只能从标记访问 这使得推动力明显增大。找到这些对象可能有点棘手——Git有用于此的低级工具( git rev-parse git branch --contains (例如),但没有干净地打包为面向用户的解决方案。


    新的wire协议(v2旧的协议是v0,它与v1相同)改变了名称/ID对的列出方式,因为在某些存储库中 名称 数据库已经发展到了这样一个地步:像v0一样,每次简单地列出所有内容都需要太长时间。

    发送Git通常使用它对 接收 Git的对象数据库,由接收方必须具有的哈希ID决定,以构建 薄包装 其中发送者的对象与接收者已有的对象进行增量压缩。请看下面的压缩。


    侧压

    这两个键值数据库都以多种不同的方式存储在Git中。对象数据库中的对象可以存储为 释放 ,它们是zlib放气但独立的,或者 拥挤的 ,它们在哪里 delta-compressed 对抗其他物体。Delta链的行为类似于变更集,但有一个关键的区别是 实施者; 用户根本不必在意!–这里:至少在理论上,任何对象都可以压缩为任何其他对象,甚至是不同类型的对象。(实际上Git只对同一类型的对象压缩对象。)即使对blob压缩blob,也不要求某些文件对 以前的 版本 相同的 文件:它可以是同一文件的未来版本的增量,也可以是不同文件的当前版本的增量,或者其他什么。

    包文件通常是自包含的:包文件中增量压缩的对象必须提供 下一个 在同一个包文件中的链中的对象,一直到 基础 对象本身不是增量压缩的。这个 薄包装 那个 git获取 推箱子 构建故意违反了这个假设;瘦包的接收者有义务“修复”它( git index-pack --fix-thin )或以其他方式纠正问题。但所有这些也是内部唯一的细节。