Le réveil de la lenteur

Ça fait un moment que tu bosses sur ton projet de data science. Tu as vingt notebooks, quelques images, et la structure de dossiers typique qui semblait être une bonne idée il y a trois mois.

Tu fais git status pour voir ce que tu as touché et… tu attends. Et tu attends. Et pendant que tu attends, tu as le temps de te demander si l’ordinateur a planté ou s’il est simplement en train de méditer.

Spoiler : il ne médite pas. Il souffre.

Le problème a un nom (et des prénoms)

Git n’est pas lent. Ton repo l’est.

Quand tu exécutes git status, git doit faire deux choses qui semblent simples mais ne le sont pas :

  1. Scanner tout l’arbre de fichiers pour voir ce qui a changé
  2. Comparer chaque fichier avec ce qu’il a sauvegardé

Dans un repo normal, c’est instantané. Mais les notebooks Jupyter sont du JSON déguisé en document. Et pas n’importe quel JSON : un qui inclut du code, des sorties, des images encodées en base64, des métadonnées du kernel, et pratiquement tout ce qui est passé par la tête de celui qui a conçu le format.

Un notebook “petit” avec quelques graphiques peut peser plusieurs mégas. Multiplie ça par vingt notebooks et tu as un repo qui gémit chaque fois que tu le regardes.

Et si en plus tu renommes un dossier… git l’interprète comme “tu as supprimé 50 fichiers et tu en as créé 50 nouveaux”. Du divertissement garanti.

Solution 1 : Mets un portier à ton repo

La première solution est si élégante que ça fait mal de ne pas l’avoir connue avant.

Ça s’appelle FSMonitor et ça fonctionne comme ça : au lieu que git scanne tout le repo chaque fois que tu fais quelque chose, le système d’exploitation lui dit quels fichiers ont changé.

Dit en bon français : c’est comme avoir un portier à l’entrée qui te dit “seul Jean est entré” au lieu de devoir réviser toute la liste d’invités à chaque fois.

Pour l’activer :

1
2
git config core.fsmonitor true
git config core.untrackedcache true

Voilà. C’est fait.

La première fois que tu feras git status après l’avoir activé, ça prendra le même temps (ou plus, parce qu’il doit initialiser le cache). Mais à partir de la deuxième… magie.

Dans mon repo de 400+ fichiers, git status est passé de 2-3 secondes à instantané. Pas “rapide”. Instantané.

Ça marche partout ?

  • macOS : Oui, utilise FSEvents
  • Linux : Oui, utilise inotify (si ton kernel le supporte, ce qu’il fait)
  • Windows : Oui, utilise ReadDirectoryChangesW

Autrement dit, oui.

Solution 2 : Sauvegarde la recette, pas la photo du plat

FSMonitor accélère le scan, mais il y a un autre problème : les notebooks restent énormes. Et chaque fois que tu exécutes une cellule et que tu sauvegardes, même si le code est le même, le fichier change parce que les sorties sont différentes.

Ça signifie :

  • Des diffs illisibles (qui veut réviser du base64 d’une image ?)
  • Des commits gonflés
  • Des merges de l’enfer

La solution s’appelle nbstripout et fait exactement ce que son nom indique : strippe l’output des notebooks avant de commiter.

C’est comme sauvegarder la recette sans les photos du plat fini. Le code est là, les résultats tu les régénères quand tu veux.

Pour l’installer avec uv :

1
2
uv add nbstripout
uv run nbstripout --install

Ou avec pip, si tu es de la vieille école :

1
2
pip install nbstripout
nbstripout --install

Le --install configure git automatiquement pour qu’il utilise nbstripout comme filtre. À partir de ce moment, quand tu fais un commit d’un notebook, il se sauvegarde sans outputs.

Pour vérifier que ça fonctionne :

1
git config --get filter.nbstripout.clean

Si ça retourne quelque chose comme nbstripout, c’est bon.

Et si j’ai besoin de sauvegarder les outputs ?

Bonne question. Parfois tu veux commiter un notebook déjà exécuté, par exemple pour la documentation ou pour que quelqu’un le voie sans avoir à l’exécuter.

nbstripout te permet de marquer des notebooks spécifiques pour qu’ils sautent le filtre :

1
git -c diff.nbstripout.textconv=cat diff

Ou tu peux configurer des exceptions dans ton .gitattributes. Mais en général, mon conseil c’est : ne sauvegarde pas les outputs. Les notebooks sont du code, pas des documents finaux.

Mention honorable : git-lfs

Si en plus des notebooks tu as des images ou des vidéos qui changent fréquemment, il existe git-lfs (Large File Storage).

L’idée est simple : au lieu de sauvegarder le binaire lourd dans le repo, tu sauvegardes un pointeur et le fichier réel vit sur un serveur à part.

C’est comme déposer les valises à la consigne de l’aéroport et ne garder que le reçu.

1
2
3
git lfs install
git lfs track "*.png"
git lfs track "*.mp4"

Pourquoi c’est seulement une mention honorable et pas la solution principale ? Parce que ça ajoute de la complexité. Tu as besoin d’un serveur LFS (GitHub et GitLab l’offrent, mais ils ont des limites), et si quelqu’un clone le repo sans git-lfs installé, il récupère les pointeurs au lieu des fichiers.

Pour les notebooks, FSMonitor + nbstripout suffisent généralement. git-lfs c’est pour quand tu as de vrais datasets ou des assets multimédia.

Le combo gagnant

Ma configuration pour n’importe quel repo avec des notebooks :

1
2
3
4
5
6
7
# Vitesse
git config core.fsmonitor true
git config core.untrackedcache true

# Notebooks propres
uv add nbstripout
uv run nbstripout --install

Quatre commandes et ton repo passe de grand-père arthritique à athlète olympique.

Vérifie que tout fonctionne

1
2
3
4
5
6
7
# FSMonitor actif
git config --get core.fsmonitor
# Devrait retourner : true

# nbstripout configuré
git config --get filter.nbstripout.clean
# Devrait retourner : nbstripout

Si les deux retournent ce qui est attendu, c’est bon. Profite de ton git status instantané et de tes diffs lisibles.

Conclusion

La prochaine fois que quelqu’un te dit “git est lent”, tu sais quoi répondre : git n’est pas lent, ton repo est mal configuré.

FSMonitor pour que le système d’exploitation fasse le sale boulot. nbstripout pour que les notebooks ne pèsent pas comme s’ils portaient du lest.

Et si après tout ça c’est encore lent… peut-être qu’il est temps de se demander si tu as vraiment besoin de 200 notebooks dans le même repo. Mais ça c’est une autre histoire.