Git-Tools für Notebooks#

Es gibt mehrere Probleme, um Jupyter Notebooks mit Git zu verwalten:

  • Die Metadaten von Zellen der Jupyter Notebooks ändern sich auch, wenn keine inhaltlichen Änderungen an den Zellen vorgenommen wurden. Damit werden Git-Diffs unnötig kompliziert.

  • Die Zeilen, die Git bei Merge-Konflikten in die *.ipynb-Dateien schreibt, führen dazu, dass die Notebooks nicht mehr gültiges JSON sind und von Jupyter deswegen nicht geöffnet werden kann: ihr erhaltet dann beim Öffnen die Fehlermeldung Error loading notebook.

    Konflikte treten besonders häufig in Notebooks auf, da Jupyter bei jeder Ausführung eines Notizbuchs Folgendes ändert:

    • Jede Zelle enthält eine Nummer, die angibt, in welcher Reihenfolge sie ausgeführt wurde. Wenn Team-Mitglieder die Zellen in unterschiedlicher Reihenfolge ausführen, hat jede einzelne Zelle einen Konflikt! Dies manuell zu beheben, würde sehr lange dauern.

    • Für jede Abbildung, z.B. einen Plot, nimmt Jupyter nicht nur das Bild selbst in das Notizbuch auf, sondern auch eine einfache Textbeschreibung, die die ID des Objekts enthält, z.B. <matplotlib.axes._subplots.AxesSubplot at 0x7fbc113dbe90>. Dies ändert sich jedes Mal, wenn ihr ein Notizbuch ausführt, und führt daher jedes Mal zu einem Konflikt, wenn zwei Personen diese Zelle ausführen.

    • Einige Ausgaben können nicht-deterministisch sein, z.B. ein Notebook, das Zufallszahlen verwendet oder mit einem Dienst interagiert, der im Laufe der Zeit unterschiedliche Ausgaben liefert.

    • Jupyter fügt dem Notizbuch Metadaten hinzu, die die Umgebung beschreiben, in der es zuletzt ausgeführt wurde, wie z.B. den Namen des Kernels. Dies variiert oft zwischen verschiedenen Installationen, und daher werden zwei Personen, die ein Notizbuch speichern (auch ohne andere Änderungen), oft einen Konflikt in den Metadaten haben.

nbdev2#

nbdev2 bietet eine Reihe von Git-Hooks, die saubere Git-Diffs bereitstellen, die die meisten Git-Konflikte automatisch lösen und sicherstellen, dass alle verbleibenden Konflikte vollständig innerhalb der Standard-Jupyter-Notebook-Umgebung aufgelöst werden können:

  • Ein neuer git merge-Treiber bietet notebook-native Konfliktmarkierungen, die dazu führen, dass Notebooks direkt in Jupyter geöffnet werden können, auch wenn es Git-Konflikte gibt. Lokale und entfernte Änderung werden jeweils als separate Zellen im Notizbuch angezeigt, so dass ihr die Version, die ihr nicht behalten möchtet, einfach löschen oder die beiden Zellen nach Bedarf kombinieren könnt.

    Siehe auch

    nbdev.merge docs

  • Git-Merges lokal zu lösen ist äußerst hilfreich, aber wir müssen sie auch Remote lösen. Wenn z.B. ein Merge-Request eingereicht wird und dann jemand anderes dasselbe Notebook überträgt, bevor der Merge-Request zusammengeführt wird, könnte dieser einen Konflikt hervorrufen:

       "outputs": [
        {
    <<<<<< HEAD
         "execution_count": 8,
    ======
         "execution_count": 5,
    >>>>>> 83e94d58314ea43ccd136e6d53b8989ccf9aab1b
         "metadata": {},
    

    Der save hook von nbdev2 entfernt automatisch alle unnötigen Metadaten (einschließlich execution_count) und nicht-deterministischen Zellausgaben; d.h., dass es keine sinnlosen Konflikte wie den obigen gibt, da diese Informationen gar nicht erst in den Commits gespeichert werden.

Um loszulegen, folgt den Anweisungen in Git-Friendly Jupyter.

Andere Git-Tools für Notebooks#

ReviewNB#

ReviewNB löst das Problem, Merge-Requests mit Notebooks durchzuführen. Die Code-Review-GUI von GitLab funktioniert nur bei zeilenbasierten Dateiformaten, wie z.B. Python-Skripten. Meistens bevorzuge ich jedoch, die Quelltext-Notebooks zu prüfen, weil:

  • ich die Dokumentation und die Tests überprüfen möchte, nicht nur die Implementierung

  • ich die Änderungen an den Zellausgaben sehen möchte, wie Diagrammen und Tabellen, nicht nur den Code.

Für diesen Zweck ist ReviewNB perfekt.

nbdime#

nbdime ist ein GUI für nbformat-Diffs und ersetzt nbdiff. Es versucht lokal Content-Aware-Diffing sowie das Merging von Notebooks, beschränkt sich nicht nur auf die Darstellung von Diffs, sondern verhindert auch, dass unnötige Änderungen eingecheckt werden. Es ist jedoch nicht kompatibel mit nbdev2.

nbstripout#

nbstripout automatisiert Clear all outputs. Es nutzt auch nbformat und ein paar Automagien um git config einzurichten. Meines Erachtens hat es jedoch zwei Nachteile:

  • es beschränkt sich auf den problematischen Metadaten-Abschnitt

  • es ist langsam.