Git difftool在Cygwin/MinGW中速度极慢

8
我注意到git difftool非常慢。每次调用之间会出现约1..2秒的延迟。
为了对其进行基准测试,我编写了一个自定义的difftool命令:
#!/bin/sh
echo $0 $1 $2

我已经配置了 Git 使用这个工具在我的 ~/.gitconfig 文件中。

[diff]
    tool = mydiff
[difftool "mydiff"]
    prompt = false
    cmd = "~/mydiff \"$LOCAL\" \"$REMOTE\""

我在Git源代码上进行了测试:

$ git clone https://github.com/git/git.git
$ cd git
$ git rev-parse HEAD
1bc8feaa7cc752fe3b902ccf83ae9332e40921db
$ git diff head~10 --stat --name-only | wc -l
23

当我使用259b5e6d33进行git difftool时,结果非常缓慢:

$ time git difftool 259b5
mydiff /dev/null Documentation/RelNotes/2.6.3.txt
...
mydiff /tmp/mY2T6l_upload-pack.c upload-pack.c

real    0m10.381s
user    0m1.997s
sys     0m6.667s

通过尝试一个更简单的脚本,速度会更快:
$ time git diff --name-only --stat 259b5 | xargs -n1 -I{} sh -c 'git show 259b5:{} > {}.tmp && ~/mydiff {} {}.tmp'
mydiff Documentation/RelNotes/2.6.3.txt Documentation/RelNotes/2.6.3.txt.tmp
mydiff upload-pack.c upload-pack.c.tmp

real    0m1.149s
user    0m0.472s
sys     0m0.821s

我错过了什么?

这是我得到的结果

| Cygwin | Debian | Ubuntu | Method   |
| ------ | ------ | ------ | -------- |
| 10.381 |  2.620 | 0.580  | difftool |
|  1.149 |  0.567 | 0.210  | custom   |

对于 Cygwin 的结果,我测量了在 git-difftool 中花费了 2.8 秒,在 git-difftool--helper 中花费了 7.5 秒。后者有 98 行代码。我不明白为什么速度会这么慢。


2
嗨,我面临着同样的问题。你是否得到过关于为什么git difftool如此缓慢的答案? - user1342582
我还没有找到任何解决方案。 - nowox
嗨。我想你可能会感兴趣,这个问题已经在Git 2.8.1 for Windows中得到了解决。请参见https://github.com/git-for-windows/git/issues/711。 - user1342582
1
我在mingw64和Git 2.8.1中看到了完全相同的行为。此外,@jeyoung提供的链接是关于vim集成的问题。不同的问题,可能? - GalacticCowboy
3个回答

2
使用在msysgit GitHub上找到的一些技术,我已经缩小了范围。
对于差异中的每个文件,`git-difftool--helper` 重新运行以下内部命令:
12:44:46.941239 git.c:351               trace: built-in: git 'config' 'diff.tool'
12:44:47.359239 git.c:351               trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:47.933239 git.c:351               trace: built-in: git 'config' '--bool' 'mergetool.prompt'
12:44:48.797239 git.c:351               trace: built-in: git 'config' '--bool' 'difftool.prompt'
12:44:49.696239 git.c:351               trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:50.135239 git.c:351               trace: built-in: git 'config' 'difftool.bc.path'
12:44:50.422239 git.c:351               trace: built-in: git 'config' 'mergetool.bc.path'
12:44:51.060239 git.c:351               trace: built-in: git 'config' 'difftool.bc.cmd'
12:44:51.452239 git.c:351               trace: built-in: git 'config' 'difftool.bc.cmd'

请注意,在这种特定情况下,执行这些命令大约需要4.5秒钟。这是我日志中一个相当一致的模式。
还要注意,其中一些是重复的 - git config difftool.bc.cmd被调用了4次!
现在,可能的解决方案:
  • 通过将所有与差异相关的部分移动到我的.gitconfig文件的顶部,我将这些命令的执行时间缩短了一半。说真的。它仍然很明显,但现在只有2秒左右,而不是4.5秒。
  • 确保你的Program Files下的Git文件夹和你的用户配置文件(包括.gitconfig)都被排除在实时病毒扫描之外。
  • 从根本上讲,Git需要更有效地解析和获取配置值。理想情况下,它应该缓存这些值,而不是每次在循环中重新请求(和重新解析...)配置。甚至可以为整个命令执行缓存。

1

Git difftool在Git 2.13(Q2 2017)版中会稍微快一些。

