来自Git 2.33的亮点

泰勒·布劳的形象

开源的Git项目Git 2.33发布来自超过74个贡献者的功能和错误修复,其中19个新的。我们上次赶上了你最新的git2.31何时发布.以下是从那时起的一些最有趣的功能和更改。

几何重新包装

以前的博客帖子,我们讨论了GitHub如何使用新的模式git重新包装实现我们的存储库维护工作。在Git 2.32中,许多这些补丁在开源Git项目中释放。因此,如果您尚未阅读我们的早期博客文章,或者只需要进修,这里有一些关于几何重新包装的细节。

从历史上看,git重新包装做了两件事之一:它要么将所有松散对象重新包装成一个新包(可选地删除每个对象的松散副本),或者它将所有包重新包装成一个新的包(可选地删除冗余包)。

一般来说,当包装较少时,Git有更好的性能,因为许多操作缩放了存储库中的包数。因此,将所有内容包装在一起,往往是一个好主意。但历史上讲,经常忙碌的存储库需要所有的东西都被打包成一个巨大的包。这是因为可达性位图,这是服务器端Git性能的关键优化,只能描述单个包中的对象。因此,如果您想使用位图有效地覆盖存储库中的许多对象,那么这些对象必须存储在同一个包中。

我们正在努力去除这个限制(你可以阅读更多关于我们如何完成的信息)但是,沿着方式的一个重要步骤是实现一种新的重新重种方案,它在具有相对较少的包之间交易,以及最近添加的对象(换句话说,近似自上一个添加的新对象重新包装)。

为了做到这一点,Git学习了一种新的“几何”重新打包策略。这个想法是确定一组可以组合在一起的包,以便剩余的包根据对象的大小形成一个几何级数。换句话说,如果最小的包有N对象,那么下一个最大的包至少有2n.沿途的每个步骤,对象,等等,加倍(或由任意常数)加倍(或由任意常数)。

为了更好地了解这是如何工作的,让我们通过七个包上的一个例子来工作。首先,GIT根据它们包含的对象数(每个方形中的数字),按升序命令所有包(以下面的绿色或红色广场表示)。然后,比较相邻的包装(以最大的包装开始并朝向较小的包装开始),以确保存在几何进展:

这里,第二和第三包之间的进展被打破。那是因为这两个包都具有相同数量的对象(在这种情况下,只有一个)。然后,GIT决定至少第一两个包装将包含在旨在恢复几何进度的新包装中。然后,它需要弄清楚必须卷起多少个较大的包以保持进展:

将前两个包组合在一起我们便能够获得两个对象,但它们仍然太大而无法融入进程中(因为第二大包中只有一个对象)。但卷起前4个包就足够了,因为第5个包包含的对象是前4个包加起来的两倍多:

你可以用下面的脚本在几何重新打包之前和之后,通过比较笔记本电脑上的存储库中的包大小来尝试一下:

$ packsize(){find .git /对象/ pack -type f-name'* .pack'|读完包;do printf“%7d%s \ n”\“$(git show-index <$ {pack%.pack} .idx | wc -l)”$ pack“完成|$ packsize#前git重新包装--geometric = 2 -d $ packsizes#之后

我们还提供了编写新版本的补丁磁盘反向索引格式多包索引.这种格式将是最终被使用通过允许Git将位位置映射回多包索引中的对象,来增强多包位图。

在一起,这两个特征将使可以使用可达性位图覆盖生成的包中的对象,即使剩余多个包。这些补丁仍在抛光和审核,但期待我们纳入释放时的更新。

来源来源

一种新的合并策略

当Git在两个分支之间执行合并时,它使用几个“策略”后端之一来解决更改。最初的策略被简单地称为解决是否有一个标准三方合并.但这种违约被取代了在Git历史的早期通过合并递归,这有两个重要的优势:

  • 在“交叉”合并的情况下(其中没有一个单一的
    两个分支的共同点),策略
    对每一个执行一系列的合并(递归的,名字由此而来)
    可能的基础。这可以解决那些解决
    战略会产生冲突。

  • 它沿每个分支检测到文件级重命名。一个文件
    在一边修改,但更名为另一方将有其
    修改适用于重命名的目的地(而不是
    产生非常令人困惑的冲突)。

合并递归作为Git的默认设置,它已经很好地使用了很多年,但是它有一些缺点。它最初是作为外部Python脚本编写的,使用Git的管道命令来检查数据。这是后来改写在C中,提供了速度提升。但其代码组织和数据结构仍然反映了它的起源:它仍然主要在Git的“索引”(用于新提交)和工作树中收集更改的磁盘区域)。

