Dépannage des opérations Git lentes : pièges courants et solutions

Diagnostiquez les commandes Git lentes en séparant les causes liées au statut, au clone, au fetch, au push, aux hooks, au système de fichiers, au réseau et à la taille du dépôt.

Dépannage des opérations Git lentes : pièges courants et solutions

Git lent a différentes causes selon la commande qui est lente. Un git status lent est généralement dû au système de fichiers local ou au travail sur l'index. Un git fetch lent est souvent lié au réseau, à la taille du dépôt distant ou à la négociation. Un git checkout lent peut être dû au nombre de fichiers, à l'analyse antivirus, à des problèmes de checkout partiel ou à des fichiers générés. Un git push lent peut être dû à des objets volumineux, des hooks, la compression ou le serveur distant.

Donc la première solution n'est pas git gc. La première solution est de mesurer l'opération exacte.

Sur macOS ou Linux :

time git status
time git fetch --prune
time git checkout main

Sur PowerShell :

Measure-Command { git status }

Exécutez la commande deux fois. La première exécution peut être plus lente car le cache du système d'exploitation est froid. Si le premier git status prend dix secondes et le second une seconde, vous êtes peut-être confronté à un comportement du cache disque. Si les deux sont lents, continuez à creuser.

Git dispose d'un traçage intégré qui peut montrer où le temps est passé :

GIT_TRACE=1 git status
GIT_TRACE_PERFORMANCE=1 git status
GIT_TRACE_PACKET=1 GIT_TRACE=1 git fetch

GIT_TRACE_PACKET est bruyant, mais utile lorsque fetch ou push se bloque pendant la négociation du protocole. Ne collez pas la sortie de traçage avec des URL de dépôt privé ou des jetons dans des tickets publics.

Quand git status est lent

git status vérifie l'index et l'arbre de travail. Il devient lent lorsque le dépôt a un nombre énorme de fichiers, que l'arbre de travail est sur un système de fichiers lent, que les métadonnées des fichiers sont coûteuses à lire, ou qu'un autre programme analyse chaque fichier que Git touche.

Commencez par les bases :

git status --short
git config --show-origin --get core.fsmonitor
git config --show-origin --get core.untrackedCache
git config --show-origin --get core.preloadIndex

Pour les grands arbres de travail, ces paramètres peuvent aider sur de nombreux systèmes :

git config core.untrackedCache true
git config core.preloadIndex true

Utilisez d'abord la configuration locale pour pouvoir tester par dépôt. Si cela aide, rendez-la globale plus tard.

Le moniteur de système de fichiers intégré de Git peut accélérer le statut en évitant les analyses complètes sur les plateformes et versions de Git prises en charge :

git config core.fsmonitor true

Si le statut devient incorrect ou étrange après l'avoir activé, désactivez-le et mettez à jour Git avant de réessayer :

git config --unset core.fsmonitor

Les fichiers non suivis peuvent être un problème caché. Les sorties de construction, les répertoires de dépendances, les rapports générés et les journaux locaux doivent généralement être ignorés. Vérifiez ce que Git analyse :

git status --untracked-files=all --short | head -100

Si vous voyez node_modules/, dist/, .venv/, target/ ou des répertoires générés similaires, ajoutez les motifs appropriés à .gitignore. N'ignorez pas les fichiers source juste pour accélérer le statut. Ignorez les fichiers qui ne devraient vraiment pas être versionnés.

Sous Windows, l'analyse antivirus en temps réel est une raison courante pour laquelle Git semble lent. Git lit de nombreux petits fichiers dans .git et l'arbre de travail, et le logiciel de sécurité peut inspecter chaque accès. Si votre organisation le permet, excluez les espaces de travail de développement de confiance de l'analyse en temps réel. N'excluez pas les répertoires où vous exécutez du code non fiable.

Évitez également de placer des dépôts actifs dans des dossiers de synchronisation cloud tels que OneDrive, Dropbox ou iCloud Drive. Les outils de synchronisation peuvent verrouiller des fichiers, réécrire des métadonnées et entrer en concurrence avec les propres opérations de fichiers de Git.

Quand le clone ou le fetch est lent

Un clone lent peut signifier un historique volumineux, de nombreux gros blobs, un dépôt distant lent ou un chemin réseau avec une latence élevée. Mesurez la taille du dépôt après le clonage :

git count-objects -vH
du -sh .git 2>/dev/null

Pour les jobs CI et les environnements temporaires, utilisez un clone peu profond lorsque l'historique n'est pas nécessaire :

git clone --depth 1 <url>

Pour une construction de branche :

git clone --depth 1 --branch main <url>

Les clones peu profonds ne sont pas idéaux pour tous les workflows. Les commandes qui nécessitent l'historique, les tags, les bases de fusion ou les calculs de version peuvent échouer ou produire des réponses incomplètes. En CI, c'est souvent acceptable. Sur une machine de développeur, cela peut être frustrant.

Le clone partiel est utile lorsque l'historique du dépôt est nécessaire mais que les blobs de fichiers peuvent être téléchargés paresseusement :

