【问题标题】:Ways to improve git status performance提高 git status 性能的方法
【发布时间】:2011-06-27 01:22:25
【问题描述】:

我在 NFS 上的 Linux 机器上有一个 10 GB 的存储库。第一次git status 需要 36 分钟,后续git status 需要 8 分钟。似乎 Git 依赖于操作系统来缓存文件。只有第一个 git 命令,如 commitstatus,涉及打包/重新打包整个 repo 需要很长时间才能获得巨大的 repo。我不确定你是否在这么大的 repo 上使用过git status,但是有人遇到过这个问题吗?

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

子模块或任何其他概念(如将 repo 分解成更小的部分)会有帮助吗?如果是这样,哪个是拆分更大回购的最佳选择。有没有其他方法可以缩短大型 repo 上 git 命令所花费的时间?

【问题讨论】:

标签: performance git nfs


【解决方案1】:

更准确地说,git 取决于lstat(2) 系统调用的效率,因此调整客户端的“attribute cache timeout” 可能会奏效。

git-update-index 的手册(本质上是git-status 的手动模式)描述了您可以采取哪些措施来缓解这种情况,通过using the --assume-unchanged flag 抑制其正常行为并手动更新您已更改的路径。您甚至可以对编辑器进行编程以在每次保存文件时取消设置此标志。

正如您所建议的,另一种选择是减小结帐的大小(包文件的大小在这里并没有真正发挥作用)。选项包括稀疏结帐、子模块或 Google 的 repo 工具。

(有一个邮件列表thread about using Git with NFS,但它没有回答很多问题。)

【讨论】:

  • 你错过的事情:Linus 的补丁确实被合并了,它可以通过将 core.preloadindex 设置为 true 来启用 - 请参阅 git-config 文档以获得更多描述。 (我的工作场所使用 NFS,我也遇到过这个问题 - 但从未注意到 preloadindex 设置。感谢您指出正确的方法!)
  • 'git config core.preloadindex true' 应添加到此处接受的答案中。可能带有来自 user1077329 的 -uno 标志
  • core.preloadindex 从 Git 2.1.0 开始默认设置为 true:git.kernel.org/pub/scm/git/git.git/tree/Documentation/RelNotes/…
【解决方案2】:

我也在一个通过 NFS 共享的大型项目中看到了这个问题。

我花了一些时间才发现可以同时赋予 git commit 和 git status 的标志 -uno

此标志的作用是禁止查找未跟踪的文件。这显着减少了 nfs 操作的数量。原因是为了让 git 发现未跟踪的文件,它必须查看所有子目录,所以如果你有很多子目录,这会伤害你。通过禁止 git 查找未跟踪的文件,您可以消除所有这些 NFS 操作。

将此与 core.preloadindex 标志结合使用,即使在 NFS 上也可以获得合理的性能。

【讨论】:

  • 正如git-status(1) 中提到的,可以通过设置status.showUntrackedFiles 配置将其设置为默认值。
【解决方案3】:

如果您的 git repo 大量使用子模块,您可以通过编辑 .git 目录中的配置文件并在任何特别大/重的子模块上设置 ignore = dirty 来大大加快 git status 的性能。例如:

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

您将失去提醒您可能忘记的任何子模块中存在未分阶段更改的便利,但您仍将保留了解子模块何时与主模块不同步的主要便利回购。另外,您仍然可以将工作目录更改为子模块本身,并像往常一样在其中使用 git status 以查看更多信息。有关“脏”含义的更多详细信息,请参阅this question

