提高git状态性能的方法

100

我在一台Linux机器上有一个10 GB的存储库,它位于NFS上。第一次执行git status需要36分钟,而随后的git status只需要8分钟。似乎Git依赖操作系统缓存文件。像commitstatus这样涉及整个存储库打包/重打包的第一个git命令需要花费很长时间。我不确定您是否在这样大的存储库上使用过git status,但是否有人遇到过这个问题?

我已尝试过git gcgit cleangit repack,但所需时间仍然几乎相同。

子模块或将存储库拆分为较小部分等其他概念是否有帮助?如果有,最适合分割大存储库的方法是什么?还有其他改进在大型存储库中使用git命令所需时间的方法吗?


2
NFS在这里几乎成为了瓶颈。lstat是一种相当同步的操作。 - user611775
1
可能是 Git Status Takes a Long Time to Complete 的重复问题。 - Seth Battin
14个回答

52
更准确地说,git依赖于lstat(2)系统调用的效率,因此调整客户端的“属性缓存超时”可能会起作用。 git-update-index的手册——实际上是git-status的手动模式——描述了您可以采取的措施来缓解这个问题,通过使用--assume-unchanged标志来抑制其正常行为,并手动更新您已更改的路径。您甚至可以编程您的编辑器,在每次保存文件时取消设置此标志。
另一种选择,正如您所建议的,是减小您的检出大小(包文件的大小在这里并不重要)。可选的方法有稀疏检出、子模块或Google的repo工具。
(这里有一个邮件列表关于使用NFS的Git线程,但它并没有回答很多问题。)

32
你错过的事情是:Linus的补丁已经被合并了,可以通过将“core.preloadindex”设置为true来启用 - 有关更多说明,请参见git-config文档。 (我的工作场所使用NFS,我曾遇到这个问题 - 但从未注意到preloadindex设置。感谢您指出正确的方向!) - Cascabel
2
应该在此处接受的答案中添加'git config core.preloadindex true'。可能还需要使用用户1077329的-uno标志。 - ostler.c
2
从Git 2.1.0开始,默认情况下将core.preloadindex标志设置为true:https://git.kernel.org/pub/scm/git/git.git/tree/Documentation/RelNotes/2.1.0.txt - Petr Gazarov

39

我在一个共享的大型项目上也遇到了这个问题,并且使用NFS。

花了一些时间,我才发现可以给git commit 和 git status命令添加-uno 标志。

该标志的作用是禁用查找未跟踪的文件,从而显著减少 NFS 操作次数。原因是为了让 Git 发现未跟踪的文件,必须在所有子目录中查找,因此如果你有许多子目录,则会影响性能。通过禁用Git查找未跟踪的文件,您可以消除所有这些NFS操作。

结合core.preloadindex标志,即可在NFS上获得合理的性能表现。


1
git-status(1) 中所提到的,可以通过设置 status.showUntrackedFiles 配置来将其设置为默认值。 - johankj

37

尝试使用git gc。此外,git clean可能也会有所帮助。

Git手册中写道:

在当前仓库内运行多个日常维护任务,例如压缩文件修订版(以减少磁盘空间和提高性能)和删除不可达的对象,这些对象可能是由于之前运行git add命令而创建的。

鼓励用户在每个仓库中定期运行此任务,以确保良好的磁盘空间利用率和良好的操作性能。

我发现当git status变慢时运行git gc总会有所改善!

更新II - 不确定我怎么错过了这一点,但OP已经尝试过git gcgit clean。我发誓最初没有那里,但我在编辑中没有看到任何更改。对此感到抱歉!


5
我也不明白为什么会有负评,这真的很有用。在我的一个仓库中,运行 git log 的时间从15秒降到了0,全靠 git gc。请注意,我没有改变原文的意思。 - GreenRaccoon23
@NicolasC 啊!不确定我怎么会错过那个,但我也会为此降低我的回答评分。 :-/ - Jabari
1
git cg 很好,git clean 可能会删除一些不需要的文件? - Luca Reghellin
1
警告:避免使用 git clean,它会删除所有未受版本控制的文件!这是非常 糟糕的,因为这些文件是无法恢复的。 - Contango
git gc --aggressive 更好 - crypdick

26