git clone --filter=blob:none <url>

Cela fonctionne mieux avec les serveurs Git modernes qui prennent bien en charge le clone partiel. Testez-le avec votre hébergeur avant d'en faire la recommandation officielle de l'équipe.

Si vous n'avez besoin que d'une partie d'un monorepo, combinez le checkout partiel avec un clone normal ou partiel :

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/api shared/lib

Le checkout partiel réduit la taille de l'arbre de travail. Il ne rend pas magiquement chaque opération Git bon marché, mais il aide lorsque le nombre de fichiers est le problème principal.

Pour les fetch avec de nombreuses branches distantes supprimées, éliminez les références obsolètes :

git fetch --prune

Pour en faire la valeur par défaut :

git config --global fetch.prune true

Quand le push est lent

La vitesse du push dépend de la quantité de nouvelles données d'objet que vous envoyez, du coût de l'empaquetage local, de l'exécution des hooks et de la rapidité avec laquelle le dépôt distant accepte le pack.

Vérifiez si vous avez accidentellement commité des fichiers volumineux :

git rev-list --objects --all | sort -k 2 | tail

Cette commande est grossière car elle n'affiche pas les tailles. Pour une inspection plus approfondie, utilisez des outils tels que git-sizer ou les commandes d'analyse de git filter-repo si disponibles. Le point pratique est simple : si une vidéo, un dump de base de données, une archive ou un artefact de construction est entré dans l'historique, chaque clone peut en payer le prix jusqu'à ce que l'historique soit réécrit ou que le projet passe à un meilleur modèle de stockage.

Git LFS est la réponse habituelle pour les gros actifs binaires qui appartiennent au projet mais ne devraient pas vivre comme des blobs Git normaux :

git lfs install
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes

Git LFS aide surtout lorsqu'il est adopté avant que les gros fichiers n'entrent dans l'historique. Migrer un historique existant est possible, mais cela réécrit les commits et nécessite une coordination d'équipe.

Soyez prudent avec les anciens conseils d'augmenter http.postBuffer. Il est souvent suggéré pour les problèmes de push, mais il résout rarement la lenteur générale dans Git moderne. Si les push échouent avec des erreurs HTTP spécifiques, vérifiez l'erreur exacte, le proxy, les limites du serveur et la version de Git avant d'appliquer des paramètres de tampon aléatoires.

Maintenance du dépôt : git gc, graphes de commits et réempaquetage

Git stocke les objets dans des packfiles. Au fil du temps, les dépôts locaux peuvent accumuler des objets lâches et des packs inefficaces. Git exécute la maintenance automatiquement dans de nombreux workflows, mais la maintenance manuelle peut encore aider les dépôts plus anciens ou très actifs.

Commencez par une commande de maintenance sûre :

git maintenance run

Ou la commande plus ancienne :

git gc

Évitez de faire de git gc --prune=now votre premier geste. L'élagage immédiat supprime les objets inaccessibles qui pourraient autrement être récupérables pendant un certain temps. Cela peut être acceptable lorsque vous savez ce que vous faites, mais ce n'est pas un bouton de vitesse inoffensif.

Pour les dépôts avec de grands historiques, les graphes de commits peuvent améliorer les parcours d'historique utilisés par des commandes telles que log, merge-base et la négociation de fetch :

git commit-graph write --reachable

La maintenance moderne de Git peut gérer cela pour vous. Vérifiez votre version :

git --version

Garder Git à jour est l'une des corrections de performance les moins spectaculaires. Les versions plus récentes améliorent régulièrement le checkout partiel, le clone partiel, la surveillance du système de fichiers et le comportement de maintenance.

Grands dépôts et monorepos

Si un dépôt est lent parce qu'il est vraiment volumineux, les ajustements locaux ne suffisent pas. Vous avez besoin de changements de workflow.

Pour les dépôts lourds en binaires, déplacez les gros actifs vers Git LFS ou un magasin d'artefacts. Pour les fichiers générés, arrêtez de commiter les sorties qui peuvent être reconstruites. Pour les monorepos, utilisez le checkout partiel et des outils de construction qui comprennent les limites du projet. Pour la CI, évitez les clones en profondeur complète sauf si le job a besoin de tout l'historique.

Une configuration utile de monorepo pour un développeur travaillant sur un seul service pourrait être :

git clone --filter=blob:none --sparse <url>
cd repo
git sparse-checkout set services/billing packages/common

Une configuration CI utile pour un simple job de test pourrait être :

git fetch --depth 50 origin main

La bonne profondeur dépend du job. Si votre outil de versionnement utilise des tags datant de plusieurs mois, une profondeur de 1 le cassera.

Hooks et outils externes

Git n'est peut-être pas la partie lente. Un hook pre-commit peut exécuter des formateurs, des linters, des tests, des analyses de secrets ou des vérifications de dépendances. Un hook post-checkout peut reconstruire des fichiers. Un helper d'identification peut faire une pause en essayant de déverrouiller un trousseau.

Vérifiez les hooks :