【讨论】:

    【解决方案4】:

    试试git gc。另外,git clean可能提供帮助。

    更新 - 不知道否决票来自哪里,但 git 手册明确指出:

    在当前存储库中运行许多内务处理任务,例如压缩文件修订(以减少磁盘空间和提高性能)和删除可能已被删除的无法访问的对象从之前的 git add 调用创建。

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

    当 git status 很慢时,我在运行 git gc 后总是注意到不同之处!

    更新 II - 不知道我是怎么错过的,但 OP 已经尝试过 git gcgit clean。我发誓最初并不存在,但我没有看到编辑有任何变化。对不起!

    【讨论】:

    • 我也不明白反对票;这真的很有帮助。 git gc 减少了 git log 在我的一个存储库上运行的时间从 15 秒到 0。
    • @NicolasC 啊!不知道我是怎么错过的,但我也会否决我的答案。 :-/
    • git cg 很好,git clean 可能会删除一些不需要的文件?
    【解决方案5】:

    git status 的性能应该会在 Git 2.13(2017 年第 2 季度)中得到改善。

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

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

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

    在一个非常大的回购状态期间,有很多变化, 总运行时间的很大一部分用于重新分配wt_status.changes array

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


    此外,Git 2.17(2018 年第 2 季度)将引入新的跟踪,用于衡量在索引繁重的操作中花费的时间。

    参见commit ca54d9bcommit ca54d9b(2018 年 1 月 27 日)。
    (由 Junio C Hamano -- gitster -- 合并于 commit 090dbea,2018 年 2 月 15 日)

    trace:衡量索引繁重的操作花费的时间

    测量所有已知的重代码块(对象数据库除外 使用权)。这应该有助于确定优化是否有效或 不是。
    未优化的 git-status 会给出如下内容:

    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() 中,我们检查是否存在 目标文件,看看我们是否应该将提交视为已解析。结果 是在提交上设置“已解析”位。

    修改条件只检查has_object_file()如果结果 会改变解析的位。

    当本地分支与其上游引用不同时,“git status” 将计算提前/落后计数。
    这使用paint_down_to_common() 并点击mark_parents_uninteresting()

    在远程分支“origin/master”后面的本地实例“master”的 Linux 存储库副本上,大约 60,000 次提交,我们发现 “git status”从 1.42 秒变为 1.32 秒,对于一个亲戚 差异为 -7.0%。


    Git 2.24(2019 年第三季度)提出了另一种设置来提高 git status 的性能:

    参见commit aaf633ccommit c6cc4c5commit ad0fb65commit 31b1de6commit b068d9acommit 7211b9e(2019 年 8 月 13 日)Derrick Stolee (derrickstolee)
    (由Junio C Hamano -- gitster -- 合并commit f4f8dfe,2019 年 9 月 9 日)

    repo-settings:创建 feature.manyFiles 设置

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

    但是:

    在 Git 2.24(2019 年第四季度)中,读取 index.version 配置的代码路径因最近的更新而被破坏,该更新已得到纠正。

    参见Derrick Stolee (derrickstolee)commit c11e996(2019 年 10 月 23 日)。
    (由 Junio C Hamano -- gitster -- 合并到 commit 4d6fb2b,2019 年 10 月 24 日)

    repo-settings: 读取 index.version 的 int

    签字人:Derrick Stolee

    多个配置选项被合并到 ds/feature-macros 中的 repo_settings 结构中,包括移动 7211b9e 中的“index.version”配置设置(“repo-settings:整合一些配置设置”,2019 -08-13,Git v2.24.0-rc1 -- mergebatch #0 中列出)。

    不幸的是,该文件看起来像很多样板文件,显然是复制粘贴过载的一个因素,配置设置被解析为 repo_config_ge_bool() 而不是 repo_config_get_int()。这意味着设置“index.version=4”将无法正确注册,并将恢复为默认版本 3。

    我在将 v2.24.0-rc0 合并到 Git 代码库的 VFS 时发现了这一点,我们非常关心索引是在版本 4 中。

    这没有被代码库捕获,因为放在t1600-index.sh 中的版本检查没有足够测试“基本”场景。在这里,我们修改测试以包含这些正常设置,以免被features.manyFilesGIT_INDEX_VERSION 覆盖。
    虽然“默认”版本是 3,但在不需要时会在 do_write_index() 中降级为版本 2。


    由于 Git 2.33(2021 年第三季度),git status 也将更快地比较 SHA1,在写入索引文件的代码路径中使用优化的哈希文件 API。

    参见Derrick Stolee (derrickstolee)commit f6e2cd0commit 410334ecommit 2ca245f(2021 年 5 月 18 日)和 commit 68142e1(2021 年 5 月 17 日)。
    (由@987654364 中的Junio C Hamano -- gitster -- 合并@,2021 年 6 月 14 日)

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

    签字人: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 中的哈希缓冲区类似的功能,但该代码在 f279894 中从 8KB 更新为 128KB(“read-cache:使索引写入缓冲区大小为 128K”,2021-02-18,Git v2 .31.0-rc1 -- merge)。
    理由是 do_write_index() 从 1.02 秒提高到 0.72 秒。
    由于我们的最终目标是让索引编写代码使用 hashfile API,我们需要统一这个缓冲区大小以避免性能下降。

    由于这些缓冲区现在位于堆上,我们可以根据消费者的需要调整它们的大小。
    特别是,hashfd_throughput() 的调用者希望在缓冲区刷新时报告进度指示器。
    这些调用者更喜欢较小的 8k 缓冲区以避免更新之间的大延迟,尤其是对于网络较慢的用户。
    当不使用进度指示器时,最好使用较大的缓冲区。

    通过在块格式 API 中添加一个新的 trace2 区域,我们可以看到 'git multi-pack-index write'(man) 的写入部分从 ~1.49s 降低到 ~在 Linux 机器上为 1.47 秒。
    这些影响在其他文件系统上可能更加明显或减弱。

    【讨论】:

    【解决方案6】:

    git config --global core.preloadIndex true

    为我完成了这项工作。查看官方文档here

    【讨论】:

    • 你使用的是什么版本的 Git?
    • 2.7.4。我使用适用于 Windows 的 Linux 子系统,甚至更新 apt-get 似乎引用了相当旧的包。
    • 好吧,有道理。我认为更新的版本不需要它。
    • 这甚至帮助了我的 git 版本 2.17.1
    【解决方案7】:

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

    【讨论】:

    • 设置所有未来的 git 状态:git config diff.ignoreSubmodules dirty
    【解决方案8】:

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

    git config core.fscache true
    


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

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

    source

    【讨论】:

    • 这在 Windows 10 上帮助了我,尽管我有一个相当新的 Git for Windows 版本。谢谢你。我的仓库在 .git 文件夹(git lfs)中约为 100 Gb
    【解决方案9】:

    剩余的index.lock 文件

    当您有剩余的index.lock 文件时,git status 可能会异常缓慢。

    当您拥有git submodules 时尤其会发生这种情况,因为这样您通常不会注意到此类遗留文件。

    总结:运行find .git/ -name index.lock,并在检查剩余文件确实未被任何当前正在运行的程序使用后删除它们


    详情

    我发现我的 repo 中的 shell git status 非常慢,在 Ubuntu 16.04 上使用 git 2.19。

    挖掘并发现我的assets git 子模块中的/usr/bin/time git status 耗时1.7 秒。

    使用strace 发现 git 使用mmap 读取了我所有的大文件。 它通常不会那样做,通常stat 就足够了。

    我搜索了这个问题并找到了Use of index and Racy Git problem

    试过git update-index somefile(在我的情况下gitignore在子模块结帐中)显示here 但它失败了

    fatal: Unable to create '/home/niklas/src/myproject/.git/modules/assets/index.lock': File exists.
    
    Another git process seems to be running in this repository, e.g.
    an editor opened by 'git commit'. Please make sure all processes
    are terminated then try again. If it still fails, a git process
    may have crashed in this repository earlier:
    remove the file manually to continue.
    

    这是一个经典错误。通常你在任何 git 操作中都会注意到它,但是对于你不经常提交的子模块,你可能几个月都不会注意到它,因为它只在向索引添加内容时出现;只读 git status 不会引发警告。

    删除index.lock 文件,git status 立即变快,mmaps 消失,现在快了 1000 倍以上。

    因此,如果您的 git 状态异常缓慢,请检查 find .git/ -name index.lock 并删除剩余部分。

    【讨论】:

      【解决方案10】:

      这是一个相当古老的问题。不过,鉴于存储库的大小,没有人评论二进制文件,这让我感到惊讶。

      您提到您的 git 存储库约为 10GB。似乎除了 NFS 问题和其他 git 问题(可通过git gc 解决,并且 git 配置更改为其他答案中的概述),git 命令(git status、git diff、git add)可能会因为大量二进制文件而变慢在存储库中。 git 不擅长处理二进制文件。您可以使用以下命令删除不必要的二进制文件(以 NetCDF 文件为例;之前有 git 存储库的备份):

      git filter-branch --force --index-filter \  
      'git rm --cached --ignore-unmatch *.nc' \   
      --prune-empty --tag-name-filter cat -- --all
      

      不要忘记将 '*.nc' 放入 gitignore 文件以阻止 git 重新提交文件。

      【讨论】:

      • 文件大小与git status性能无关。
      【解决方案11】:

      好的,如果我不亲眼看到,这简直难以置信……我在全新的工作笔记本电脑上表现非常糟糕,git status 需要 5 到 10 秒才能完成,即使是最长时间愚蠢的存储库。 我已经尝试了这个线程中的所有建议然后我注意到git log 也很慢所以我已经广泛搜索了 git 全新安装的一般缓慢并且我发现了这个 https://github.com/gitextensions/gitextensions/issues/5314#issuecomment-416081823

      我绝望地尝试更新笔记本电脑的图形驱动程序,然后...

      神圣的圣诞老人 sh*t... 成功了!

      ...我也是!

      显然显卡驱动程序在这里有一些关系......很难理解为什么,但现在性能“如预期”!

      【讨论】:

        猜你喜欢
        • 2022-06-11
        • 1970-01-01
        • 2011-12-19
        • 2018-11-06
        • 2019-03-20
        • 1970-01-01
        • 1970-01-01
        • 2012-04-27
        • 2014-11-20
        相关资源
        最近更新 更多