TLDR;如mentioned in GitMerge 2019:
git config --global core.commitGraph true
git config --global gc.writeCommitGraph true
cd /path/to/repo
git commit-graph write
实际上(见最后),Git 2.24+(2019 年第三季度)不需要前两个配置:默认情况下它们是 true。
正如T4cC0re 在the comments 中提到的那样:
如果你使用的是 git 2.29 或更高版本,你应该运行:
git commit-graph write --reachable --changed-paths
这将预先计算文件路径,因此作用于文件的git log 命令也可以从这个缓存中受益。
Git 2.18(2018 年第二季度)将提高 git log 的性能:
见commit 902f5a2(2018 年 3 月 24 日)René Scharfe (rscharfe)。
请参阅commit 0aaf05b、commit 3d475f4(2018 年 3 月 22 日)Derrick Stolee (derrickstolee)。
请参阅 brian m. carlson (bk2204) 的 commit 626fd98(2018 年 3 月 22 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 51f813c,2018 年 4 月 10 日)
sha1_name:使用bsearch_pack() 表示缩写
在针对单个对象 ID 计算缩写长度时
packfile,方法find_abbrev_len_for_pack()目前实现
二进制搜索。
这是几个实现之一。
此实现的一个问题是它忽略了pack-index 中的扇出表。
翻译此二分搜索以使用现有的bsearch_pack() 方法
正确使用扇出表。
由于使用了扇出表,缩写计算为
比以前快了一点。
对于完全重新打包的 Linux 存储库副本,改进了以下“git log”命令:
* git log --oneline --parents --raw
Before: 59.2s
After: 56.9s
Rel %: -3.8%
* git log --oneline --parents
Before: 6.48s
After: 5.91s
Rel %: -8.9%
相同的 Git 2.18 增加了一个提交图:预先计算和存储祖先遍历所需的信息,以优化图遍历。
参见commit 7547b95、commit 3d5df01、commit 049d51a、commit 177722b、commit 4f2542b、commit 1b70dfd、commit 2a2e32b(2018 年 4 月 10 日)和commit f237c8b、@987654341、@、@987 commit ae30d7b、commit b84f767、commit cfe8321、commit f2af9f5(2018 年 4 月 2 日)Derrick Stolee (derrickstolee)。
(由 Junio C Hamano -- gitster -- 合并到 commit b10edb2,2018 年 5 月 8 日)
commit:将提交图与提交解析集成
教 Git 检查提交图文件以提供
调用parse_commit_gently()时的结构提交。
此实现满足结构提交的所有后置条件,包括加载父级、根树和提交日期。
如果core.commitGraph 是false,则不要检查图形文件。
在测试脚本t5318-commit-graph.sh,添加output-matching条件
只读图操作。
通过从图中加载提交而不是解析提交缓冲区,我们
节省大量时间进行长时间的提交遍历。
以下是 Linux 存储库副本的一些性能结果,其中“master”有 678,653 个可访问的提交,并且比“origin/master”落后 59,929 个提交。
| Command | Before | After | Rel % |
|----------------------------------|--------|--------|-------|
| log --oneline --topo-order -1000 | 8.31s | 0.94s | -88% |
| branch -vv | 1.02s | 0.14s | -86% |
| rev-list --all | 5.89s | 1.07s | -81% |
| rev-list --all --objects | 66.15s | 58.45s | -11% |
要了解有关提交图的更多信息,请参阅“How does 'git log --graph' work?”。
相同的 Git 2.18(2018 年第二季度)添加了延迟加载树。
代码已被教导使用存储的重复信息
在提交图文件中了解提交的树对象名称
避免在有意义时打开和解析提交对象
这样做。
见commit 279ffad(2018 年 4 月 30 日)SZEDER Gábor (szeder)。
请参阅Derrick Stolee (derrickstolee) 的commit 7b8a21d、commit 2e27bd7、commit 5bb03de、commit 891435d(2018 年 4 月 6 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit c89b6e1,2018 年 5 月 23 日)
commit-graph: 延迟加载树提交
提交图文件提供对提交数据的快速访问,包括
图中每个提交的根树的 OID。表演时
深度提交图遍历,我们可能不需要加载大部分树
对于这些提交。
延迟加载从图表加载的提交的树对象
直到通过get_commit_tree()请求。
不要为不在图中的提交延迟加载树,因为这需要重复解析,并且在不需要树时相对性能改进很小。
在 Linux 存储库上,对以下项目进行了性能测试
命令:
git log --graph --oneline -1000
Before: 0.92s
After: 0.66s
Rel %: -28.3%
Git 2.21(2019 年第一季度)添加了松散缓存。
参见commit 8be88db(2019 年 1 月 7 日)和 commit 4cea1ce、commit d4e19e5、commit 0000d65(2019 年 1 月 6 日)René Scharfe (rscharfe)。
(由@987654368 中的Junio C Hamano -- gitster -- 合并@,2019 年 1 月 18 日)
object-store:每个子目录使用一个oid_array 用于松散缓存
松散对象缓存根据需要一次填充一个子目录。
它存储在oid_array 中,每次添加操作后都必须使用它。
所以在查询大范围的对象时,部分填充的数组需要最多255次,比一次排序要长100倍以上。
为每个子目录使用一个oid_array。
这确保条目只需排序一次。它还避免了每次缓存查找的八个二分搜索步骤。
缓存用于日志占位符%h、%t 和%p 的冲突检查,我们可以在存储库中看到更改速度加快了它们的速度。每个子目录 100 个对象:
$ git count-objects
26733 objects, 68808 kilobytes
Test HEAD^ HEAD
--------------------------------------------------------------------
4205.1: log with %H 0.51(0.47+0.04) 0.51(0.49+0.02) +0.0%
4205.2: log with %h 0.84(0.82+0.02) 0.60(0.57+0.03) -28.6%
4205.3: log with %T 0.53(0.49+0.04) 0.52(0.48+0.03) -1.9%
4205.4: log with %t 0.84(0.80+0.04) 0.60(0.59+0.01) -28.6%
4205.5: log with %P 0.52(0.48+0.03) 0.51(0.50+0.01) -1.9%
4205.6: log with %p 0.85(0.78+0.06) 0.61(0.56+0.05) -28.2%
4205.7: log with %h-%h-%h 0.96(0.92+0.03) 0.69(0.64+0.04) -28.1%
Git 2.22(2019 年 4 月)在使用从提交图文件读取的数据之前检查错误。
参见commit 93b4405、commit 43d3561、commit 7b8ce9c、commit 67a530f、commit 61df89c、commit 2ac138d(2019 年 3 月 25 日)和commit 945944c、commit f6761fa(2019 年 2 月 21 日)@9875 .
(由 Junio C Hamano -- gitster -- 合并于 commit a5e4be2,2019 年 4 月 25 日)
commit-graph 写:如果现有图已损坏,请不要死
当commit-graph 被写入时,我们最终会调用parse_commit()。这将反过来调用代码,该代码将咨询现有的commit-graph 关于提交,如果图表损坏,我们就会死。
因此,如果设置了core.commitGraph=true,我们会进入一个失败的“commit-graph verify”不能用“commit-graph write”跟进的状态,要么需要手动删除图表才能继续,要么core.commitGraph 需要设置为“false”。
更改“commit-graph write”代码路径以使用新的 parse_commit_no_graph() 帮助程序而不是 parse_commit() 来避免这种情况。
后者将使用use_commit_graph=1 调用repo_parse_commit_internal(),如177722b 所示(“commit:集成提交图与提交解析”,2018-04-10,Git v2.18.0-rc0)。
完全不使用旧图会稍微减慢新图的写入速度,但这是防止现有提交图中的错误传播的明智方法。
使用 Git 2.24+(2019 年第三季度),提交图默认处于活动状态:
参见commit aaf633c、commit c6cc4c5、commit ad0fb65、commit 31b1de6、commit b068d9a、commit 7211b9e(2019 年 8 月 13 日)Derrick Stolee (derrickstolee)。
(由 Junio C Hamano -- gitster -- 合并commit f4f8dfe,2019 年 9 月 9 日)
commit-graph:默认开启commit-graph
commit-graph 功能在过去有很多活动
自推出以来大约一年。
该功能是中型到大型存储库的关键性能增强,并且不会显着损害小型存储库。
更改core.commitGraph 和gc.writeCommitGraph 的默认值
为 true 以便用户默认受益于此功能。
仍然使用 Git 2.24(2019 年第四季度),配置变量告诉“git fetch”在完成后写入提交图。
参见Derrick Stolee (derrickstolee) 的commit 50f26bd(2019 年 9 月 3 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 5a53509,2019 年 9 月 30 日)
fetch:添加 fetch.writeCommitGraph 配置设置
commit-graph 功能现在默认开启,默认在“git gc”期间写入。
通常,Git 仅在“git gc --auto”命令将gc.auto 设置传递给实际工作时才编写提交图。这意味着提交图将
通常落后于每天使用的提交。
要及时了解最新提交,请在“git fetch”中添加一个步骤,以便在获取新对象后编写提交图。
fetch.writeCommitGraph 配置设置 允许编写拆分提交图,因此平均而言,编写此文件的成本非常小。有时,提交图链会崩溃到一个级别,这对于非常大的存储库来说可能会很慢。
如需额外使用,请在启用feature.experimental 时将默认值调整为true。
仍然使用 Git 2.24(2019 年第四季度),commit-graph 更加强大。
参见Jeff King (peff) 的commit 6abada1、commit fbab552(2019 年 9 月 12 日)。
(由 Junio C Hamano -- gitster -- 合并到 commit 098e8c6,2019 年 10 月 7 日)
commit-graph: 碰撞 DIE_ON_LOAD 检查实际加载时间
Commit 43d3561 (commit-graph write: 不要死如果现有的图
已损坏,2019-03-25,Git v2.22.0-rc0) 添加了我们仅在测试套件中使用的环境变量,$GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD。
但是它将对该变量的检查放在prepare_commit_graph() 的最顶部,每次我们想要使用提交图时都会调用它。
最重要的是,它在我们检查快速路径“我们是否已经尝试加载?”之前出现,这意味着我们最终会在每次使用提交图时调用getenv(),而不是仅仅在何时使用我们加载。
getenv() 被允许有意想不到的副作用,但这不应该
这里有问题;我们正在延迟加载图表,所以很明显
至少 一个 调用此函数将调用它。
但是效率低下。 getenv() 通常必须进行线性搜索
通过环境空间。
我们可以记住调用,但将检查降低到实际加载步骤仍然更简单。这对我们在 t5318 中的唯一用户来说很好,并产生了这个微小的现实世界加速:
[before]
Benchmark #1: git -C linux rev-list HEAD >/dev/null
Time (mean ± σ): 1.460 s ± 0.017 s [User: 1.174 s, System: 0.285 s]
Range (min … max): 1.440 s … 1.491 s 10 runs
[after]
Benchmark #1: git -C linux rev-list HEAD >/dev/null
Time (mean ± σ): 1.391 s ± 0.005 s [User: 1.118 s, System: 0.273 s]
Range (min … max): 1.385 s … 1.399 s 10 runs
Git 2.24(2019 年第四季度)还包括回归修复。
参见Derrick Stolee (derrickstolee) 的commit cb99a34、commit e88aab9(2019 年 10 月 24 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit dac1d83,2019 年 11 月 4 日)
报告者:Johannes Schindelin
帮助者:Jeff King
帮助者:Szeder Gábor
签字人:Derrick Stolee
之前的提交包括对 fetch.writeCommitGraph 问题的失败测试以及在带有子模块的 repo 中获取。在这里,我们修复了该错误并将测试设置为"test_expect_success"。
当<url> 的远程仓库有一个子模块时,这组命令就会出现问题。
注意 --recurse-submodules 不需要演示这个错误。
$ git clone <url> test
$ cd test
$ git -c fetch.writeCommitGraph=true fetch origin
Computing commit graph generation numbers: 100% (12/12), done.
BUG: commit-graph.c:886: missing parent <hash1> for commit <hash2>
Aborted (core dumped)
作为初始修复,我将builtin/fetch.c 中调用write_commit_graph_reachable() 的代码转换为启动“git commit-graph 写入--reachable --split”进程。该代码有效,但不是我们希望该功能长期有效的方式。
该测试确实表明问题一定与“git fetch”进程的内部状态有关。
commit-graph.c 中的write_commit_graph() 方法确保我们计划编写的提交使用close_reachable()“在可达性下关闭”。
此方法从输入提交开始,并使用UNINTERESTING 标志来标记已访问过哪些提交。这允许步行花费O(N) 时间,其中N 是提交数,而不是O(P) 时间,其中P 是路径数。 (路径的数量可以是提交次数中的指数。)
但是,UNINTERESTING 标志在代码库中的很多地方都使用过。此标志通常意味着阻止提交遍历的一些障碍,例如在修订遍历中比较历史记录。
步行完成后通常不会清除它,因为这些步行的起点没有UNINTERESTING 标志,clear_commit_marks() 会立即停止。
这发生在使用遥控器进行“git fetch”通话期间。 fetch 协商将远程 refs 与本地 refs 进行比较,并将一些提交标记为UNINTERESTING。
我测试了运行 clear_commit_marks_many() 以清除 close_reachable() 中的 UNINTERESTING 标志,但提示没有标志,所以什么也没做。
原来calculate_changed_submodule_paths() 方法有问题。谢谢,Peff,指出这个细节!更具体地说,对于每个子模块,collect_changed_submodules() 运行修订遍历以基本上对子模块列表进行文件历史记录。如果通过不更改子模块来简化它们,则该修订步行标记提交 UNININTERESTING。
相反,我最终得出结论,我应该使用代码的任何其他部分中未使用的标志。在commit-reach.c 中,为提交遍历算法定义了许多标志。 REACHABLE 标志似乎是最有意义的,它似乎并没有在文件中实际使用。
REACHABLE 标志在 commit-reach.c 的早期版本中使用,但被 4fbcca4 删除(“commit-reach: make can_all_from_reach...linear”, 2018-07-20, v2.20.0-rc0) .
将REACHABLE 标志添加到commit-graph.c 并在close_reachable() 中使用它而不是UNINTERESTING。
这修复了手动测试中的错误。
从多个远程并行提取到同一个存储库与最近更改(可选)在提取作业完成后更新提交图有不良交互,因为这些并行提取相互竞争。
这已在 Git 2.25(2020 年第一季度)中得到纠正。
参见Johannes Schindelin (dscho)commit 7d8e72b、commit c14e6e7(2019 年 11 月 3 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit bcb06e2,2019 年 12 月 1 日)
fetch:添加命令行选项--write-commit-graph
签字人:约翰内斯·辛德林
此选项会覆盖配置设置 fetch.writeCommitGraph,如果两者都已设置。
还有:
fetch:避免 fetch.jobs/fetch.writeCommitGraph 之间的锁定问题
签字人:约翰内斯·辛德林
当fetch.jobs 和fetch.writeCommitGraph 都设置了时,我们目前尝试在每个并发获取作业中编写提交图,这经常会导致这样的错误消息:
fatal: Unable to create '.../.git/objects/info/commit-graphs/commit-graph-chain.lock': File exists.
让我们通过推迟编写提交图直到所有获取作业完成来避免这种情况。
在获取用于拆分结果文件的参数的计算虚假值时编写拆分提交图文件的代码,已在 Git 2.25(2020 年第一季度)中更正。
参见commit 63020f1(2020 年 1 月 2 日)Derrick Stolee (derrickstolee)。
(由 Junio C Hamano -- gitster -- 合并到 commit 037f067,2020 年 1 月 6 日)
签字人:Derrick Stolee
在50f26bd ("fetch: add fetch.writeCommitGraph config setting", 2019-09-02, Git v2.24.0-rc0 -- merge 列在batch #4) 中,内置的 fetch 添加了使用“--split”功能编写提交图的能力。
此功能创建多个提交图文件,这些文件可以基于一组“拆分选项”进行合并,包括大小倍数。
默认大小倍数为 2,旨在提供提交图链的log_2N 深度,其中 N 是提交次数。
但是,我在 dogfooding 期间注意到,当仅由“git fetch”构建时,我的提交图链变得非常大。
事实证明,在split_graph_merge_strategy() 中,我们将size_mult 变量默认为2,除非我们用上下文的split_opts 覆盖它(如果它们存在)。
在builtin/fetch.c 中,我们创建了这样一个split_opts,,但不使用值填充它。
这个问题是由于两个故障造成的:
- 尚不清楚我们是否可以将标志
COMMIT_GRAPH_WRITE_SPLIT 添加到 NULL split_opts。
- 如果我们有一个非 NULL
split_opts,,那么即使给出了零值,我们也会覆盖默认值。
纠正这两个问题。
- 首先,在选项提供零值时,请勿覆盖@ 987654592。
- 其次,停止在 fetch 内置函数中创建
split_opts。
请注意,在使用 magic pathspec 时,git log 在 Git 2.22(2019 年 5 月)和 Git 2.27(2020 年第二季度)之间被破坏。
“git log :/a/b/”的命令行解析被破坏了大约整整一年,没有人注意到,已经更正了。
参见Jeff King (peff)commit 0220461(2020 年 4 月 10 日)。
请参阅 Junio C Hamano (gitster) 的 commit 5ff4b92(2020 年 4 月 10 日)。
(由 Junio C Hamano -- gitster -- 合并于 commit 95ca489,2020 年 4 月 22 日)
sha1-name: 不要假设 ref store 已经初始化了
报告人:Érico Rolim
c931ba4e ("sha1-name.c``: remove the_repo from handle_one_ref()", 2019-04-16, Git v2.22.0-rc0 -- merge 列在batch #8 ) 将for_each_ref() 助手的使用替换为refs_for_each_ref(),它可以与默认存储库实例的主引用存储一起使用,它可以在任何引用存储实例上工作,假设给定函数的存储库实例具有它的引用存储已初始化。
但可能没有人对其进行初始化,在这种情况下,代码最终会取消引用 NULL 指针。
还有:
签字人:杰夫·金
结构存储库中的“refs”指针以NULL 开头,但在通过get_main_ref_store() 访问时会延迟初始化。
然而,调用代码很容易忘记这一点并直接访问它,导致代码在某些时间可以工作,但如果在其他人访问 refs 之前调用它就会失败。
这是由5ff4b920eb 修复的错误的原因(“sha1-name:不要假设引用存储已初始化”,2020-04-09,Git v2.27.0 -- merge 在@ 中列出987654444@)。为了防止出现类似的bug,让我们更清楚地将“refs”字段标记为私有。