Une mise à jour a rempli un trou de 434 Go dans le disque

Vous cliquez sur Update sur le portable, allez vous faire un café, et vous revenez sur un navigateur qui ne s’ouvre plus, un éditeur qui se plaint de l’espace disque et Hermes lui-même qui refuse de démarrer. Le coupable se révèle être ~/.hermes/hermes-agent/.git, qui est passé d’environ un gigaoctet à 434 Go et a dévoré un SSD de 1 To.
Il ne s’agissait pas de « le téléchargement de la mise à jour était énorme ». Une vérification de version en arrière-plan a déclenché une récupération à la demande sur une disposition d’installation particulière, et l’effet boule de neige a suivi. Voici le mécanisme, les chiffres réels et comment nettoyer.
Un appel git qui ne s’enquiert que d’un seul chemin
L’application desktop Hermes exécute une vérification de cohérence au démarrage (en interne : bundle-skew). Elle demande si le code empaqueté dans l’application correspond au code sur le disque, et propose un redémarrage s’ils divergent. Elle le fait avec deux commandes git :
git merge-base --is-ancestor <stamp> HEAD
git rev-list --count <stamp>..HEAD -- apps/desktop/...
Les deux sont bon marché dans un clone normal. Le problème, c’est la disposition d’installation : certaines installations sont des clones partiels treeless (--filter=tree:0 — les trees sont récupérés à la demande au lieu d’être conservés pour chaque commit historique).
La seconde commande est filtrée par chemin (-- apps/desktop/...). Pour compter combien de commits de cette plage ont touché apps/desktop, git doit lire le tree de chaque commit de toute la plage. Aucun n’est local, donc git retombe sur la machinerie des promisor et les récupère sur le réseau — et chaque récupération laisse son propre promisor pack sur le disque. Plus l’historique est long, plus il y a de trees à récupérer et de packs.
Dans le rapport d’incident, cette plage couvrait 1 784 commits.
De 1 Go à 434 Go : les chiffres
Tout ce qui suit provient du rapport de l’issue #129514 (macOS, SSD de 1 To) :
| Élément | Valeur |
|---|---|
Croissance de .git |
~1,1 Go pour un clone complet → 434 Go |
| Promisor packs | 2 659 (~398 Gio) |
Fichiers tmp_pack_* abandonnés |
1 309 (~29 Gio) |
| Plus gros pack unique | 641 Mo |
| Maintenance de fond concurrente | 179 processus au pic (git maintenance run --auto → git commit-graph write --split --reachable), chacun ~2 Go de RSS |
| Charge système | load average 220 |
| Cadence de vérification | un nouveau rev-list toutes les ~2 minutes après le reboot, chacun tournant 9 à 15 minutes |
| Résultat | la mise à jour a échoué avec [Errno 28] No space left on device, le Mac a redémarré et la CLI/TUI refusait de démarrer ensuite |
Des pics antérieurs figurent dans le même rapport : 138 promisor packs le 25 septembre, 11 le 27 septembre, 339 le 28 septembre, avec un redémarrage inattendu entre-temps. Ce n’était pas une exécution malchanceuse — cela s’accumule.
Pourquoi « c’est déjà corrigé » ne vous sauve pas en pleine mise à jour
Le correctif a atterri le 28 septembre. Aujourd’hui, la vérification sur main ressemble à ceci :
const options = {
cwd: repoRoot,
env: { ...process.env, GIT_NO_LAZY_FETCH: '1', GIT_TERMINAL_PROMPT: '0', GCM_INTERACTIVE: 'Never' },
timeoutMs: 5000
}
GIT_NO_LAZY_FETCH=1 signifie ne pas récupérer les objets à la demande — échouer plutôt. C’est exactement la sémantique que veut cette vérification, puisqu’elle ne devrait jamais dépendre du réseau. Ajoutez le timeout de 5 secondes et le reaping de l’arbre de processus, et elle ne peut plus mettre la machine à genoux.
Mais il y a un problème de timing, et c’est la partie la plus précieuse du rapport :
Le build qui exécute la vérification est celui déjà installé, et il n’est remplacé qu’à la toute fin de la mise à jour.
L’application desktop du rapporteur avait été construite le 27 septembre à partir de 96cce68 — un jour avant le correctif. Une fois que la mise à jour a tiré 1 292 commits, la vérification de l’ancienne application a parcouru 96cce68..HEAD avec l’ancien code, sans GIT_NO_LAZY_FETCH. Le disque s’est rempli, la mise à jour a échoué avant l’étape qui installe le nouveau build desktop, si bien que l’application non corrigée est restée en place — et a repris la boucle après le redémarrage. Elle ne se répare pas toute seule.
Vérifiez si vous êtes concerné
Quatre commandes en lecture seule, quelques secondes :
du -sh ~/.hermes/hermes-agent/.git # taille du stockage d'objets ; ~1 Go est normal
ls ~/.hermes/hermes-agent/.git/objects/pack/*.pack | wc -l # nombre de packs ; un ou deux chiffres sont normaux
ls ~/.hermes/hermes-agent/.git/objects/pack/tmp_pack_* | wc -l # téléchargements non activés ; normalement 0
df -h ~ # espace libre
Cherchez ensuite une file de processus git et une charge anormale :
uptime
ps aux | grep -E '[r]ev-list|[g]it maintenance' | wc -l
Des centaines de packs, une montagne de tmp_pack_*, une charge en dizaines — c’est le même schéma.
Comment nettoyer
Il s’agit d’une séquence de nettoyage git générique, pas d’une liste de commandes officielle. Assurez-vous de comprendre chaque étape avant de l’exécuter :
- Arrêtez l’hémorragie. Quittez l’application desktop (sinon elle se relance toutes les deux minutes) et arrêtez les processus
git rev-list/git maintenanceen cours. Ne lancez pas de travail lourd sur cette machine entre-temps. - Supprimez les restes non activés. Les fichiers
tmp_pack_*dans.git/objects/pack/sont des téléchargements incomplets que git ne référencera jamais ; ils peuvent être supprimés sans risque. Dans le cas rapporté, cela seul a récupéré environ 29 Gio. - Consolidez le stockage d’objets. Lancez
git gc(précédé degit repack -adsi nécessaire) pour replier les packs dispersés. Sachez qu’un gc sur un dépôt avec 2 659 packs est lent et gourmand en CPU — faites-le quand la machine est par ailleurs au repos et surveillezdf -h. - Mettez le code à jour depuis la ligne de commande. Le détail clé : faites la mise à jour depuis un terminal, pas depuis l’application desktop d’avant le correctif qui a causé le problème. Voir la référence hermes update pour les flags. Si votre installation est un clone filtré/shallow coincé dans cet état, une installation complète fraîche depuis le canal officiel est souvent plus rapide que de démêler tout ça.
- Confirmez que le correctif est bien là. Après la mise à jour,
~/.hermes/hermes-agent/apps/desktop/electron/bundle-skew.tsdevrait contenirGIT_NO_LAZY_FETCH. Sinon, vous êtes encore sur l’ancien code. - Fixez-vous un plafond mental pour
.git. Environ un gigaoctet est sain ; des dizaines de gigaoctets signifient que quelque chose tire discrètement de l’historique. N’attendez pas que le disque soit plein.
Où en est la situation
Au 2026-10-01, l’issue #129514 est toujours ouverte et étiquetée P1. C’est un suivi de #125932, qui décrivait le même mécanisme à un stade plus léger (des dizaines de processus git bloqués). Le correctif lui-même est sur main — bundle-skew.ts définit désormais GIT_NO_LAZY_FETCH=1 avec un timeout de 5 secondes, et hermes_cli/_subprocess_compat.py porte un NO_LAZY_FETCH_ENV correspondant — mais le chemin de mise à niveau depuis un build d’avant le correctif n’a aucune protection, ce qui est le problème central que soulève le rapport. On ne sait pas encore si les anciens builds recevront un garde-fou supplémentaire.
Si vous installez depuis un clone partiel, ou si vous cliquez sur Update dans l’application desktop, faites les étapes 1 et 2 ci-dessus dès aujourd’hui.
À retenir
Pas une seule ligne de code ici n’était « fausse ». Deux décisions raisonnables se sont percutées : utiliser un clone treeless pour économiser du disque, et lancer une vérification de cohérence de version par sécurité — où cette vérification contient un parcours d’historique filtré par chemin qui, sur un dépôt treeless, signifie « télécharge tout cet historique ». Une fois la boule de neige lancée, la maintenance de fond de git elle-même s’y est ajoutée et a eu raison du CPU, de la mémoire et du disque en même temps.
Trois signaux méritent d’être retenus : la taille de .git, le nombre de fichiers pack et le compte des restes tmp_pack_*. Tous les trois se lisent en quelques secondes, et tous les trois vous auraient dit que quelque chose n’allait pas bien avant que le disque ne se remplisse. Les mises à jour ont d’autres modes de défaillance, d’ailleurs — une mise à jour qui se bloque mais rapporte un succès et réussir une mise à niveau proprement valent tous deux une lecture.