如果你的 Git 仓库大量使用子模块,可以通过编辑 .git 目录下的配置文件并在任何特别大/重的子模块上设置 ignore = dirty 来显著提高 git status 的性能。例如:

[submodule "mysubmodule"]
url = ssh://mysubmoduleURL
ignore = dirty

你将失去一个方便的提醒,即在任何你可能会忘记的子模块中存在未暂存的更改,但你仍将保留知道子模块何时与主存储库不同步的主要便利性。此外,你仍然可以将你的工作目录更改为子模块本身,并像往常一样使用git status命令来查看更多信息。有关“脏”的含义的更多详细信息,请参见此问题


我将boost作为子模块添加到了某个C++项目中,你的回答正是我想要的。谢谢! 这个配置设置有没有办法在其他机器上的所有仓库中传播到该项目?似乎仅仅推送不会做到这一点。 - ThreeStarProgrammer57

11

Git 2.13(2017年第二季度Q2)将改进git status的性能。

请参阅提交950a234(由Jeff Hostetler(jeffhostetler于2017年4月14日提交)。
(由Junio C Hamano -- gitster --合并于提交8b6bba6,于2017年4月24日)

> string-list: 在重新分配string_list时使用ALLOC_GROW

请使用ALLOC_GROW()宏来重新分配string_list数组,而不是简单地增加32。
这是一种性能优化。

在对非常大的存储库进行状态检查时,如果有许多更改,则总运行时间的相当大比例将花费在重新分配wt_status.changes数组上。

此更改将我的非常大的存储库中wt_status_collect_changes_worktree()的时间从125秒减少到45秒。


此外,Git 2.17(2018年第二季度)将引入一种新的跟踪方式,用于测量索引密集操作中时间的消耗。
请参见提交 ca54d9b(2018年1月27日),由Nguyễn Thái Ngọc Duy(pclouds撰写。
(由Junio C Hamano -- gitster --合并到提交 090dbea,2018年2月15日)

trace:测量索引密集操作中时间的消耗

All the known heavy code blocks are measured (except object database access). This should help identify if an optimization is effective or not.
An unoptimized git-status would give something like below:

0.001791141 s: read cache ...
0.004011363 s: preload index
0.000516161 s: refresh index
0.003139257 s: git command: ... 'status' '--porcelain=2'
0.006788129 s: diff-files
0.002090267 s: diff-index
0.001885735 s: initialize name hash
0.032013138 s: read directory
0.051781209 s: git command: './git' 'status'

同样的 Git 2.17(2018年第二季度)通过以下方式改进了git status

revision.c:减少对象数据库查询

mark_parents_uninteresting()中,我们检查对象文件的存在以确定是否应将提交视为已分析。结果是设置提交的“parsed”位。

修改条件,只有当结果更改已解析位时,才检查has_object_file()

当本地分支与上游引用不同时,“git status”将计算ahead/behind数量。 这使用paint_down_to_common()并命中mark_parents_uninteresting()

对于Linux存储库的副本,其本地“master”实例比远程分支“origin/master”落后约60,000次提交,“git status”的性能从1.42秒降至1.32秒,相对差异为-7.0%。


Git 2.24 (Q3 2019) 提供了另一个设置来提高 git status 的性能:

请看 提交 aaf633c, 提交 c6cc4c5, 提交 ad0fb65, 提交 31b1de6, 提交 b068d9a, 提交 7211b9e (2019年8月13日) 作者为 Derrick Stolee (derrickstolee)
(由Junio C Hamano -- gitster --提交 f4f8dfe合并,2019年9月9日)

repo-settings: 创建 feature.manyFiles 设置

feature.manyFiles 设置适用于工作目录中有许多文件的仓库。
通过设置 index.version=4core.untrackedCache=true,例如 'git status' 命令应该会得到改善。

但是:

从 Git 2.24(2019年第四季度)开始,读取 index.version 配置的代码路径在最近的更新中被破坏了,这已经得到了修正。

请看提交 c11e996 (2019年10月23日),由Derrick Stolee (derrickstolee)提交。
(在提交 4d6fb2b中由Junio C Hamano -- gitster --合并,于2019年10月24日)

repo-settings:读取索引版本的整数

由Derrick Stolee签署

多个配置选项被合并到ds/feature-macros中的repo_settings结构中,包括“index.version”配置设置的移动7211b9e(“repo-settings:整合一些配置设置”,2019-08-13,Git v2.24.0-rc1 -- 合并批次#0中列出)。不幸的是,那个文件看起来像是很多样板代码和明显的复制粘贴过载因素,配置设置使用repo_config_ge_bool()进行解析,而不是repo_config_get_int()。这意味着设置“index.version=4”将无法正确注册,并会恢复为默认版本3。我在将v2.24.0-rc0合并到VFS for Git代码库中时发现了这个问题,我们非常关心索引是否在版本4中。由于在t1600-index.sh中放置的版本检查没有足够测试“基本”场景,所以代码库没有发现这个问题。在这里,我们修改测试以包括这些普通设置,以免被features.manyFilesGIT_INDEX_VERSION覆盖。虽然“默认”版本是3,但当不必要时,它在do_write_index()中降级为版本2。

git status 命令也会比以前更快地比较 SHA1,这得益于 Git 2.33(2021 年第三季度)中使用了一个优化的哈希文件 API,用于编写索引文件的代码路径。

请查看提交 f6e2cd0, 提交 410334e, 提交 2ca245f (2021年5月18日),以及提交 68142e1 (2021年5月17日) 的内容,作者为Derrick Stolee (derrickstolee)
(由Junio C Hamano -- gitster --提交 0dd2fd1合并,日期为2021年6月14日)

csum-file.h: 增加哈希文件缓冲区大小

Signed-off-by: Derrick Stolee

hashfile API使用8KB的硬编码缓冲区,自c38138c(“git-pack-objects: write the pack files with a SHA1 csum”,2005-06-26,Git v0.99 -- merge)引入以来一直如此。它执行类似于read-cache.c中的哈希缓冲区的功能,但该代码已从8KB更新为128KB,更新发生在f279894(“read-cache: make the index write buffer size 128K”,2021-02-18,Git v2.31.0-rc1 -- merge)。那里的理由是do_write_index()从1.02秒提高到0.72秒。由于我们的最终目标是使索引编写代码使用hashfile API,因此我们需要统一此缓冲区大小以避免性能退化。 由于这些缓冲区现在位于堆上,我们可以根据消费者的需求调整其大小。特别是,调用者调用hashfd_throughput()时期望在缓冲区刷新时报告进度指标。这些调用者更喜欢较小的8k缓冲区,以避免更新之间的大延迟,尤其是对于网络较慢的用户。当不使用进度指示器时,较大的缓冲区更可取。 通过在chunk-format API中添加一个新的trace2区域,我们可以看到“git multi-pack-index writeman的写入部分在Linux机器上从约1.49秒降至约1.47秒。这些效果在其他文件系统上可能更加显著或减弱。

请参考 https://dev59.com/THA75IYBdhLWcg3waIMJ#43667992 和新的 index.threads 配置设置。 - VonC
GIT_TRACE=true git log这是如何运行跟踪并找到瓶颈的方法。 - dhavale
1
@dhavale 实际上,自Git .22以来,您还可以使用trace2:https://dev59.com/am025IYBdhLWcg3wKSiP#56094711 - VonC

7

git config --global core.preloadIndex true

这个命令对我很有帮助。你可以在这里查看官方文档。


你使用的 Git 版本是什么? - VonC
2.7.4。我使用Linux子系统来安装Windows,即使更新了apt-get,似乎还是引用了相当老的软件包。 - klimat
1
好的,明白了。我认为在更新的版本中不需要它。 - VonC
这甚至帮助我处理了git版本2.17.1。 - Markus Zeller
这已经默认启用了。 - Timmmm

6

我们的代码库中有大约20-30个子模块,
git status --ignore-submodules
极大地加快了我的速度。请注意,这不会报告子模块的状态


1
为所有未来的git状态设置:git config diff.ignoreSubmodules dirty - d2207197

5
从Git 2.40(2023年第一季度)开始,当"git status"需要长时间枚举未跟踪的路径时,建议信息已经更新。它更好地说明了您可以应用的所有配置设置,以获得更快的git status。请参阅提交ecbc23e(由Rudy Rigot (rudyrigot)于2022年11月30日进行)。(由< a href="https://github.com/gitster" rel="nofollow noreferrer">Junio C Hamano -- gitster --于2022年12月19日在提交f3d9bc8中合并)

git status(手册) 在存在大量未跟踪的文件和目录时,可能会变得很慢,因为 Git 必须搜索整个工作树以枚举它们。
当速度过慢时,Git 会打印出建议,其中包括经过的搜索时间和使用 -uno 选项禁用搜索的建议。
这个建议还带有一个警告,可能会吓到一些用户。

然而,现在 -uno 不是唯一的选择了。
当启用了 core.untrackedCachecore.fsmonitor 功能时,Git 可以通过缓存来自先前 git status 调用的结果,从而减少枚举未跟踪文件所需的时间。

更新 git status 手册,以解释这些配置选项,并更新建议以提供更多关于当前配置的详细信息,并引用已更新的文档。

git status 现在在其手册页面中包括以下内容:

未跟踪的文件和性能
如果/当需要搜索未跟踪的文件和目录时,git status在大型工作树中可能非常慢。
有许多配置选项可用于通过避免工作或利用先前Git命令的缓存结果来加速此过程。
没有适合所有人的单一最佳设置。
我们将列出相关选项的摘要以帮助您,但在列出列表之前,您可能希望重新运行git status,因为您的配置可能已经缓存了git status结果,因此后续运行可能会更快。
  • --untracked-files=no标志或status.showUntrackedfiles=false配置(两者均见上文):表示git status不应报告未跟踪的文件。这是最快的选项。
    git status不会列出未跟踪的文件,因此您需要小心记住是否创建了任何新文件并手动git add它们。

  • advice.statusUoption=false(请参见git config):将此变量设置为false可禁用枚举未跟踪文件时超过2秒时给出的警告消息。在大型项目中,可能需要更长时间,并且用户可能已经接受了折衷方案(例如,对于用户来说,使用“-uno”可能不是可接受的选项),在这种情况下,没有发出警告消息是没有意义的,因此,在这种情况下,禁用警告可能是最好的。

  • core.untrackedCache=true(请参见git update-index):启用未跟踪的缓存功能,并仅搜索自上一个git status命令以来已被修改的目录。
    Git记住每个目录中的未跟踪文件集,并假定如果目录未被修改,则其中的未跟踪文件集未发生更改。

    这比枚举每个目录的内容要快得多,但仍然不是没有成本的,因为Git仍然必须搜索一组已修改的目录。未跟踪的缓存存储在.git/index文件中。搜索未跟踪文件的减少成本略微抵消了索引的增加大小和保持其最新状态的成本。通常,这种减少搜索时间的效果是值得额外大小的。

  • core.untrackedCache=truecore.fsmonitor=truecore.fsmonitor=<hook_command_pathname>(请参见git update-index):同时启用未跟踪的缓存和FSMonitor功能,并仅搜索自上一个git status命令以来已被修改的目录。
    这比仅使用未跟踪的缓存要快,因为Git还可以避免搜索已修改的目录。
    Git只需要枚


5

还没有提到的是,在Windows机器上激活文件系统缓存(Linux文件系统完全不同,Git已经针对它们进行了优化,因此这可能只对Windows有帮助)。

git config core.fscache true

作为最后的手段,如果git仍然很慢,可以关闭修改时间检查,这是git需要找出哪些文件已更改的操作。
git config core.ignoreStat true

但是:更改的文件必须由开发人员自己使用git add添加。Git本身无法发现更改。 来源

1
这对我在Windows 10上非常有帮助,尽管我已经安装了最新版本的Git for Windows。谢谢。我的repo在.git文件夹(git lfs)中有大约100 GB的大小。 - Alex Sorokoletov

2

好的,如果我没有亲眼看到这个问题,我很难相信...我的全新工作笔记本电脑性能非常差,即使是最简单的代码库,git status命令也需要5到10秒才能完成。我尝试了这个主题中的所有建议,然后注意到git log也很慢,因此我开始搜索与git安装有关的通用缓慢问题,并找到了https://github.com/gitextensions/gitextensions/issues/5314#issuecomment-416081823

在绝望的情况下,我尝试更新笔记本电脑的显卡驱动程序...

天啊...这真的奏效了!

对我也一样!

所以显卡驱动程序似乎与此有关...很难理解为什么,但现在表现符合预期!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接