Git difftool ridículamente lento en Cygwin / MinGW

Noté que git difftool es muy lento. Aparece un retraso de aproximadamente 1..2 segundos entre cada invocación de diferencia.

Para compararlo, he escrito un command difftool personalizado:

 #!/bin/sh echo $0 $1 $2 

Y configuré Git para usar esta herramienta en mi ~/.gitconfig

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

Lo probé en las fonts de 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 

Cuando git difftool un git difftool con 259b5e6d33 , el resultado es ridículamente lento:

 $ 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 

Al intentar una secuencia de commands más simple, va mucho más rápido:

 $ 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 

¿Qué me perdí?

Aquí los resultados que obtuve

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

Para los resultados de Cygwin , medí 2.8s gastados en git-difftool y 7.5s gastados en git-difftool--helper . El último tiene 98 líneas de largo. No entiendo por qué es tan lento.

Utilizando algunas de las técnicas que se encuentran en el msysgit GitHub , he networkingucido un poco esto.

Para cada file en el diff , git-difftool--helper vuelve a ejecutar los siguientes commands internos:

 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' 

Tenga en count que, en este caso particular, tomó aproximadamente 4.5 segundos ejecutarlos. Este es un patrón bastante consistente en mi logging.

Tenga en count también que algunos de estos son duplicates – git config difftool.bc.cmd se llama 4 veces!

Ahora, posibles remedios:

  • Corté el time de ejecución para estos commands a la mitad moviendo todas las secciones relacionadas con el diff a la parte superior de mi file .gitconfig . Seriamente. Todavía es notable, pero ahora del order de 2 segundos en lugar de 4.5.
  • Asegúrese de que su carpeta Git en Archivos de progtwig y su perfil de usuario (donde .gitconfig vive) estén ambos excluidos del análisis de virus en time real.
  • Fundamentalmente, Git necesita ser más eficiente al analizar y get valores de configuration. Idealmente, los almacenaría en caching en lugar de volver a solicitarlos (y reparsing …) desde config cada vez en un bucle. Tal vez incluso en caching para la ejecución del command completo.

git difftool debe ser un poco más rápido con Git 2.13 (Q2 2017)
Ver commit d12a8cf (14 abr 2017) por Jeff Hostetler ( jeffhostetler ) .
(Fusionado por Junio ​​C Hamano – gitster – in commit 8868ba1 , 24 de abril de 2017)

unpack-trees : evite búsquedas duplicadas de ODB durante el pago

(ODB: Object DataBase)

Enseñe a traverse_trees_recursive() a no hacer búsquedas networkingundantes de ODB cuando ambos directorys se refieren al mismo OID.

En operaciones como read-tree y checkout , es probable que existan muchos directorys pares que tengan el mismo OID cuando las diferencias entre los commit sean relativamente pequeñas.
En estos casos, podemos evitar golpear el ODB varias veces para el mismo OID.

Este parche maneja n = 2 y n = 3 casos y simplemente copy los datos en lugar de repetir el fill_tree_descriptor ().

 ================ 

En el repository de Windows (500K treees, files 3.1M, índice de 450MB), esto networkingujo el time total en 0.75 segundos cuando se alterna entre 2 confirmaciones con una sola diferencia de file.

 (avg) before: 22.699 (avg) after: 21.955 =============== 

Después de algunas investigaciones, tengo evidencia de que el mal performance tuvo que ver con files propiedad de un usuario de un dominio diferente. Específicamente, llegué a las siguientes conclusiones:

  • Estoy trabajando en un entorno corporativo con varios dominios y miles de usuarios.
  • Debido a cambios organizativos, cada usuario, probablemente solo durante una fase de transición, se mantiene en dos dominios, su dominio principal y otro dominio. Al cambiar la propiedad del object a través de la GUI de Windows, cada usuario aparece dos veces y uno debe ir a la selección de usuario extendida para identificar el asignado a un dominio específico.
  • cygwin con acl habilitado muestra el usuario del file "otro dominio" como "<dominio> + <nombre de usuario>". El dominio principal es solo "<nombre de usuario>". Cygwin sin acl muestra solo "<nombre de usuario>" en ambos casos. Eso puede ser bastante confuso porque el permiso de file y la propiedad, tal como lo reconoce cygwin, indicaría permiso de escritura, mientras que el usuario, de hecho, no tiene eso.
  • Los files que pertenecen al dominio de "otro dominio" son escriturables por mi yo "este dominio", por lo que la asignación de dominio es en gran medida transparente.
  • Un gran tree fuente de nuestro sistema de control de versiones (que también se reflejó en un repository git) tenía miles de files propiedad del "otro dominio". Eso pareció causar las lentas operaciones de file. Cambiar la propiedad al "dominio principal" solucionó el problema de velocidad, tanto para git como para otro acceso a files.

Debo asumir que la obtención de permissions de files para usuarios en otros dominios es lenta y, por alguna razón, no almacenada en la memory caching (siempre fue el mismo usuario).

El rest del artículo a continuación es lo que originalmente publiqué. Lo dejo en pie.


Para mí (trabajando en una gran compañía con múltiples dominios de Windows distribuidos geográficamente), el culpable es que cygwin usa Windows acl por defecto. Considere esta request para todos los usuarios conocidos en el dominio:

 $ time (mkpasswd -D | wc -l) 45183 real 27m55,340s user 0m1,637s sys 0m0,123s 

El arreglo (1) (2) era una simple cuestión de montar los filesystems NTFS con noacl , es decir, mi /etc/fstab contiene la línea

 none / cygdrive binary,posix=0,user,noacl 0 0 

(al mismo time, elimina el molesto prefijo cygdrive ).

No puedo evitar imaginar que cygwin / msys (el mismo comportamiento allí, excepto que la installation de Windows git monta noacl de forma pnetworkingeterminada, probablemente por este motivo) realiza una consulta de server de dominio para cada file que toca y no almacena en caching los resultados.

El cambio se introdujo en algún momento alnetworkingedor de 2015 con cygwin 2.4 o 2.5. De las notas de la versión para 2.4:

Para dar cabida a las ACL estándar de Windows, los permissions POSIX del propietario y de todos los demás usuarios de la ACL se calculan utilizando la API de Windows AuthZ. Esto puede ralentizar el cálculo de los permissions POSIX notoriamente en algunas […] circunstancias (énfasis mío).

La opción noacl networkingujo el time para ejecutar BeyondCompare (o repetir una cadena, para el caso) de 25 segundos a 1. Es completamente ininteligible por qué un simple git diff en el mismo file es muy rápido incluso con acl ya que asumiría ingenuamente que la información requerida y, por lo tanto, las acciones FS requeridas son idénticas.

Voy a ver el cygserver ahora, que puede mejorar las cosas mediante el almacenamiento en caching.

Actualización: cygserver no mejora la situación, desafortunadamente.


(1) La solución para git. mkpasswd no se ve afectado.

(2) No he entendido y probado el impacto en los permissions de files y la propiedad con respecto a git (y las vistas de ClearCase a las que también accedemos a través de cygwin). Mi intuición es que uno quiere mantenerse fiel a la semántica de Windows lo más cerca posible (lo que significa que noacl puede tener problemas).

(3) La documentation de cygwin discute escenarios en los que los resultados de la consulta no están en la memory caching. Uno consiste en una secuencia de processs cygwin que no se generan a partir de un ancestro cygwin común (como un bash) sino de un progtwig de Windows como cmd . Debo asumir que Windows proporciona un mecanismo de almacenamiento en caching para los progtwigs nativos, o que un sistema de Windows sería inutilizable en este entorno corporativo. Por alguna razón, cygwin no lo usa.