在具有许多提交的存储库上加速`git blame`

use*_*739 3 git git-blame

我正在尝试git blame以下文件(在我的本地机器上运行),因为它太慢而无法产生 GitHub 的责备:

https://github.com/Homebrew/homebrew-core/blob/master/Formula/sqlite.rb

但是在本地运行也很慢,在我的机器上运行一分钟以上

time git --no-pager blame Formula/sqlite.rb > /dev/null
Run Code Online (Sandbox Code Playgroud)

存储库包含超过 150K 的提交。

有没有办法加快git blame命令的速度?

Von*_*onC 5

在 Git 2.27(2020 年第二季度)中,“ git blame”学会了利用存储在提交图文件中的“ changed-paths布隆过滤器,并引入了git log.

请参阅Jeff King ( ) 的commit 1b4c57fcommit 24b7d1ecommit fe88f9f(2020 年 4 月 23 日。 请参阅Derrick Stolee ( ) 的commit 0906ac2commit b23ea97commit 8918e37(2020 年 4 月 16 日(由Junio C Hamano合并-- --提交 6d56d4c 中,2020 年 5 月 1 日)peff
derrickstolee
gitster

blame: 使用changed-path布隆过滤器

签字人:德里克·斯托利

changed-path布隆过滤器帮助减少在历史查询所需树解析量

在计算差异之前,我们可以询问过滤器在提交与其第一个父级之间的路径是否发生变化。

  • 如果过滤器说“不”,那么我们可以继续前进而无需解析树。
  • 如果过滤器显示“可能”,那么我们解析树以发现答案实际上是“是”还是“否”。

在计算责备时,有一个部分find_origin()计算提交与其父项之一之间的差异。
当这是第一个父级时,我们可以在调用之前检查布隆过滤器diff_tree_oid()

为了使这个与blame机制一起工作,我们需要bloom_key用初始路径初始化一个结构体。但是,如果检测到重命名,我们还需要向列表中添加更多键。然后我们检查这些键中是否有任何一个在差异中回答“可能”。

如果用户使用“ git blame -C”请求复制检测,那么“重要”文件集可以扩展到更多地方。我不太了解这种情况是如何在怪罪机制中发生的。
因此,在此模式下明确禁用布隆过滤器集成。
稍后的更改可以bloom_key通过对 的适当调用(或调用)扩展数据add_bloom_key()

通常,这是一种性能增强,不应git blame以任何方式改变“ ”的行为。
如果 repo 有一个提交图文件,其中包含计算出的更改路径布隆过滤器,那么他们应该注意到他们的“ git blame”命令的性能有所提高。

以下是我通过归咎于 Linux 内核存储库中的某些路径而发现的一些示例时间:

我专门寻找也被多次编辑的“深层”路径。
作为对比,该MAINTAINERS文件被多次编辑但位于根树中。
这意味着计算相对于路径规范的差异的成本非常小。以下是该命令的时间:

这些时间是五个中最好的。
对于这两种情况,最坏情况下的运行时间约为 2.5 分钟。
请注意,该MAINTAINERS文件在 17,000 多次提交中有 18,740 行。这恰好是此更改提供的改进最少的情况之一。

MAINTAINERS可以很容易地解释文件缺乏改进和其他示例的相对适度改进。
责备机制需要计算行级差异,以确定每次提交更改了哪些行。这占计算时间的很大一部分,并且此更改并未尝试改进算法的该部分。
MAINTAINERS文件很大并且经常更改,因此需要时间来确定哪些行被哪个提交更新。相比之下,代码文件要小得多,并且计算 Linux 邮件列表上单个补丁的逐行差异需要更长的时间。

除了 " -C" 集成之外,我相信git blame在此补丁之后,' '的更改路径布隆过滤器几乎没有什么好处。


不过,请务必使用 Git 2.29(2020 年第四季度),因为存在一个小错误:

请参阅Edmundo Carmona Antoranz ( ) 的commit 1302bad (08 Sep 2020 )(由Junio C Hamano合并-- --提交 e1dd499 中,2020 年 9 月 18 日)eantoranz
gitster

blame.c: 替换!oidcmpfor 的实例oideq

签字人:埃德蒙多·卡莫纳·安托兰兹

0906ac2b (" blame: use changed-path Bloom filters", 2020-04-16, Git v2.27.0-rc0 --合并第 6 批中列出) 引入了对 oidcmp() 的调用,该调用应该oideq()14438c44中引入(“介绍hasheq()oideq()”,2018 年 8 月 28 日,Git v2.20.0-rc0 --合并第 1 批中列出)。


在 Git 2.29(2020 年第四季度)中,“ ( man ) write” 学会了限制使用该选项从头开始计算的布隆过滤器的数量。git commit-graph--max-new-filters

那将受益git blame

提交d356d5d提交98bb796提交59f0d50提交97ffa4f(2020年9月17日),提交809e032(2020年9月18日),提交9a7a9ed提交312cff5(2020年9月16日),以及提交b66d847提交24f951a提交ab14d06提交025d529,由Taylor Blau ( )提交 4f36440(2020 年 9 月 9 日。 请参阅Derrick Stolee ( )提交的 b16a827 (2020 年 9 月 16 日)(由Junio C Hamano合并-- --ttaylorr
derrickstolee
gitster提交 288ed98,2020年 9 月 29 日)

builtin/commit-graph.c: 引入 '--max-new-filters='

帮助者:Junio C Hamano
签字人:Taylor Blau

引入一个命令行标志来指定“ git commit-graph writeman愿意从头开始计算的新布隆过滤器的最大数量。

在此补丁之前,使用“ --changed-paths”的提交图写入将为尚未计算的所有选定提交计算布隆过滤器(即,通过先前使用“ --split”的提交图写入,以便汇总或替换执行)。

由于多种原因,此行为可能导致过长的提交图写入:

  • 可能有很多过滤器的差异需要很长时间才能生成(例如,它们的更改次数接近最大数量,差异本身需要很长时间等)。
  • 旧式提交图(将具有太多条目的过滤器编码为根本没有计算过)导致我们浪费时间重新计算似乎没有计算过的过滤器,却发现它们太大了。

这会使 ' git commit-graph write --changed-paths' ( man )所需时间的上限变得相当不可预测。

为了使此命令的行为更可预测,请引入 ' --max-new-filters=<n>' 以允许最多<n>从头开始计算 ' ' 布隆过滤器。
这让“计算”已知过滤器快速进行,同时限制 Git 愿意执行的慢速任务的数量。

git commit-graph现在包括在其手册页中

使用该--max-new-filters=<n>选项,最多生成n新的布隆过滤器(如果--changed-paths已指定)。
如果n-1,则不强制执行任何限制。
只有出现在新层中的提交才会计入此限制。
要在较早的层上追溯计算布隆过滤器,建议使用--split=replace.


使用 Git 2.31(2021 年第一季度),优化“ git blameman

请参阅Rafael Silva ( ) 的commit 8e16eff(2021 年 2 月 17 日(由Junio C Hamano合并-- --commit 18decfd,2021 年 2 月 25 日)raffs
gitster

blame: 删除不必要的使用 get_commit_info()

签字人:Rafael Silva
评论人:Taylor Blau

( man ) 时,调用 以选择如何根据提交的作者日期为输出着色。 它使用 将信息解析为结构,然而,这实际上是不必要的,因为调用者也这样做。git blame --color-by-agedetermine_line_heat()
get_commit_info()commit_infodetermine_line_heat()

相反,让我们更改determine_line_heat()以采用commit_info结构并删除内部调用,get_commit_info()从而清理和优化代码路径。

启用 Git 的 trace2 API 以记录每次调用determine_line_heat()函数的执行时间:

+ trace2_region_enter("blame", "determine_line_heat", the_repository);
  determine_line_heat(ent, &default_color);
+ trace2_region_enter("blame", "determine_line_heat", the_repository);
Run Code Online (Sandbox Code Playgroud)

然后,在 linux.git 中运行git blamekernel/fork.c”并总结每个调用的所有执行时间(大约 1.3k 次调用)导致执行速度提高 2.6 倍(最好是 3):

git built from 328c109303 (The eighth batch, 2021-02-12) = 42ms
git built from 328c109303 + this change                  = 16ms
Run Code Online (Sandbox Code Playgroud)


Sch*_*ern 0

按照 Git 标准,homebrew-core 存储库相当大。一个 250 MB 的存储库,4000 个“公式”的 150,000 次提交。这会影响性能。Github 确实遇到了麻烦。

在此输入图像描述

git blame Formula/sqlite.rb在我的 2018 i7 Macbook 上使用 Git 2.22.0 大约需要 45 秒。按照 Git 标准,速度很慢,但考虑到运行的频率,这是可以接受的git blame

作为该存储库的用户,无需做太多事情。git blame必须向后搜索每个提交以查看哪些提交更改了该文件。不幸的是,git blame似乎没有利用并行处理。

有一些选择...

  1. 请联系 GitHub 了解该问题并希望他们能够解决。
  2. 限制你回顾历史的时间:git blame --since=1.year -- Formula/sqlite.rb
  3. 重新考虑此存储库中需要快速的任何流程git blame
  4. 缓存结果。