Solución de problemas de operaciones lentas de Git: Errores comunes y soluciones
Diagnostica comandos lentos de Git separando las causas de estado, clonación, fetch, push, hooks, sistema de archivos, red y tamaño del repositorio.
Solución de problemas de operaciones lentas de Git: Errores comunes y soluciones
Git lento tiene diferentes causas dependiendo de qué comando es lento. Un git status lento suele ser problema del sistema de archivos local o del índice. Un git fetch lento a menudo es problema de red, tamaño remoto o negociación. Un git checkout lento puede deberse a la cantidad de archivos, escaneo antivirus, problemas de sparse checkout o archivos generados. Un git push lento puede deberse a objetos grandes, hooks, compresión o el servidor remoto.
Por lo tanto, la primera solución no es git gc. La primera solución es medir la operación exacta.
En macOS o Linux:
time git status
time git fetch --prune
time git checkout main
En PowerShell:
Measure-Command { git status }
Ejecuta el comando dos veces. La primera ejecución puede ser más lenta porque la caché del sistema operativo está fría. Si el primer git status tarda diez segundos y el segundo uno, puede que estés viendo el comportamiento de la caché del disco. Si ambos son lentos, sigue investigando.
Git tiene trazado incorporado que puede mostrar dónde se va el tiempo:
GIT_TRACE=1 git status
GIT_TRACE_PERFORMANCE=1 git status
GIT_TRACE_PACKET=1 GIT_TRACE=1 git fetch
GIT_TRACE_PACKET es ruidoso, pero útil cuando fetch o push se cuelgan durante la negociación del protocolo. No pegues la salida del trazado con URLs de repositorios privados o tokens en tickets públicos.
Cuando git status es lento
git status verifica el índice y el árbol de trabajo. Se vuelve lento cuando el repositorio tiene una gran cantidad de archivos, el árbol de trabajo está en un sistema de archivos lento, los metadatos de archivos son costosos de leer, u otro programa escanea cada archivo que Git toca.
Empieza con lo básico:
git status --short
git config --show-origin --get core.fsmonitor
git config --show-origin --get core.untrackedCache
git config --show-origin --get core.preloadIndex
Para árboles de trabajo grandes, estas configuraciones pueden ayudar en muchos sistemas:
git config core.untrackedCache true
git config core.preloadIndex true
Usa primero la configuración local para poder probar por repositorio. Si ayuda, hazla global después.
El monitor de sistema de archivos incorporado de Git puede acelerar el estado evitando escaneos completos en plataformas y versiones de Git compatibles:
git config core.fsmonitor true
Si el estado se vuelve incorrecto o extraño después de habilitarlo, desactívalo y actualiza Git antes de intentarlo de nuevo:
git config --unset core.fsmonitor
Los archivos no rastreados pueden ser un problema oculto. Las salidas de compilación, directorios de dependencias, informes generados y registros locales generalmente deben ignorarse. Verifica qué está escaneando Git:
git status --untracked-files=all --short | head -100
Si ves node_modules/, dist/, .venv/, target/ o directorios generados similares, agrega los patrones correctos a .gitignore. No ignores archivos fuente solo para hacer el estado más rápido. Ignora archivos que realmente no deberían estar versionados.
En Windows, el escaneo antivirus en tiempo real es una razón común por la que Git se siente lento. Git lee muchos archivos pequeños dentro de .git y el árbol de trabajo, y el software de seguridad puede inspeccionar cada acceso. Si tu organización lo permite, excluye los espacios de trabajo de desarrollo de confianza del escaneo en tiempo real. No excluyas directorios donde ejecutes código no confiable.
También evita colocar repositorios activos en carpetas de sincronización en la nube como OneDrive, Dropbox o iCloud Drive. Las herramientas de sincronización pueden bloquear archivos, reescribir metadatos y competir con las propias operaciones de archivos de Git.
Cuando clonar o fetch es lento
Un clon lento puede significar un historial grande, muchos blobs grandes, un remoto lento o una ruta de red con alta latencia. Mide el tamaño del repositorio después de clonar:
git count-objects -vH
du -sh .git 2>/dev/null
Para trabajos de CI y entornos temporales, usa un clon superficial cuando no se necesite historial:
git clone --depth 1 <url>
Para una compilación de rama:
git clone --depth 1 --branch main <url>
Los clones superficiales no son ideales para todos los flujos de trabajo. Los comandos que necesitan historial, etiquetas, bases de fusión o cálculos de versión pueden fallar o producir respuestas incompletas. En CI, eso suele ser aceptable. En una máquina de desarrollador, puede ser frustrante.
El clon parcial es útil cuando se necesita el historial del repositorio pero los blobs de archivos se pueden descargar de forma perezosa:
git clone --filter=blob:none <url>
Esto funciona mejor con servidores Git modernos que soportan bien el clon parcial. Pruébalo con tu host antes de convertirlo en la recomendación oficial del equipo.
Si solo necesitas una parte de un monorepo, combina el sparse checkout con un clon normal o parcial:
git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/api shared/lib
El sparse checkout reduce el tamaño del árbol de trabajo. No hace que todas las operaciones de Git sean mágicamente baratas, pero ayuda cuando la cantidad de archivos es el problema principal.
Para fetches con muchas ramas remotas eliminadas, poda las referencias obsoletas:
git fetch --prune
Para hacerlo predeterminado:
git config --global fetch.prune true
Cuando push es lento
La velocidad de push depende de cuántos datos de objetos nuevos envíes, qué tan costoso es el empaquetado local, si se ejecutan hooks y qué tan rápido el remoto acepta el paquete.
Verifica si accidentalmente has confirmado archivos grandes:
git rev-list --objects --all | sort -k 2 | tail
Ese comando es crudo porque no muestra tamaños. Para una inspección más profunda, usa herramientas como git-sizer o comandos de análisis de git filter-repo si están disponibles. El punto práctico es simple: si un video, volcado de base de datos, archivo o artefacto de compilación entró en el historial, cada clon puede pagar por ello hasta que el historial se reescriba o el proyecto se mueva a un mejor patrón de almacenamiento.
Git LFS es la respuesta habitual para activos binarios grandes que pertenecen al proyecto pero no deben vivir como blobs normales de Git:
git lfs install
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes
Git LFS ayuda más cuando se adopta antes de que los archivos grandes entren en el historial. Migrar el historial existente es posible, pero reescribe confirmaciones y necesita coordinación del equipo.
Ten cuidado con el consejo antiguo de aumentar http.postBuffer. A menudo se sugiere para problemas de push, pero rara vez soluciona la lentitud general en Git moderno. Si los pushes fallan con errores HTTP específicos, verifica el error exacto, el proxy, los límites del servidor y la versión de Git antes de aplicar configuraciones de búfer aleatorias.
Mantenimiento del repositorio: git gc, gráficos de confirmación y reempaquetado
Git almacena objetos en packfiles. Con el tiempo, los repositorios locales pueden acumular objetos sueltos y packs ineficientes. Git ejecuta mantenimiento automáticamente en muchos flujos de trabajo, pero el mantenimiento manual aún puede ayudar a repositorios más antiguos o ocupados.
Comienza con un comando de mantenimiento seguro:
git maintenance run
O el comando más antiguo:
git gc
Evita hacer de git gc --prune=now tu primer movimiento casual. Poda inmediatamente elimina objetos inalcanzables que de otro modo podrían ser recuperables por un tiempo. Puede estar bien cuando sabes lo que haces, pero no es un botón de velocidad inofensivo.
Para repositorios con historiales grandes, los gráficos de confirmación pueden mejorar los recorridos de historial utilizados por comandos como log, merge-base y negociación de fetch:
git commit-graph write --reachable
El mantenimiento moderno de Git puede manejar esto por ti. Verifica tu versión:
git --version
Mantener Git actualizado es una de las soluciones de rendimiento menos dramáticas. Las versiones más nuevas mejoran regularmente el sparse checkout, el clon parcial, el monitoreo del sistema de archivos y el comportamiento de mantenimiento.
Repositorios grandes y monorepos
Si un repositorio es lento porque es genuinamente grande, los ajustes locales solo llegan hasta cierto punto. Necesitas cambios en el flujo de trabajo.
Para repositorios con muchos binarios, mueve los activos grandes a Git LFS o un almacén de artefactos. Para archivos generados, deja de confirmar salidas que se pueden reconstruir. Para monorepos, usa sparse checkout y herramientas de compilación que entiendan los límites del proyecto. Para CI, evita clones de profundidad completa a menos que el trabajo necesite el historial completo.
Una configuración útil de monorepo para un desarrollador que trabaja en un servicio podría ser:
git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/billing packages/common
Una configuración útil de CI para un trabajo de prueba simple podría ser:
git fetch --depth 50 origin main
La profundidad correcta depende del trabajo. Si tu herramienta de versionado usa etiquetas de hace meses, una profundidad de 1 la romperá.
Hooks y herramientas externas
Git puede no ser la parte lenta. Un hook pre-commit puede ejecutar formateadores, linters, pruebas, escaneos de secretos o verificaciones de dependencias. Un hook post-checkout puede reconstruir archivos. Un ayudante de credenciales puede pausarse mientras intenta desbloquear un llavero.
Verifica los hooks:
git config --get core.hooksPath
ls -l .git/hooks .githooks 2>/dev/null
Compara temporalmente con los hooks deshabilitados solo si entiendes el riesgo:
git commit --no-verify
Para comandos que no son de confirmación, mueve o deshabilita el hook en una copia de prueba del repositorio en lugar de eliminar los hooks del equipo de tu checkout principal.
Si un IDE hace que Git sea lento pero la terminal es rápida, inspecciona las integraciones de Git del IDE. Algunas herramientas ejecutan git status repetidamente, escanean archivos no rastreados o actualizan el estado de la rama en segundo plano.
Verificaciones de red y remotas
Para operaciones remotas, separa Git de la ruta de red. Prueba:
GIT_TRACE_PERFORMANCE=1 git ls-remote <url>
GIT_TRACE_PERFORMANCE=1 git fetch
Si git ls-remote es lento, el retraso ocurre antes de que se transfieran muchos datos del repositorio. Piensa en DNS, proxy, VPN, autenticación SSH, disponibilidad remota o solicitudes de credenciales. Si ls-remote es rápido pero fetch es lento, el tamaño de los datos del repositorio y la negociación son más probables.
Para remotos SSH, prueba SSH directamente:
ssh -T [email protected]
Usa tu host Git real. Para remotos HTTPS, las solicitudes del administrador de credenciales pueden estar ocultas detrás de ventanas de GUI. Un fetch detenido puede estar esperando autenticación.
Un árbol de decisión corto
Si git status es lento, inspecciona archivos no rastreados, directorios generados, antivirus, carpetas de sincronización en la nube, monitor del sistema de archivos y configuraciones del índice.
Si el clon es lento, considera clon superficial, clon parcial, sparse checkout, Git LFS y si el historial del repositorio contiene blobs grandes.
Si fetch es lento, poda referencias obsoletas, actualiza Git, inspecciona trazas de red y verifica si el remoto tiene muchas ramas o etiquetas.
Si push es lento, busca objetos nuevos grandes, hooks lentos, verificaciones del lado del servidor y problemas de red o proxy.
Si todos los comandos de Git son lentos, verifica la salud del disco, el espacio libre, el software de seguridad, la versión de Git y si el repositorio reside en un montaje de red.
La mejor solución es la que coincide con el cuello de botella medido. El trabajo de rendimiento de Git se vuelve desordenado cuando se aplican todas las sugerencias a la vez. Cambia una cosa, mide de nuevo y mantén el cambio solo si realmente ayuda.
Las soluciones a nivel de equipo superan los ajustes personales
Si solo un desarrollador tiene Git lento, la configuración local y la salud de la máquina son buenos lugares para empezar. Si todos tienen Git lento, el repositorio necesita atención. Los ajustes personales ocultarán el dolor por un tiempo, pero los nuevos desarrolladores y los trabajos de CI seguirán pagando el costo.
Busca objetos grandes que nunca deberían haberse confirmado, directorios generados que pertenecen a .gitignore y ramas antiguas que mantienen vivo el historial innecesario. Antes de reescribir el historial, habla con el equipo. Las reescrituras de historial afectan a cada clon y cada rama abierta. Pueden valer la pena, pero necesitan coordinación.
Para repositorios con activos grandes legítimos, define una política en lugar de confiar en la memoria. Por ejemplo: código fuente en Git, exportaciones de diseño en Git LFS, artefactos de compilación en el repositorio de artefactos, volcados de base de datos en almacenamiento controlado y archivos temporales locales ignorados. Pon esas reglas en .gitattributes y .gitignore para que Git pueda hacer cumplir la forma del repositorio.
CI merece su propia revisión. Muchos pipelines clonan el historial completo porque era el predeterminado copiado hace años. Si el trabajo solo ejecuta pruebas unitarias, puede que no necesite todas las etiquetas y todas las ramas. Si el trabajo construye una versión, puede necesitar etiquetas pero no todos los blobs en un monorepo. Mide el tiempo de clonación por separado del tiempo de compilación para que el costo del repositorio sea visible.
Una auditoría simple de CI pregunta:
¿Este trabajo necesita el historial completo?
¿Necesita etiquetas?
¿Necesita cada submódulo?
¿Necesita cada directorio en el monorepo?
¿Obtiene archivos LFS que nunca lee?
Responder esas preguntas honestamente a menudo ahorra más tiempo que ajustar opciones oscuras de Git.
Finalmente, documenta el comando de clonación recomendado para el proyecto. Si los nuevos desarrolladores deben usar clon parcial y sparse checkout, dilo en el README. Si necesitan Git LFS antes del checkout, dilo también. La guía de rendimiento que solo vive en el historial de shell de un desarrollador senior no ayuda a la siguiente persona.