git - Speed up `git blame` on repository with many commits
问题描述
I am trying to git blame
the following file (run on my local machine) as it is too slow to generate the blame of GitHub:
https://github.com/Homebrew/homebrew-core/blob/master/Formula/sqlite.rb
But it is also very slow to run locally, over a minute on my machine as measured by
time git --no-pager blame Formula/sqlite.rb > /dev/null
The repository contains over 150K commits.
Is there a way to speed up the git blame
command?
解决方案
在 Git 2.27(2020 年第二季度)中,“ git blame
”学习利用存储在提交图文件中的“ changed-paths
”布隆过滤器,并在git log
.
请参阅Jeff King ( ) 的提交 1b4c57f、提交 24b7d1e、提交 fe88f9f(2020 年 4 月 23 日)。
请参阅Derrick Stolee ( ) 的提交 0906ac2、提交 b23ea97、提交 8918e37(2020 年 4 月 16 日)。(由Junio C Hamano 合并 -- --在提交 6d56d4c中,2020 年 5 月 1 日)peff
derrickstolee
gitster
blame
: 使用changed-path
布隆过滤器签字人:Derrick Stolee
changed-path
Bloom 过滤器有助于减少历史查询期间所需的树解析量。在计算差异之前,我们可以询问过滤器是否在提交和其第一个父项之间更改了路径。
- 如果过滤器说“不”,那么我们可以在不解析树的情况下继续前进。
- 如果过滤器说“可能”,那么我们解析树以发现答案实际上是“是”还是“否”。
在计算责备时,其中有一个部分
find_origin()
计算提交与其父级之一之间的差异。
当这是第一个父级时,我们可以在调用之前检查 Bloom 过滤器diff_tree_oid()
。为了使这项工作与责任机制一起工作,我们需要
bloom_key
用初始路径初始化一个结构。但是,如果检测到重命名,我们需要向列表中添加更多键。然后我们检查这些键中的任何一个是否在差异中回答“可能”。如果用户使用“”请求复制检测
git blame -C
,则“重要”文件集可以扩展的位置更多。我不太了解这在责备机制中是如何发生的。
因此,布隆过滤器集成在此模式下被显式禁用。
以后的更改可以bloom_key
通过适当的调用(或调用)来扩展数据add_bloom_key()
。通常,这是一种性能增强,不应
git blame
以任何方式改变 ' ' 的行为。如果一个 repo 有一个带有计算的更改路径 Bloom 过滤器的提交图文件,那么他们应该注意到他们的“ ”命令
的性能得到了提高。git blame
以下是我通过归咎于 Linux 内核存储库中的一些路径而发现的一些示例时序:
git blame arch/x86/kernel/topology.c
>/dev/null`Before: 0.83s After: 0.24s
git blame kernel/time/time.c >/dev/null
Before: 0.72s After: 0.24s
git blame tools/perf/ui/stdio/hist.c >/dev/null
Before: 0.27s After: 0.11s
我专门寻找也被多次编辑的“深层”路径。
作为对比,该MAINTAINERS
文件被多次编辑,但位于根树中。
这意味着计算相对于路径规范的差异的成本非常小。以下是该命令的时间安排:
git blame MAINTAINERS >/dev/null
Before: 20.1s After: 18.0s
这些时间是五个中最好的。
对于这两种情况,最坏情况的运行时间约为 2.5 分钟。
请注意,该MAINTAINERS
文件在 17,000 多次提交中有 18,740 行。这恰好是此更改提供最少改进的情况之一。可以很容易地解释文件缺乏改进
MAINTAINERS
以及其他示例相对适度的改进。
责备机制需要计算行级差异以确定每次提交更改了哪些行。这占了计算时间的很大一部分,并且这种更改并没有试图改进算法的那部分。
该MAINTAINERS
文件很大并且经常更改,因此需要时间来确定哪些行由哪个提交更新。相比之下,代码文件要小得多,计算 Linux 邮件列表中单个补丁的逐行差异需要更长的时间。在 " " 集成之外,我相信在此补丁之后
-C
,从 ' ' 的更改路径 Bloom 过滤器中获得的收益很少。git blame
不过,请确保使用 Git 2.29(2020 年第四季度),因为存在一个小错误:
请参阅Edmundo Carmona Antoranz ( ) 的提交 1302bad(2020 年 9 月 8 日)。(由Junio C Hamano 合并 -- --在提交 e1dd499中,2020 年 9 月 18 日)eantoranz
gitster
blame.c
!oidcmp
: 替换for 的实例oideq
签字人:埃德蒙多·卡莫纳·安托兰兹
0906ac2b(“
blame
:使用更改路径的 Bloom 过滤器”,2020-04-16,Git v2.27.0-rc0 -批次 #6中列出的合并)引入了对 oidcmp() 的调用,该调用本应在14438c44中引入(“介绍和”,2018 年 8 月 28 日,Git v2.20.0-rc0 --合并在批次 #1中列出)。oideq()
hasheq()
oideq()
在 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、提交 24296、提交 025d1、提交 ab1 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 write
' ( man )愿意从头开始计算的最大新 Bloom 过滤器数量。在此补丁之前,使用 '
--changed-paths
' 写入的提交图将为尚未计算的所有选定提交计算 Bloom 过滤器(即,通过先前使用 '--split
' 写入的提交图,以便汇总或替换执行)。由于多种原因,此行为可能会导致提交图写入过长:
- 可能有很多过滤器的差异需要很长时间才能生成(例如,它们的更改数量接近最大,差异本身需要很长时间等)。
- 旧式提交图(它编码具有太多条目的过滤器,因为根本没有计算过)导致我们浪费时间重新计算似乎没有计算过的过滤器,只是发现它们太大了。
这会使 '
git commit-graph write --changed-paths
' ( man )所需时间的上限变得相当不可预测。为了使该命令的行为更具可预测性,引入 '
--max-new-filters=<n>
' 以允许<n>
从头开始计算最多 ' ' 布隆过滤器。
这可以让“计算”已知过滤器快速进行,同时限制 Git 愿意执行的慢速任务的数量。
git commit-graph
现在在其手册页中包含:
使用该
--max-new-filters=<n>
选项,最多生成n
新的 Bloom 过滤器(如果--changed-paths
已指定)。
如果n
是-1
,则不强制执行任何限制。
只有新层中存在的提交才计入此限制。
要在较早的层上追溯计算 Bloom 过滤器,建议使用--split=replace
.
使用 Git 2.31(2021 年第一季度),“ git blame
” (man)中的优化
请参阅Rafael Silva ( ) 的提交 8e16eff(2021 年 2 月 17 日)。(由Junio C Hamano 合并 -- --在提交 18decfd中,2021 年 2 月 25 日)raffs
gitster
blame
:删除不必要的使用get_commit_info()
签字人:Rafael Silva
审核人:Taylor Blau
当( man )时,调用 以根据提交的作者日期选择如何为输出着色。 它使用 将信息解析为结构,然而,这实际上是不必要的,因为调用者也这样做。
git blame
--color-by-age
determine_line_heat()
get_commit_info()
commit_info
determine_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);
然后,在 linux.git 中运行
git blame
"kernel/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
推荐阅读
- javascript - 正则表达式只允许特定数字
- selenium - 在 CircleCI 中将 Chrome 版本降级到 74
- jquery - 在每个循环中检测 jQuery 中的空数据
- android - 为什么我的图像没有显示在我测试它的设备上?
- azure - Microsoft Azure RA-GRS 存储 - 在 Microsoft 管理的故障转移到次要区域后,数据是否仍进行异地复制?
- php - 允许包中的原生 Laravel “魔术”方法
- reactjs - 将 Kendo React PDF 上传到 Firebase 存储
- android - Moodle 移动版 3.7.0
- vue.js - 如何选择 vue.config.js 位置?
- elasticsearch - 使用 ndjson 格式将文档索引到 elasticsearch