这导致多年来几年棘手的角落案例(例如,这一个或一些人这些)。

合并递归的起源也使得优化和扩展代码变得更加困难。在大多数工作流程中,合并时间并不是瓶颈,但在一些大的情况下(特别是涉及重命名)合并递归可能会很慢。同样,合并后端用于合并两组更改的许多操作。一个择优挑选或者变基操作可以执行一系列合并,并加速它们具有明显的效果。

合并-ORT.策略是具有相同概念(递归和重命名检测)的临时重写,但解决了许多长期存在的正确性和性能问题。结果是得更快。对于合并(但是一个大的,复杂的,包含很多重命名的合并),合并-ORT.获得超过500倍的加速。对于重基操作中的一系列类似的合并,加速超过9000x(因为合并-ORT.能够缓存并重复使用对合并共有的一些计算)。这些案件被选择特别糟糕合并递归算法,但在我们对典型案例的测试中,我们发现合并-ORT.总是比归并递归快一点。真正的胜利是这样的合并-ORT.始终如一地执行此快速合并递归具有高方差。

最重要的是,生成的代码更简洁,更易于操作。它修复了一些已知的bug合并递归.它更加谨慎地访问树的不变部分,这意味着使用部分克隆的人员应该能够完成更多合并,而无需下载额外的对象。而且因为它不依赖于执行合并的同时索引或工作树,因此它将为像工具开辟新的机会git日志显示合并(例如,普通合并结果和最终提交状态之间的差异,这显示了作者如何解决任何冲突)。

合并-ORT.可能成为Git未来版本的默认策略。与此同时,您可以通过运行尝试git merge -s ort或设置你的拉扯..配置,ORT.(尽管名称不同,这用于任何合并,而不仅仅是git拉)。您可能没有看到所有相同的加速;其中一些需要更改Git的其他部分(例如,变基帮助在每个单独的合并之间传递缓存数据)。

看看,而不是链接到源代理,其中超过150分布超过十几个分支机构,看看这个总结从作者上邮寄列表。或者如果您想深入,请查看他的系列博客员潜入理论以及各种优化的细节:

第1部分
第2部分
第3部分
第4部分
第5部分

所有的

