FonctionnalitésTarifsDocumentationBlog
Obtenir mote
← Blog
Technique·7 septembre 2026·4 min de lecture

Pour que le document ne soit pas vide après un `git checkout`, nous regardons les octets plutôt que l’horloge

Vous est-il déjà arrivé de changer de branche et de voir un document ouvert devenir vide ? En prenant une capture d’écran destinée au blog, nous avons observé cette scène dans notre application et, tant qu’à la corriger, nous avons aussi éliminé un autre bug, plus discret.

m
mote
La bannière qui apparaît lorsqu’un document en cours de modification est modifié depuis l’extérieur. Elle permet de choisir entre le recharger et continuer à le modifier, tandis que la barre d’état indique qu’il reste encore 13 B non enregistrés.
La bannière qui apparaît lorsqu’un document en cours de modification est modifié depuis l’extérieur. Elle permet de choisir entre le recharger et continuer à le modifier, tandis que la barre d’état indique qu’il reste encore 13 B non enregistrés.

mote fait confiance aux fichiers tels quels. Le contenu affiché à l’écran est identique aux octets présents sur le disque et, lors de l’enregistrement, ces octets sont écrits tels quels.

Mais que se passe-t-il si un fichier est modifié en dehors de l’application ? Si mote ne s’en aperçoit pas, le prochain enregistrement risque d’écraser ce qu’une autre personne a écrit.

Nous avons découvert ce problème en prenant des captures d’écran. Alors que nous passions d’une branche à l’autre pour préparer l’écran des « octets qui changent », il arrivait que le document reste figé sur un écran vide.

Deux problèmes cachés derrière l’écran vide

git checkout ne remplace pas un fichier en une seule fois. Il supprime le fichier existant, en crée un nouveau, puis y écrit son contenu.

Le système de surveillance des fichiers signale ce processus au moyen de plusieurs événements. Mais mote lisait le fichier dès l’arrivée du premier événement. Comme son contenu n’avait pas encore été écrit, il arrivait qu’il lise un fichier vide.

En relisant le fichier, nous recréions également le système de surveillance. Pendant ce temps, les événements encore en attente dans l’ancien système de surveillance disparaissaient, et l’écran restait vide.

Il existait aussi un problème plus dangereux.

Pendant 1,5 seconde après un enregistrement, mote considérait les événements reçus comme des « modifications que j’ai moi-même produites » et les ignorait. Mais, durant cet intervalle, un formateur ou un hook d’enregistrement pouvait à son tour modifier le fichier.

Dans les faits, si le fichier était modifié 0,3 seconde après son enregistrement, mote n’affichait aucun changement. Lorsque l’utilisateur l’enregistrait de nouveau, le contenu modifié depuis l’extérieur disparaissait silencieusement.

L’un était visible, l’autre ne l’était pas. Leur cause était la même.

Nous jugions les modifications d’un fichier en fonction du temps, et non de son contenu.

Nous avons comparé le contenu plutôt que le temps

Désormais, lorsqu’un événement de fichier survient, nous ne lisons pas immédiatement le fichier. Nous attendons 200 ms et recommençons ce délai chaque fois qu’un nouvel événement arrive.

Une fois terminée la succession d’opérations consistant à supprimer, créer puis écrire le fichier, nous ne lisons qu’une seule fois son état final. Les anciens minuteurs sont invalidés à l’aide d’un numéro de génération. Nous n’avons pas non plus créé de minuteur qui continuerait à tourner lorsqu’il ne se passe rien.

Le contenu lu est comparé aux derniers octets du disque observés par le tampon.

S’ils sont identiques, cela signifie que l’application a enregistré le fichier ou que le même contenu a été réécrit. S’ils diffèrent, il s’agit d’une véritable modification externe. Que 0,1 seconde ou 1 seconde se soit écoulée depuis l’enregistrement n’a aucune importance.

Lorsque nous relisons le même fichier, nous conservons également le système de surveillance. Ainsi, aucun événement ne disparaît pendant son remplacement.

Nous ne posons la question que pendant une modification

Après avoir détecté une modification externe, nous examinons l’état de l’utilisateur.

S’il existe des modifications non enregistrées, nous affichons une bannière. L’utilisateur peut choisir de recharger le fichier modifié ou de continuer à modifier le contenu actuel. Comme du contenu peut être perdu dans les deux cas, l’application ne prend pas cette décision à sa place.

Si aucune modification n’est en cours, nous chargeons immédiatement le nouveau contenu. Nous n’affichons pas de bannière et conservons la position du curseur et du défilement. Lorsque l’on consulte un document en passant d’une branche à l’autre, il est plus naturel de conserver l’endroit où l’on était en train de lire que d’afficher une notification.

Fichier modifié depuis l’extérieur alors qu’aucune modification n’est en cours. Le nouveau contenu a été chargé sans bannière, et l’indicateur de modification dans le titre ainsi que les octets en attente dans la barre d’état ont disparu.
Fichier modifié depuis l’extérieur alors qu’aucune modification n’est en cours. Le nouveau contenu a été chargé sans bannière, et l’indicateur de modification dans le titre ainsi que les octets en attente dans la barre d’état ont disparu.

Lorsqu’un fichier disparaît, nous ne prenons pas non plus de décision immédiatement. Comme un autre éditeur peut être en train de renommer le fichier, nous effectuons une nouvelle vérification après 300 ms.

Si le fichier est toujours absent à ce moment-là, nous en informons l’utilisateur. Le contenu en cours de rédaction reste dans le tampon et, s’il est enregistré de nouveau, le fichier est recréé.

Nous vérifions six éléments dans un environnement réel

Il est difficile de vérifier ce problème uniquement à l’aide de tests unitaires. Il survient en effet lorsque le système de surveillance des fichiers, le disque réel et le gestionnaire de fenêtres fonctionnent ensemble.

Nous avons donc créé des vérifications exécutées dans un environnement réel.

Nous vérifions que le tampon continue à suivre le fichier même après dix changements de branche, et que les modifications apportées au fichier 300 ms après son enregistrement sont bien prises en compte. Nous vérifions également qu’aucune notification ne s’affiche lorsque l’application enregistre elle-même le fichier et que, lorsqu’une modification externe survient pendant l’édition, une bannière apparaît tout en préservant le contenu rédigé.

Il y en a six en tout. Nous les exécutons à chaque publication.

Sortie de l’exécution de tools/e2e/external.sh sur un véritable écran. Les six lignes indiquent toutes ok, et la dernière ligne affiche PASS=6 FAIL=0.
Sortie de l’exécution de tools/e2e/external.sh sur un véritable écran. Les six lignes indiquent toutes ok, et la dernière ligne affiche PASS=6 FAIL=0.

Traiter un fichier comme la source de vérité ne se limite pas à écrire tel quel le contenu enregistré.

Lorsqu’un fichier est modifié à notre insu, nous devons également veiller à ne pas manquer cette modification.

← Plus anciensComment nous avons empêché les fichiers d’être corrompus même en cas d’arrêt pendant l’enregistrement
Suivez la suite par RSS.
Ne garder que l’écriture.
Français
© 2026 mote