git config --get core.hooksPath
ls -l .git/hooks .githooks 2>/dev/null

Comparez temporairement avec les hooks désactivés seulement si vous comprenez le risque :

git commit --no-verify

Pour les commandes non-commit, déplacez ou désactivez le hook dans une copie de test du dépôt plutôt que de supprimer les hooks d'équipe de votre checkout principal.

Si un IDE rend Git lent mais que le terminal est rapide, inspectez les intégrations Git de l'IDE. Certains outils exécutent git status de manière répétée, analysent les fichiers non suivis ou actualisent l'état de la branche en arrière-plan.

Vérifications réseau et distantes

Pour les opérations distantes, séparez Git du chemin réseau. Essayez :

GIT_TRACE_PERFORMANCE=1 git ls-remote <url>
GIT_TRACE_PERFORMANCE=1 git fetch

Si git ls-remote est lent, le retard se produit avant que beaucoup de données du dépôt ne soient transférées. Pensez au DNS, proxy, VPN, authentification SSH, disponibilité distante ou invites d'identification. Si ls-remote est rapide mais que fetch est lent, la taille des données du dépôt et la négociation sont plus probables.

Pour les dépôts distants SSH, testez SSH directement :

ssh -T [email protected]

Utilisez votre véritable hébergeur Git. Pour les dépôts distants HTTPS, les invites du gestionnaire d'identification peuvent être cachées derrière des fenêtres GUI. Un fetch bloqué peut attendre une authentification.

Un petit arbre de décision

Si git status est lent, inspectez les fichiers non suivis, les répertoires générés, l'antivirus, les dossiers de synchronisation cloud, le moniteur de système de fichiers et les paramètres d'index.

Si le clone est lent, envisagez le clone peu profond, le clone partiel, le checkout partiel, Git LFS et si l'historique du dépôt contient de gros blobs.

Si le fetch est lent, éliminez les références obsolètes, mettez à jour Git, inspectez les traces réseau et vérifiez si le dépôt distant a de nombreuses branches ou tags.

Si le push est lent, recherchez de gros nouveaux objets, des hooks lents, des vérifications côté serveur et des problèmes de réseau ou de proxy.

Si toutes les commandes Git sont lentes, vérifiez la santé du disque, l'espace libre, le logiciel de sécurité, la version de Git et si le dépôt se trouve sur un montage réseau.

La meilleure correction est celle qui correspond au goulot d'étranglement mesuré. Le travail de performance Git devient désordonné lorsque toutes les suggestions sont appliquées à la fois. Changez une chose, mesurez à nouveau et ne conservez le changement que s'il aide réellement.

Les correctifs au niveau de l'équipe surpassent les ajustements personnels

Si un seul développeur a Git lent, les paramètres locaux et la santé de la machine sont de bons points de départ. Si tout le monde a Git lent, le dépôt a besoin d'attention. Les ajustements personnels cacheront la douleur pendant un certain temps, mais les nouveaux développeurs et les jobs CI continueront à en payer le coût.

Recherchez les objets volumineux qui n'auraient jamais dû être commités, les répertoires générés qui appartiennent à .gitignore et les anciennes branches qui maintiennent un historique inutile en vie. Avant de réécrire l'historique, parlez-en à l'équipe. Les réécritures d'historique affectent chaque clone et chaque branche ouverte. Elles peuvent en valoir la peine, mais elles nécessitent une coordination.

Pour les dépôts avec des actifs volumineux légitimes, définissez une politique au lieu de compter sur la mémoire. Par exemple : code source dans Git, exports de conception dans Git LFS, artefacts de construction dans le dépôt d'artefacts, dumps de base de données dans un stockage contrôlé et fichiers de travail locaux ignorés. Mettez ces règles dans .gitattributes et .gitignore pour que Git puisse appliquer la forme du dépôt.

La CI mérite sa propre révision. De nombreux pipelines clonent l'historique complet parce que c'était la valeur par défaut copiée il y a des années. Si le job exécute seulement des tests unitaires, il n'a peut-être pas besoin de tous les tags et de toutes les branches. Si le job construit une version, il peut avoir besoin de tags mais pas de chaque blob dans un monorepo. Mesurez le temps de clone séparément du temps de construction pour que le coût du dépôt soit visible.

Un audit CI simple demande :

Ce job a-t-il besoin de tout l'historique ?
A-t-il besoin de tags ?
A-t-il besoin de chaque sous-module ?
A-t-il besoin de chaque répertoire dans le monorepo ?
Récupère-t-il des fichiers LFS qu'il ne lit jamais ?

Répondre honnêtement à ces questions permet souvent d'économiser plus de temps que de régler des options Git obscures.

Enfin, documentez la commande de clone recommandée pour le projet. Si les nouveaux développeurs doivent utiliser le clone partiel et le checkout partiel, dites-le dans le README. S'ils ont besoin de Git LFS avant le checkout, dites-le aussi. Les conseils de performance qui ne vivent que dans l'historique du shell d'un développeur senior n'aident pas la personne suivante.