按照我们通常的风格,我们喜欢详细地介绍最近发布的两三个项目,然后是十几个较小的主题。现在我们已经解决了前者的问题,下面是Git 2.32和2.33中一些有趣的变化:

  • 你可能已经使用过Git Rev-List推动Git的历史遍历机械。
    脚本时,它可能真的很有用,特别是如果您需要列出
    在历史的两个端点之间提交/对象。

    Git Rev-List有一个非常方便的人- 漂亮允许它格式化的国旗
    它所遇到的提交。- 漂亮可以显示有关提交的信息
    (例如其作者,作者日期,哈希,其消息的部分,等等)。但脚本时可能难以使用。假设你想要你写的天天列表。你可能会想到这样的东西:

    $ git rev-list --format =%--author = peff head |HEAD-4提交27F45CCF336D70E9078075EB963FB92541DA8690 2021-07-26提交8231C841FF7F213A86AA1FA890EA213F2DC630BE 2021-07-26

    (这里,我们只是在问REV-LIST.显示每次提交的作者日期
    由@Peff写的。)但是那些线路交流了什么犯罪
    这是由于一个历史怪癖REV-LIST.首先写一条线
    包含提交<散列>在显示提交之前- 漂亮
    为了保持与现有脚本的向后兼容性
    怪癖必须留下来。

    在Git 2.33中,您可以指定- nno-commit-header选择退出
    历史设计决策,使脚本脚本更容易:

    来源

  • 这是一块git trivia:说你希望列出小于200字节的所有斑点.你可能会考虑使用REV-LIST.s- 筛选功能(与动力相同的机制部分克隆)完成这一点。但是你会期望打印以下内容?

    $ git rev-list --objects --no-object-names \ --filter = blob:limit = 200 v2.32.0..v2.33.0 \ |git cat-file  - 咬 - 检查='%(ObjectType)'|排序-u.

    (在这个例子中,我们问REV-LIST.列出所有引入的对象
    自版本2.32以来,过滤大于200字节的所有BLOB。
    然后,我们问CAT文件打印出所有这些对象的类型和
    列出唯一值)。

    如果你以为只是“blob”,那你就错了!的——= blob过滤:限制= 200
    只有过滤器blob。它并没有停止REV-LIST.从打印非blob对象。

    在Git 2.32中,您可以通过用新的斑点排除斑点来解决这个问题
    --filter =对象:type = 筛选。自多个以来- 筛选S通过采取结果的结合组合在一起,这是技巧:

    $ git rev-list --objects --no-object-names \ --filter =对象:type = blob \ --filter = blob:limit = 200 \ -filter-提供 - 对象v2.32.0..v2。33.0 \ |git cat-file  - 咬 - 检查='%(ObjectType)'|排序-u blob.

    (这里,- Filter提供的 - 物体允许REV-LIST.申请相同
    过滤到其遍历的提示,豁免过滤
    默认)。

    来源

  • 你可能知道git日志的“装饰”功能,它将输出添加到
    某些提交指示它们的引用点。例如:

    $ git log --oneline --decorate |HEAD -1 2D755DFAC9(头部 - >主,标签:V2.33.0-RC1,OUSION / MASTER,OUSIN / HEAD)GIT 2.33-RC1

    默认情况下,如果输出要到终端或如果- 装饰给了。但是加载这些装饰可以在这样的例子中浪费:

    $ git log -graph --decorate --format ='%h%s'|HEAD -1 2D755DFAC9 GIT 2.33-RC1

    在这里,Git将浪费时间加载所有参考- 装饰
    被给出但它- 格式不引起任何装饰信息
    写入产出。

    Git 2.33学会了如何检测加载引用装饰有用(如果他们会出现在输出),它优化了加载过程做尽可能少的工作即使我们展示装饰品,但装饰对象并不出现在输出。

    来源

  • 说起git log -format.Git 2.32自带了一些占位符
    新的。您现在可以以“人类”格式显示作者和提交日期(哪个我们谈过的当它被引入时在Git 2.21中) 和%啊%ch.

    %(描述)占位符可以包括输出
    git描述在日志中显示提交。您可以使用%(描述)得到的裸输出git描述在每一行,或者你可以写%(描述:匹配= < foo >,排除= > <酒吧)控制- 比赛——排除选项。

    来源来源

  • 你有没有在一系列补丁上工作,并意识到你
    忘了改变几个罪行吗?如果你有,你可能
    已经诱惑了重启回到那个点,让你的变化,然后
    择优挑选剩下的提交回顶部。

    有更好的方法:如果您直接在工作副本中进行更改
    (即使在您在上面写的更多补丁后),您也可以制作特殊的“修复”
    提交的git commit --fixup.选择。这个选项创建一个新的
    使用您预先申请的更改提交。然后,当你
    rebase,git自动序列在正确的位置和
    把它的内容压回你原来想要的地方。

    但是,如果不是改变修补程序的内容,你想要怎么办
    更改日志消息本身?在git 2.32中,Git提交学会了一个新的
    --fixup =(修改| reworm):选项,允许您调整
    行为——固定.和--fixup =修改, Git将替换日志
    消息和补丁内容与您的新一个,所有的没有你必须
    暂停您的工作流程以重新设置基础。

    如果你使用--fixup = reworle相反,您可以告诉Git只替换
    日志消息,同时留下冗余的重对修补程序的内容。

    来源

  • 你可能熟悉git的“拖车”机制,结构
    有时会在提交结束时出现的信息。例如,你可能见过用来表示开发人员的原产证明在承诺结束时签署了.或者你可能看到过项目表明谁审查了什么审查

    这些拖车通常在制定提交消息时用手写入
    在您的编辑中。但现在Git可以通过新的新手插入这些拖车本身
    - 预告片跑步时的标志Git提交.拖车自动
    插入到正确的位置,以便Git以后可以解析它们。这可以
    特别强大的新——固定我们刚刚讨论过的选项。为
    示例,将丢失的预告片添加到提交,喷火

    $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^ . $ git rebase -i——autosquash foo^

    来源

  • 这里是两个与签出相关的花絮中的第一个。Git使用git结帐程序来更新您的工作副本(即您读取/编辑/编译的磁盘上的实际文件),以匹配历史中的特定状态。直到最近,git结帐通过逐个创建、修改或删除文件和目录来工作。

    当旋转磁盘驱动器更常见时,任何性能改进
    并行化这个过程可以忽略不计,因为
    硬盘驱动器比更现代的固态更频繁地更频繁地I / O绑定
    和基于NVME的驱动器。

    (当然,这是一点点过度简化:有时有
    更多的任务给I/O调度器更多的项目来处理,这实际上
    可以帮助大多数旋转磁盘,因为请求可以通过拼盘订购
    的位置。因此,Git使用并行方式刷新索引
    lstat()线程。)

    在git 2.32中,git结帐学习了如何更新工作副本
    通过划分需要执行到不同组的更新来并行,
    然后将每个组委派给工人流程。有两个旋钮
    调整:

    • 结账。工作者配置更新时要使用的worker数量
      树(您可以使用' 0 '表示它应该使用相同数量的工人
      因为有逻辑CPU核心)。

    • checkout.thresholdForParallelism配置有多少更新
      在PIT踢出并行代码路径之前必要的
      顺序的。

    如图所示,当在不同的环境中检出存储库时,这些功能可以提供实质性的加速在这里

    来源来源来源

  • 在一个早期的博客文章中,德里克提尔谈到了稀疏结账.稀疏结账的目标是让它感觉像你工作的存储库很小,无论它实际上是多么大。

    即使稀疏结账缩小了工作副本(即在磁盘上创建的文件和目录的数量),即指数- 数据结构git用于创建提交 - 历史地跟踪了存储库中的每个文件,而不仅仅是稀疏结账中的文件。这使得在稀疏结账时需要索引速度的操作,即使结帐小,因为Git必须计算和重写修改索引的操作的整个索引。

    Git 2.32已更新许多索引内部,只能在稀疏结账中跟踪文件中的文件和在操作时稀疏结账边界处的任何目录锥形模式

    与索引相互作用的不同命令每个都有自己的
    关于指数应该如何工作的假设,所以它们正在进行中
    被一个一个地更新。到目前为止,git结帐Git提交, 和git
    地位
    已更新,更多的命令即将推出。

    您可以通过启用存储库中的稀疏索引
    index.sparse.配置变量。但是请注意,虽然这个功能仍然
    正在开发的是,Git可能想要转换稀疏索引
    到一瞬间,这可能比你正在执行的原始操作慢。在未来的版本中,在转换所有索引相关命令之前,较少命令将展示此行为。

    来源来源来源

  • 这里有一个简短而甜蜜的来自2.32:a新的SECURITY.md文档介绍了解释如何安全地报告Git中的漏洞。最重要的是,以安全为中心的邮件列表的电子邮件地址(git-security@googlegroups.com.)更加突出地列出。(对于令人愉快的,涵盖禁运安全发布的细节也是有关和分发的。)

    来源

