FunktionenPreiseDokumentationBlog
mote holen
← Blog
Entwicklung·7. September 2026·4 Min. Lesezeit

Damit das Dokument auch nach `git checkout` nicht leer ist, prüfen wir Bytes statt Zeitstempel

Ist Ihnen schon einmal ein geöffnetes Dokument plötzlich leer angezeigt worden, nachdem Sie den Branch gewechselt hatten? Beim Aufnehmen eines Screenshots für unseren Blog haben wir genau das in unserer App erlebt und bei der Behebung gleich noch einen unauffälligeren Fehler mit beseitigt.

m
mote
Das Banner, das erscheint, wenn ein gerade bearbeitetes Dokument außerhalb der App geändert wurde. Man kann wählen, ob man es neu laden oder weiter bearbeiten möchte, und in der Statuszeile stehen noch 13 B ungespeicherter Änderungen aus.
Das Banner, das erscheint, wenn ein gerade bearbeitetes Dokument außerhalb der App geändert wurde. Man kann wählen, ob man es neu laden oder weiter bearbeiten möchte, und in der Statuszeile stehen noch 13 B ungespeicherter Änderungen aus.

mote vertraut Dateien unverändert. Der Inhalt auf dem Bildschirm entspricht den Bytes auf dem Datenträger, und beim Speichern schreibt mote genau diese Bytes zurück.

Aber was passiert, wenn eine Datei außerhalb der App geändert wird? Wenn mote das nicht bemerkt, kann beim nächsten Speichern der Inhalt überschrieben werden, den jemand anderes geschrieben hat.

Wir haben dieses Problem beim Aufnehmen eines Screenshots entdeckt. Während wir zwischen Branches wechselten, um eine Ansicht mit „sich ändernden Bytes“ vorzubereiten, blieb das Dokument gelegentlich als leerer Bildschirm stehen.

Zwei Probleme, die sich hinter dem leeren Bildschirm verbargen

git checkout ändert eine Datei nicht auf einmal. Es löscht die vorhandene Datei, erstellt sie neu und schreibt anschließend den Inhalt hinein.

Der Datei-Watcher meldet diesen Vorgang als mehrere Ereignisse. mote las die Datei jedoch sofort, sobald das erste Ereignis eintraf. Manchmal wurde deshalb eine leere Datei gelesen, bevor ihr Inhalt geschrieben worden war.

Beim erneuten Lesen der Datei wurde auch der Watcher neu erstellt. In der Zwischenzeit gingen die noch beim vorherigen Watcher anstehenden Ereignisse verloren, und der Bildschirm blieb leer.

Es gab noch ein gefährlicheres Problem.

mote betrachtete alle Ereignisse, die innerhalb von 1,5 Sekunden nach dem Speichern eintrafen, als „von mir verursachte Änderungen“ und ignorierte sie. Innerhalb dieses Zeitraums kann jedoch auch ein Formatter oder ein Speicher-Hook die Datei erneut ändern.

Wenn die Datei tatsächlich 0,3 Sekunden nach dem Speichern geändert wurde, zeigte mote keinerlei Veränderung an. Speicherten die Benutzer erneut, verschwand der außerhalb der App geänderte Inhalt unbemerkt.

Das eine Problem war sichtbar, das andere nicht. Die Ursache war dieselbe.

Änderungen an Dateien wurden anhand der Zeit statt anhand ihres Inhalts beurteilt.

Wir vergleichen jetzt den Inhalt statt der Zeit

Wenn jetzt ein Dateiereignis eintrifft, wird die Datei nicht sofort gelesen. Wir warten 200 ms und starten die Wartezeit jedes Mal neu, wenn ein weiteres Ereignis eintrifft.

Erst nachdem das Löschen, Erstellen und Schreiben der Datei abgeschlossen ist, wird ihr letzter Zustand ein einziges Mal gelesen. Veraltete Timer werden mithilfe einer Generationsnummer ungültig gemacht. Außerdem haben wir keinen Timer angelegt, der ständig läuft, wenn nichts passiert.

Der gelesene Inhalt wird mit den zuletzt vom Puffer gesehenen Bytes auf dem Datenträger verglichen.

