git分支和标签如何存储在磁盘中?

 lovely叫我龙哥 发布于 2023-02-09 13:23

我最近在工作中检查了我的一个git存储库,它有超过10,000个分支和超过30000个标记.新鲜克隆后,回收的总大小为12Gigs.我相信没有理由拥有10000个分支机构.所以我相信它们会在磁盘中占用相当大的空间.所以,我的问题如下

    分支和标签如何存储在磁盘中,例如使用什么数据结构,为每个分支存储什么信息?

    如何获取有关分支的元数据?就像创建分支时一样,分支的大小是多少.

poke.. 17

所以,我将稍微扩展一下这个主题并解释Git 如何存储什么.这样做将解释存储哪些信息,以及存储库大小的确切重要性.作为一个公平的警告:这个答案相当长:)

Git对象

Git本质上是一个对象数据库.这些对象有四种不同的类型,并且都由其内容的SHA1哈希标识.这四种类型是blob,,提交标签.

斑点

斑点是对象的最简单的类型.它存储文件的内容.因此,对于存储在Git存储库中的每个文件内容,对象数据库中都存在一个blob对象.由于它仅存储文件内容,而不存储文件名等元数据,因此这也是防止具有相同内容的文件被多次存储的机制.

向上一级,是将blob放入目录结构的对象.单个树对应于单个目录.它本质上是一个文件和子目录列表,每个条目包含一个文件模式,一个文件或目录名,以及对属于该条目的Git对象的引用.对于子目录,此引用指向描述子目录的树对象; 对于文件,此引用指向存储文件内容的blob对象.

承诺

Blob和树已足以代表完整的文件系统.要在其上添加版本控制,我们有提交对象.无论何时在Git中提交内容,都会创建提交对象.每次提交都代表修订历史记录中的快照.

它包含对描述存储库根目录的树对象的引用.这也意味着每次实际引入一些更改的提交至少需要一个新的树对象(可能更多).

提交还包含对其父提交的引用.虽然通常只有一个父级(对于线性历史记录),但提交可以包含任意数量的父级,在这种情况下,它通常称为合并提交.大多数工作流程只会让你与两个父母合并,但你也可以拥有任何其他数字.

最后,提交还包含您希望提交的元数据:作者和提交者(名称和时间),当然还有提交消息.

这就是拥有完整版本控制系统所需的一切; 但当然还有一种对象类型:

标签

标记对象是存储标记的一种方法.确切地说,标记对象存储带注释的标记,这些标记具有类似于提交的标记 - 一些元信息.它们由git tag -a(或创建签名标记时)创建并需要标记消息.它们还包含对它们指向的提交对象的引用,以及标记器(名称和时间).

参考

到目前为止,我们有一个完整的版本控制系统,带有注释标签,但我们所有的对象都由它们的SHA1哈希标识.这当然有点烦人,所以我们还有一些其他的东西可以让它变得更容易:引用.

引用有不同的风格,但最重要的是它们:它们是包含40个字符的简单文本文件 - 它们指向的对象的SHA1哈希.因为它们很简单,所以非常便宜,所以使用很多参考文件都没有问题.它不会产生任何开销,也没有理由不使用它们.

通常有三种"类型"的引用:分支,标签和远程分支.他们真的工作相同,都指向提交对象; 除了指向标记对象的带注释标记之外(普通标记也只是提交引用).它们之间的区别在于您如何创建它们以及/refs/它们存储在哪个子路径中.我现在不会介绍这个,因为几乎每个Git教程都会对此进行解释; 请记住:引用,即分支,非常便宜,所以不要犹豫为几乎所有东西创建它们.

压缩

现在因为torek在他的回答中提到了关于Git压缩的一些内容,我想稍微澄清一下.不幸的是,他混合了一些东西.

因此,通常对于新的存储库,所有Git对象都存储在.git/objects由SHA1哈希标识的文件中.前两个字符从文件名中删除,用于将文件分区为多个文件夹,这样可以更容易导航.

在某些时候,当历史变得更大或由其他东西触发时,Git将开始压缩对象.它通过将多个对象打包到单个包文件中来实现.这究竟如何起作用并不是那么重要; 它将减少单个Git对象的数量并有效地将它们存储在单个索引存档中(此时,Git将使用delta压缩btw.).然后将包文件存储在其中,.git/objects/pack并且可以轻松地获得几百MiB的大小.

作为参考,情况有点类似,虽然简单得多.所有当前引用都存储在.git/refs,例如分支.git/refs/heads,标签.git/refs/tags和远程分支中.git/refs/remotes/.如上所述,它们是简单的文本文件,仅包含它们所指向的对象的40个字符标识符.