请参见提交d12a8cf(由Jeff Hostetler (jeffhostetler)于2017年4月14日提交)。

(由Junio C Hamano -- gitster --合并于提交8868ba1,2017年4月24日)

unpack-trees:避免在检出期间进行重复的ODB查找

(ODB:对象数据库)

traverse_trees_recursive()在两个目录引用相同OID时不执行冗余ODB查找。
在像read-treecheckout这样的操作中,当提交之间的差异相对较小时,可能会有许多具有相同OID的对等目录。 在这些情况下,我们可以避免为相同的OID多次命中ODB。
此补丁处理n = 2和n = 3的情况,并简单地复制数据而不是重复填充fill_tree_descriptor()
================

在Windows代码库(500K个树,3.1M个文件,450MB的索引)中,当在两个提交之间循环且只有一个文件差异时,这将减少总时间0.75秒。
(avg) before: 22.699
(avg) after:  21.955
===============

1
经过调查,我有证据表明性能不佳与归属于不同域的用户拥有的文件有关。具体而言,我得出了以下结论:
  • 我在一个企业环境中工作,有几个域和数千个用户。
  • 由于组织变化,每个用户可能只在过渡阶段内被保留在两个域中,即他或她的主要域和第二个域。通过Windows GUI更改对象所有权时,每个用户会出现两次,必须进入扩展用户选择来识别分配给特定域的用户。
  • 启用acl的cygwin显示“其他域”文件用户为“<domain>+<username>”。主域本身只是“<username>”。没有acl的Cygwin在两种情况下都只显示“<username>”,这可能相当令人困惑,因为像cygwin认可的文件权限和所有权将指示写入权限,而事实上用户并没有这样的权限。
  • 属于“其他域”自身的文件可由“此域”自身编写,因此域分配在很大程度上是透明的。
  • 我们版本控制系统中的一个大型源代码树(也在git repo中镜像)拥有数千个文件,这些文件由“其他域自身”拥有。这似乎导致了缓慢的文件操作。将所有权更改为“主域自身”可以解决速度问题,对于git和其他文件访问都是如此。

我必须假设在其他域中为用户获取文件权限很慢,并且由于某种原因没有缓存(始终是同一用户)。

下面的文章是我最初发布的内容。我让它保持不变。


对于我来说(在一个拥有多个地理分布的Windows域的大公司中工作),罪魁祸首是cygwin默认使用Windows acl。考虑此请求适用于域中所有已知用户:

$ time (mkpasswd -D | wc -l)
45183

real    27m55,340s
user    0m1,637s
sys     0m0,123s

修复(1)(2)很简单,只需使用noacl来挂载NTFS文件系统,即我的/etc/fstab文件中包含以下行。
none / cygdrive binary,posix=0,user,noacl 0 0

同时消除了令人讨厌的cygdrive前缀。

我不禁想象,cygwin/msys(在那里表现相同,只是Windows git安装默认挂载noacl,可能是出于这个原因)为它接触到的每个文件执行一个域服务器查询,并且不缓存结果。

该更改是在2015年左右引入的,随着cygwin 2.4或2.5。来自2.4版本发布说明:的:

为了适应标准的Windows ACLs,使用Windows AuthZ API计算所有用户的POSIX权限的所有者和ACL中的其他用户的POSIX权限。这可能会在某些情况下明显减慢计算POSIX权限的速度[...](由我强调)。

< p > noacl 选项将启动 BeyondCompare(或回显字符串)的时间从25秒降至1秒。令人费解的是,即使使用 acl,在同一文件上进行简单的 git diff 也非常快,因为我天真地认为所需的信息和因此所需的FS操作是相同的。

现在我会检查 cygserver ,这可能通过缓存来改善事情。

更新:不幸的是,cygserver并没有改善情况。


(1) 修复了git的问题。mkpasswd没有受到影响。

(2) 我还没有理解和测试有关git(以及我们也通过cygwin访问的ClearCase视图)的文件权限和所有权方面的影响。我的直觉是尽可能接近Windows语义(这意味着noacl可能会遇到问题)。

(3) cygwin文档讨论了查询结果未被缓存的情况。其中之一是一系列不是从一个常见的cygwin祖先(如bash)中生成而是从Windows程序(如cmd)中生成的cygwin进程序列。我必须假设Windows为本地程序提供了缓存机制,否则Windows系统在这个企业环境中将无法使用。由于某种原因,cygwin并未使用它。


很好的反馈,比我的回答更精确。+1 - VonC

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