最后,在最后一对夫妇中进行了许多与位图相关的优化
的版本。以下是一些例子:

  • 使用可达性位图以服务,例如获取请求,Git Marks
    客户已成为“不感兴趣”的对象,这意味着它们
    不需要再次发送。但直到最近,如果一个标签被标记为
    不感兴趣的是,指向的对象也没有标记为
    无趣的。

    因为留住和想要与一个相结合而不是但这只虫子没有
    影响正确性。但它可能导致Git才能浪费CPU周期
    导致全吹的对象在应标记的对象时漫步
    由于不感兴趣的位于位图之外。git 2.32修复了这个bug
    将标记的不吸引力状态传播到标记对象。

    来源

  • 一些内部Git进程必须构建自己的可达位图
    飞(就像现有位图提供了完整的覆盖
    直到最近的分支和标签,所以补充位图必须是
    然后生成或者'd)。在建立这些禁用的位图时,我们
    可以避免遍历已经在位图中的提交,因为我们
    要知道从该提交中可以到达的所有对象也将在
    位图也是如此。

    但我们不会对树做同样的事情。通常这没什么大不了的。
    根树通常不会在多个提交之间共享,因此
    步行往往值得。但遍历共享的子树是浪费的
    如果已经看到了那些子树(因为我们知道他们所有人
    也可以看到可触及的物体)。

    Git 2.33实现了与我们之前跳过的优化相同的优化
    也为树提交,它提供了一些实质性的加速,
    特别是服务器端操作。

    来源

  • 在使用可达性位图的同时生成包时,Git将尝试
    或多或少从现有包文件的开头发送区域
    逐字。但“枚举对象”进度仪中有一个错误
    这导致服务器短暂地闪过重用对象的数量
    然后在计算它将的对象之前将计数重置为零
    其实包本身。

    由于包重用,这个虫子有点难以捕捉或注意到
    机制只最近变得更加侵略,因为你必须在终端的正确时刻看到它来注意到。

    但无论如何,这个错误已经纠正,所以现在你会看到准确的
    无论你盯着你,你的终端进步表。

    来源

还有一袋薯片

这只是最近一对释放的变化样本。有关更多,请查看发行说明2.322.33, 或者以前的任何版本在git存储库中。