在某些时候,Git会将任何类型的旧引用移动到单个查找文件中:.git/packed-refs.该文件只是一个很长的哈希和引用名称列表,每行一个条目.保留在那里的引用将从refs目录中删除.

Reflogs

Torek也提到了这些,reflogs本质上只是引用的日志.他们跟踪引用的内容.如果您执行任何影响引用(提交,检出,重置等)的操作,则会添加一个新的日志条目,以便记录发生的事情.它还提供了一种在出错之后返回的方法.例如,一个常见的用例是在意外地将分支重置到不应该去的地方之后访问reflog.然后,您可以使用git reflog查看日志并查看引用之前指向的位置.由于松散的Git对象不会立即删除(永远不会删除属于历史记录的对象),因此通常可以轻松恢复以前的情况.

然而,Reflog是本地的:它们只跟踪本地存储库发生的情况.它们不与遥控器共享,也不会被转移.新克隆的存储库将具有带有单个条目的reflog,它是克隆操作.它们也被限制在一定的长度之后,旧的动作被修剪,因此它们不会成为存储问题.

最后的一些话

所以,回到你的实际问题.克隆存储库时,Git通常已经以压缩格式接收存储库.这已经完成以节省传输时间.引用非常便宜,因此它们永远不是大型存储库的原因.但是,由于Git的本质,单个当前提交对象中有一个完整的非循环图,最终将到达第一个提交,第一个树和第一个blob.因此,存储库将始终包含所有修订的所有信息.这就是使历史悠久的存储库变大的原因.不幸的是,你无法做到这一点.好吧,你可以在某些部分切断旧的历史记录,但这会让你有一个破损的存储库(你通过克隆--depth参数来做到这一点).

至于你的第二个问题,正如我在上面解释的那样,分支只是对提交的引用,而引用只是指向Git对象的指针.所以不,没有关于可以从中获取分支的任何元数据.唯一可以给你一个想法的是你在历史中分支时所做的第一次提交.但是拥有分支并不会自动意味着历史中确实存在一个分支(快速合并和重新定位对其起作用),并且仅仅因为历史中存在一些分支并不意味着分支(引用,指针)仍然存在.