Sind sie identisch, hat entweder die App gespeichert oder derselbe Inhalt wurde erneut geschrieben. Sind sie unterschiedlich, handelt es sich um eine echte externe Änderung. Ob seit dem Speichern 0,1 Sekunden oder 1 Sekunde vergangen sind, spielt keine Rolle.

Wenn dieselbe Datei erneut gelesen wird, bleibt auch der Watcher bestehen. So gehen keine Ereignisse verloren, während der Watcher ausgetauscht wird.

Wir fragen nur während der Bearbeitung nach

Nachdem eine externe Änderung erkannt wurde, prüfen wir den Zustand des Benutzers.

Wenn ungespeicherte Bearbeitungen vorhanden sind, zeigen wir ein Banner an. Die Benutzer können wählen, ob sie die geänderte Datei neu laden oder den aktuellen Inhalt weiter bearbeiten möchten. Da bei beiden Möglichkeiten Inhalte verloren gehen können, trifft die App diese Entscheidung nicht an ihrer Stelle.

Wird das Dokument gerade nicht bearbeitet, laden wir den neuen Inhalt sofort. Es erscheint kein Banner, und die Positionen von Cursor und Bildlauf bleiben unverändert. Beim Wechseln zwischen Branches, um ein Dokument zu lesen, fühlt es sich natürlicher an, die aktuelle Leseposition beizubehalten, als eine Benachrichtigung anzuzeigen.

Eine außerhalb der App geänderte Datei, während sie nicht bearbeitet wird. Der neue Inhalt wurde ohne Banner geladen, und sowohl die Bearbeitungsmarkierung im Titel als auch die ausstehenden Bytes in der Statuszeile sind verschwunden.
Eine außerhalb der App geänderte Datei, während sie nicht bearbeitet wird. Der neue Inhalt wurde ohne Banner geladen, und sowohl die Bearbeitungsmarkierung im Titel als auch die ausstehenden Bytes in der Statuszeile sind verschwunden.

Auch wenn eine Datei verschwindet, treffen wir nicht sofort eine Entscheidung. Da ein anderer Editor die Datei möglicherweise gerade umbenennt, prüfen wir nach 300 ms erneut.

Ist die Datei dann immer noch nicht vorhanden, informieren wir die Benutzer. Der geschriebene Inhalt bleibt im Puffer erhalten, und beim erneuten Speichern wird die Datei erstellt.

Wir prüfen sechs Dinge in einer realen Umgebung

Dieses Problem lässt sich nur schwer allein mit Unit-Tests überprüfen. Es entsteht nämlich, wenn Datei-Watcher, tatsächlicher Datenträger und Fenstermanager gemeinsam arbeiten.

Deshalb haben wir Prüfungen erstellt, die in einer realen Umgebung ausgeführt werden.

Wir prüfen, ob der Puffer der Datei auch nach zehn Branch-Wechseln folgt und ob eine Änderung der Datei 300 ms nach dem Speichern übernommen wird. Außerdem prüfen wir, ob keine Benachrichtigung erscheint, wenn die App selbst speichert, und ob bei einer externen Änderung während der Bearbeitung ein Banner angezeigt wird, ohne den geschriebenen Inhalt zu verlieren.

Insgesamt sind es sechs Prüfungen. Wir führen sie bei jedem Release aus.

Ausgabe der Ausführung von tools/e2e/external.sh auf einem physischen Display. Alle sechs Zeilen zeigen ok, und die letzte Zeile lautet PASS=6 FAIL=0.
Ausgabe der Ausführung von tools/e2e/external.sh auf einem physischen Display. Alle sechs Zeilen zeigen ok, und die letzte Zeile lautet PASS=6 FAIL=0.

Eine Datei als Quelle der Wahrheit zu behandeln, endet nicht damit, den gespeicherten Inhalt unverändert zu schreiben.

Wenn sich eine Datei ohne unser Wissen ändert, dürfen wir auch diese Änderung nicht übersehen.

← ÄlterWie wir verhindert haben, dass Dateien beschädigt werden, selbst wenn das Programm beim Speichern beendet wird
Bleiben Sie per RSS auf dem Laufenden.
Nur das Schreiben bleibt.
Deutsch
© 2026 mote