2 个回答
  • 所有git引用(分支,标签,注释,存储等)都使用相同的系统.这些是:

    引用本身,和

    "reflogs"

    Reflogs .git/logs/refs/基于引用名称存储,但有一个例外:reflogs for HEAD存储在.git/logs/HEAD而不是.git/logs/refs/HEAD.

    引用来自"松散"或"打包".打包引用.git/packed-refs,这是简单引用的(SHA-1,refname)对的平面文件,以及带注释标记的额外信息."宽松"的裁判在.这些文件包含原始SHA-1(可能是最常见的),或文字字符串后跟符号引用的另一个引用的名称(通常仅用于但您可以创建其他引用).符号引用不包装(或者至少,我似乎无法实现这一点:-))..git/refs/nameref:HEAD

    包装标签和"空闲"分支头(没有主动更新的那些)节省了空间和时间.你可以git pack-refs用来做这件事.但是,请为您git gc调用git pack-refs,因此通常您不需要自己执行此操作.

    2023-02-09 13:24 回答
  • 所以,我将稍微扩展一下这个主题并解释Git 如何存储什么.这样做将解释存储哪些信息,以及存储库大小的确切重要性.作为一个公平的警告:这个答案相当长:)

    Git对象

    Git本质上是一个对象数据库.这些对象有四种不同的类型,并且都由其内容的SHA1哈希标识.这四种类型是blob,,提交标签.

    斑点

    斑点是对象的最简单的类型.它存储文件的内容.因此,对于存储在Git存储库中的每个文件内容,对象数据库中都存在一个blob对象.由于它仅存储文件内容,而不存储文件名等元数据,因此这也是防止具有相同内容的文件被多次存储的机制.

    向上一级,是将blob放入目录结构的对象.单个树对应于单个目录.它本质上是一个文件和子目录列表,每个条目包含一个文件模式,一个文件或目录名,以及对属于该条目的Git对象的引用.对于子目录,此引用指向描述子目录的树对象; 对于文件,此引用指向存储文件内容的blob对象.

    承诺

    Blob和树已足以代表完整的文件系统.要在其上添加版本控制,我们有提交对象.无论何时在Git中提交内容,都会创建提交对象.每次提交都代表修订历史记录中的快照.

    它包含对描述存储库根目录的树对象的引用.这也意味着每次实际引入一些更改的提交至少需要一个新的树对象(可能更多).

    提交还包含对其父提交的引用.虽然通常只有一个父级(对于线性历史记录),但提交可以包含任意数量的父级,在这种情况下,它通常称为合并提交.大多数工作流程只会让你与两个父母合并,但你也可以拥有任何其他数字.

    最后,提交还包含您希望提交的元数据:作者和提交者(名称和时间),当然还有提交消息.

    这就是拥有完整版本控制系统所需的一切; 但当然还有一种对象类型:

    标签

    标记对象是存储标记的一种方法.确切地说,标记对象存储带注释的标记,这些标记具有类似于提交的标记 - 一些元信息.它们由git tag -a(或创建签名标记时)创建并需要标记消息.它们还包含对它们指向的提交对象的引用,以及标记器(名称和时间).

    参考

    到目前为止,我们有一个完整的版本控制系统,带有注释标签,但我们所有的对象都由它们的SHA1哈希标识.这当然有点烦人,所以我们还有一些其他的东西可以让它变得更容易:引用.

    引用有不同的风格,但最重要的是它们:它们是包含40个字符的简单文本文件 - 它们指向的对象的SHA1哈希.因为它们很简单,所以非常便宜,所以使用很多参考文件都没有问题.它不会产生任何开销,也没有理由不使用它们.

    通常有三种"类型"的引用:分支,标签和远程分支.他们真的工作相同,都指向提交对象; 除了指向标记对象的带注释标记之外(普通标记也只是提交引用).它们之间的区别在于您如何创建它们以及/refs/它们存储在哪个子路径中.我现在不会介绍这个,因为几乎每个Git教程都会对此进行解释; 请记住:引用,即分支,非常便宜,所以不要犹豫为几乎所有东西创建它们.

    压缩

    现在因为torek在他的回答中提到了关于Git压缩的一些内容,我想稍微澄清一下.不幸的是,他混合了一些东西.

    因此,通常对于新的存储库,所有Git对象都存储在.git/objects由SHA1哈希标识的文件中.前两个字符从文件名中删除,用于将文件分区为多个文件夹,这样可以更容易导航.

    在某些时候,当历史变得更大或由其他东西触发时,Git将开始压缩对象.它通过将多个对象打包到单个包文件中来实现.这究竟如何起作用并不是那么重要; 它将减少单个Git对象的数量并有效地将它们存储在单个索引存档中(此时,Git将使用delta压缩btw.).然后将包文件存储在其中,.git/objects/pack并且可以轻松地获得几百MiB的大小.

    作为参考,情况有点类似,虽然简单得多.所有当前引用都存储在.git/refs,例如分支.git/refs/heads,标签.git/refs/tags和远程分支中.git/refs/remotes/<remote>.如上所述,它们是简单的文本文件,仅包含它们所指向的对象的40个字符标识符.

    在某些时候,Git会将任何类型的旧引用移动到单个查找文件中:.git/packed-refs.该文件只是一个很长的哈希和引用名称列表,每行一个条目.保留在那里的引用将从refs目录中删除.

    Reflogs

    Torek也提到了这些,reflogs本质上只是引用的日志.他们跟踪引用的内容.如果您执行任何影响引用(提交,检出,重置等)的操作,则会添加一个新的日志条目,以便记录发生的事情.它还提供了一种在出错之后返回的方法.例如,一个常见的用例是在意外地将分支重置到不应该去的地方之后访问reflog.然后,您可以使用git reflog查看日志并查看引用之前指向的位置.由于松散的Git对象不会立即删除(永远不会删除属于历史记录的对象),因此通常可以轻松恢复以前的情况.

    然而,Reflog是本地的:它们只跟踪本地存储库发生的情况.它们不与遥控器共享,也不会被转移.新克隆的存储库将具有带有单个条目的reflog,它是克隆操作.它们也被限制在一定的长度之后,旧的动作被修剪,因此它们不会成为存储问题.

    最后的一些话

    所以,回到你的实际问题.克隆存储库时,Git通常已经以压缩格式接收存储库.这已经完成以节省传输时间.引用非常便宜,因此它们永远不是大型存储库的原因.但是,由于Git的本质,单个当前提交对象中有一个完整的非循环图,最终将到达第一个提交,第一个树和第一个blob.因此,存储库将始终包含所有修订的所有信息.这就是使历史悠久的存储库变大的原因.不幸的是,你无法做到这一点.好吧,你可以在某些部分切断旧的历史记录,但这会让你有一个破损的存储库(你通过克隆--depth参数来做到这一点).

    至于你的第二个问题,正如我在上面解释的那样,分支只是对提交的引用,而引用只是指向Git对象的指针.所以不,没有关于可以从中获取分支的任何元数据.唯一可以给你一个想法的是你在历史中分支时所做的第一次提交.但是拥有分支并不会自动意味着历史中确实存在一个分支(快速合并和重新定位对其起作用),并且仅仅因为历史中存在一些分支并不意味着分支(引用,指针)仍然存在.

    2023-02-09 13:29 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有