Quelle est la bonne stratégie pour conserver les carnets IPython sous contrôle de version?
Le format de cahier est assez pratique pour le contrôle de version: si on veut contrôler le cahier et les sorties, cela fonctionne assez bien. Le désagrément survient lorsque l’on veut seulement contrôler la version de l’entrée, à l’exclusion des sorties de cellules (alias "build products") qui peuvent être de grandes blobs binaires, en particulier pour les films et les tracés. En particulier, j'essaie de trouver un bon flux de travail qui:
Comme mentionné, si j'ai choisi d'inclure les sorties (ce qui est souhaitable lors de l'utilisation de nbviewer par exemple), alors tout va bien. Le problème est quand je ne veux pas contrôler la version de la sortie. Il existe des outils et des scripts pour supprimer la sortie du bloc-notes, mais je rencontre fréquemment les problèmes suivants:
Cell/All Output/Clear
, créant ainsi un bruit indésirable dans les diffs. Ceci est résolu par certaines des réponses.J'ai examiné plusieurs options dont je vais discuter ci-dessous, mais je n'ai pas encore trouvé de bonne solution complète. Une solution complète peut nécessiter certaines modifications d'IPython ou peut s'appuyer sur de simples scripts externes. J'utilise actuellement Mercurial , mais j'aimerais une solution qui fonctionne également avec git : une solution idéale serait agnostique avec contrôle de version.
Cette question a été discutée à plusieurs reprises, mais il n’existe pas de solution définitive ou claire du point de vue de l’utilisateur. La réponse à cette question devrait fournir la stratégie définitive. C'est bien si cela nécessite une version récente (même de développement) de IPython ou une extension facile à installer.
Mise à jour: Je joue avec mon cahier modifié version qui enregistre éventuellement une version .clean
à chaque enregistrement en utilisant suggestions de Gregory Crosswhite . Ceci satisfait la plupart de mes contraintes mais laisse les problèmes suivants non résolus:
.clean
, et devront ensuite être intégrés d'une manière ou d'une autre dans ma version de travail. (Bien sûr, je peux toujours ré-exécuter le cahier, mais cela peut être pénible, surtout si certains résultats dépendent de longs calculs, de calculs parallèles, etc.). Je ne sais pas encore comment résoudre ce problème. . Peut-être qu'un flux de travail impliquant une extension comme ipycache pourrait fonctionner, mais cela semble un peu trop compliqué.Cell/All Output/Clear
pour supprimer la sortie.Voici ma solution avec git. Cela vous permet simplement d'ajouter et de commettre (et de diff) comme d'habitude: ces opérations ne modifieront pas votre arbre de travail, et en même temps, le fait de (ré) exécuter un bloc-notes ne modifiera pas l'historique de votre git.
Bien que cela puisse probablement être adapté à d’autres VCS, je sais que cela ne répond pas à vos exigences (au moins l’agnosticité de VSC). Pourtant, il est parfait pour moi, et bien que ce ne soit rien de particulièrement brillant, et que beaucoup de gens l'utilisent probablement déjà, je n'ai pas trouvé d'instructions claires sur la façon de le mettre en œuvre en cherchant sur Google. Cela peut donc être utile à d’autres personnes.
~/bin/ipynb_output_filter.py
)chmod +x ~/bin/ipynb_output_filter.py
)Créez le fichier ~/.gitattributes
avec le contenu suivant
*.ipynb filter=dropoutput_ipynb
Exécutez les commandes suivantes:
git config --global core.attributesfile ~/.gitattributes
git config --global filter.dropoutput_ipynb.clean ~/bin/ipynb_output_filter.py
git config --global filter.dropoutput_ipynb.smudge cat
Terminé!
Limitations:
somebranch
et que vous faites git checkout otherbranch; git checkout somebranch
, vous vous attendez généralement à ce que l’arbre de travail ne soit pas modifié. Ici, vous aurez perdu la sortie et la numérotation des cellules des cahiers dont la source diffère entre les deux branches.git commit notebook_file.ipynb
, bien que cela garderait au moins git diff notebook_file.ipynb
libre de tout déchet base64).Ma solution reflète le fait que, personnellement, je n'aime pas garder les éléments générés versionnés. Notez que les fusions impliquant la sortie invalident presque toujours la sortie ou votre productivité ou les deux.
EDIT:
si vous adoptez la solution telle que je l'ai suggérée (c'est-à-dire globalement), vous aurez des problèmes en cas de repositionnement de git que vous souhaitez pour la sortie de la version. Donc, si vous voulez désactiver le filtrage de sortie pour un référentiel git spécifique, créez simplement un fichier . Git/info/attributes , avec
**. filtre ipynb =
en tant que contenu. Clairement, de la même manière, il est possible de faire le contraire: activer le filtrage uniquement pour un référentiel spécifique.
le code est maintenant maintenu dans son propre git repo
si les instructions ci-dessus aboutissent à ImportErrors, essayez d'ajouter "ipython" avant le chemin du script:
git config --global filter.dropoutput_ipynb.clean ipython ~/bin/ipynb_output_filter.py
EDIT: Mai 2016 (mise à jour en février 2017): il existe plusieurs alternatives à mon script - pour être complet, voici une liste de celles que je connais: nbstripout ( autrevariantes ), nbstrip , jq .
Nous avons un projet collaboratif dans lequel le produit est Jupyter Notebooks, et nous utilisons une approche qui fonctionne très bien: nous activons l'enregistrement automatique des fichiers .py
et suivons les fichiers .ipynb
et les fichiers .py
.
De cette façon, si quelqu'un veut voir/télécharger le dernier cahier, il peut le faire via github ou nbviewer, et si quelqu'un veut voir comment le code du cahier a changé, il peut simplement regarder les modifications apportées aux fichiers .py
. .
Pour Jupyter
serveurs de bloc-notes , vous pouvez le faire en ajoutant les lignes.
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['jupyter', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
dans le fichier jupyter_notebook_config.py
et redémarrez le serveur de notebook.
Si vous ne savez pas dans quel répertoire trouver votre fichier jupyter_notebook_config.py
, vous pouvez taper jupyter --config-dir
, et si vous n'y trouvez pas le fichier, vous pouvez le créer en tapant jupyter notebook --generate-config
. .
Pour Ipython 3
serveurs de bloc-notes , vous pouvez le faire en ajoutant les lignes.
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
dans le fichier ipython_notebook_config.py
et redémarrez le serveur de notebook. Ces lignes proviennent d'un problème de github qui donne une réponse @ minrk fourni et @dror les inclut également dans sa SO réponse.
Pour Ipython 2
serveurs de bloc-notes , vous pouvez le faire en démarrant le serveur à l'aide de:
ipython notebook --script
ou en ajoutant la ligne
c.FileNotebookManager.save_script = True
dans le fichier ipython_notebook_config.py
et redémarrez le serveur de notebook.
Si vous ne savez pas dans quel répertoire trouver votre fichier ipython_notebook_config.py
, vous pouvez taper ipython locate profile default
, et si vous n'y trouvez pas le fichier, vous pouvez le créer en tapant ipython profile create
. .
Voici notre projet sur github qui utilise cette approche : et voici un exemple de github explorant les modifications récentes apportées à un bloc-notes .
Nous avons été très heureux avec cela.
J'ai créé nbstripout
, basé sur MinRKs Gist , qui prend en charge Git et Mercurial (grâce à mforbes). Il est conçu pour être utilisé de manière autonome sur la ligne de commande ou en tant que filtre, qui est facilement installé dans le référentiel actuel via nbstripout install
/nbstripout uninstall
.
Obtenez-le de PyPI ou simplement
pip install nbstripout
Voici une nouvelle solution de Cyrille Rossant pour IPython 3.0, qui persiste à baliser les fichiers plutôt que les fichiers ipymd basés sur JSON:
(2017-02)
stratégies
nbstripout
,)nbstripout
,)nbconvert
to python: name.ipynb.py (nbconvert
)nbconvert
, ipymd
)outils
nbstripout
: supprime les sorties d'un cahier pip install nbstripout; nbstripout install
ipynb_output_filter
: efface les sorties d'un cahier ipymd
: convertir entre {Jupyter, Markdown, Atlas O'Reilly Markdown, OpenDocument, .py} nbdime
: "Outils permettant de différencier et de fusionner les blocs-notes Jupyter." (2015) nbdiff
: comparez les blocs-notes de manière conviviale pour les terminaux nbmerge
: fusion à trois voies des notebooks avec résolution automatique des conflits nbdiff-web
: vous montre un diff riche en rendu de cahiersnbmerge-web
: vous propose un outil de fusion Web à trois voies pour ordinateurs portables.nbshow
: présente un seul bloc-notes de manière conviviale pour les terminauxJ'ai finalement trouvé un moyen simple et productif de faire en sorte que Jupyter et Jit jouent bien ensemble. Je suis toujours dans les premiers pas, mais je pense déjà que c'est déjà le cas. beaucoup mieux que toutes les autres solutions compliquées.
Visual Studio Code est un éditeur de code source cool et open source de Microsoft. Il possède une excellente extension Python qui vous permet maintenant de importer un bloc-notes Jupyter en tant que code python.
Une fois que vous avez importé votre bloc-notes dans un fichier python, tout le code et toutes les annotations sont regroupés dans un fichier python ordinaire, avec des marqueurs spéciaux dans les commentaires. Vous pouvez voir dans l'image ci-dessous:
Votre fichier python contient uniquement le contenu des cellules d'entrée du bloc-notes. La sortie sera générée dans une fenêtre fractionnée. Vous avez du code pur dans le cahier, il ne change pas pendant que vous l'exécutez. Aucune sortie mélangée avec votre code. Pas d'étrange format Json incompréhensible pour analyser vos diffs.
Juste du code pur python où vous pouvez facilement identifier chaque diff.
Je n'ai même plus besoin de versionner mes fichiers .ipynb
. Je peux mettre une ligne *.ipynb
dans .gitignore
.
Besoin de générer un cahier à publier ou à partager avec quelqu'un? Pas de problème, juste cliquez sur le bouton d'exportation dans la fenêtre interactive python
Je ne l'utilise que depuis un jour, mais je peux enfin utiliser Jupyter avec Git.
P.S .: La complétion de code VSCode est bien meilleure que Jupyter.
Comme indiqué par, le --script
est obsolète dans 3.x
. Cette approche peut être utilisée en appliquant un post-save-hook. Ajoutez en particulier ce qui suit à ipython_notebook_config.py
:
import os
from subprocess import check_call
def post_save(model, os_path, contents_manager):
"""post-save hook for converting notebooks to .py scripts"""
if model['type'] != 'notebook':
return # only do this for notebooks
d, fname = os.path.split(os_path)
check_call(['ipython', 'nbconvert', '--to', 'script', fname], cwd=d)
c.FileContentsManager.post_save_hook = post_save
Le code provient de # 8009 .
Après quelques années de suppression des sorties dans les cahiers, j'ai essayé de trouver une meilleure solution. J'utilise maintenant Jupytext , une extension que j'ai conçue pour Jupyter Notebook et Jupyter Lab.
Jupytext peut convertir les blocs-notes Jupyter en divers formats de texte (Scripts, Markdown et R Markdown). Et inversement. Il offre également la possibilité de coupler un bloc-notes à l'un de ces formats et de synchroniser automatiquement les deux représentations du bloc-notes (un .ipynb
et un fichier .md/.py/.R
).
Laissez-moi vous expliquer comment Jupytext répond aux questions ci-dessus:
me permet de choisir entre inclure ou non la sortie,
Le fichier .md/.py/.R
contient uniquement les cellules en entrée. Vous devez toujours suivre ce fichier. Versionnez le fichier .ipynb
uniquement si vous souhaitez suivre les résultats.
m'empêche de commettre accidentellement des sorties si je ne les veux pas,
Ajouter *.ipynb
à .gitignore
me permet de garder la sortie dans ma version locale,
Les sorties sont conservées dans le fichier (local) .ipynb
me permet de voir les modifications apportées aux entrées à l’aide de mon système de contrôle de version (c’est-à-dire que si je ne contrôle que les versions mais que mon fichier local contient des sorties, je voudrais pouvoir voir si les entrées ont été modifiées L’utilisation de la commande version control status enregistrera toujours une différence puisque le fichier local a des sorties.)
Le diff sur le fichier .py/.R
ou .md
est ce que vous recherchez
me permet de mettre à jour mon cahier de travail (qui contient la sortie) à partir d’un cahier vierge mis à jour. (mise à jour)
Extrayez la dernière révision du fichier .py/.R
ou .md
et actualisez votre bloc-notes dans Jupyter (Ctrl + R). Vous obtiendrez les dernières cellules d'entrée du fichier texte et les sorties correspondantes du fichier .ipynb
. Le noyau n'est pas affecté, ce qui signifie que vos variables locales sont préservées - vous pouvez continuer à travailler là où vous l'avez laissé.
Ce que j'aime avec Jupytext, c'est que le cahier (sous la forme d'un fichier .py/.R
ou .md
) peut être modifié dans votre IDE préféré. Avec cette approche, la refactorisation d’un cahier devient facile. Une fois que vous avez terminé, il vous suffit de rafraîchir le cahier dans Jupyter.
Si vous voulez essayer: installez Jupytext avec pip install jupytext
et redémarrez votre éditeur Jupyter Notebook ou Lab. Ouvrez le bloc-notes que vous souhaitez contrôler par la version et associez-le à un fichier Markdown (ou à un script) à l'aide du Jupytext Men dans le bloc-notes Jupyter (ou les commandes Jupytext dans Jupyter Lab). Enregistrez votre bloc-notes et vous obtiendrez les deux fichiers: l'original .ipynb
, ainsi que la représentation textuelle promise du bloc-notes, qui convient parfaitement au contrôle de version!
Pour ceux qui pourraient être intéressés: Jupytext est également disponible sur le ligne de commande .
Vient de tomber sur "jupytext" qui ressemble à une solution parfaite. Il génère un fichier .py à partir du bloc-notes, puis les synchronise. Vous pouvez contrôler les versions, falsifier et fusionner les entrées via le fichier .py sans perdre les sorties. Lorsque vous ouvrez le bloc-notes, il utilise le fichier .py pour les cellules d'entrée et le fichier .ipynb pour la sortie. Et si vous voulez inclure la sortie dans git, vous pouvez simplement ajouter ipynb.
Les réponses très populaires de 2016 ci-dessus sont des hacks incohérents par rapport à la meilleure façon de le faire en 2019.
Plusieurs options existent, le meilleur qui réponde à la question est Jupytext.
Capturez le article de Towards Data Science sur Jupytext
La façon dont cela fonctionne avec le contrôle de version consiste à placer les fichiers .py et .ipynb dans le contrôle de version. Regardez le .py si vous voulez le diff d'entrée, regardez le .ipynb si vous voulez la dernière sortie rendue.
Mentions remarquables: VS studio, nbconvert, nbdime, hydrogène
Je pense qu'avec un peu plus de travail, VS studio et/ou hydrogène (ou similaire) deviendront les principaux acteurs de la solution à ce flux de travail.
Malheureusement, je ne connais pas grand chose à Mercurial, mais je peux vous donner une solution qui fonctionne avec Git, dans l’espoir de pouvoir traduire mes commandes Git en leurs équivalents Mercurial.
Pour l’arrière-plan, dans Git, la commande add
enregistre les modifications apportées à un fichier dans une zone de stockage intermédiaire. Une fois que vous avez fait cela, toutes les modifications ultérieures apportées au fichier sont ignorées par Git, à moins que vous ne lui demandiez de les mettre également en scène. Par conséquent, le script suivant, qui, pour chacun des fichiers donnés, supprime tous les outputs
et Prompt_number sections
, met en forme le fichier extrait, puis restaure l'original:
NOTE: Si cela vous génère un message d'erreur comme ImportError: No module named IPython.nbformat
, utilisez ipython
pour exécuter le script à la place de python
.
from IPython.nbformat import current
import io
from os import remove, rename
from shutil import copyfile
from subprocess import Popen
from sys import argv
for filename in argv[1:]:
# Backup the current file
backup_filename = filename + ".backup"
copyfile(filename,backup_filename)
try:
# Read in the notebook
with io.open(filename,'r',encoding='utf-8') as f:
notebook = current.reads(f.read(),format="ipynb")
# Strip out all of the output and Prompt_number sections
for worksheet in notebook["worksheets"]:
for cell in worksheet["cells"]:
cell.outputs = []
if "Prompt_number" in cell:
del cell["Prompt_number"]
# Write the stripped file
with io.open(filename, 'w', encoding='utf-8') as f:
current.write(notebook,f,format='ipynb')
# Run git add to stage the non-output changes
print("git add",filename)
Popen(["git","add",filename]).wait()
finally:
# Restore the original file; remove is needed in case
# we are running in windows.
remove(filename)
rename(backup_filename,filename)
Une fois que le script a été exécuté sur les fichiers dont vous voulez valider les modifications, exécutez simplement git commit
.
J'utilise une approche très pragmatique; qui fonctionnent bien pour plusieurs cahiers, à plusieurs côtés. Et cela me permet même de "transférer" des cahiers. Cela fonctionne aussi bien pour Windows que pour Unix/MacOS.
Al a pensé que c’était simple, c’est résoudre les problèmes ci-dessus ...
Fondamentalement, ne pas suivre les fichiers .ipnyb
-, uniquement les fichiers .py
- correspondants.
En démarrant le notebook-server avec l'option --script
, ce fichier est automatiquement créé/enregistré lors de l'enregistrement du notebook. .
Ces fichiers .py
- contiennent toutes les entrées; les non-codes sont enregistrés dans les commentaires, de même que les limites de cellules. Ce fichier peut être lu/importé (et glissé) dans le serveur de cahiers pour (re) créer un cahier. Seule la sortie est parti; jusqu'à ce qu'il soit relancé.
Personnellement, j'utilise Mercurial pour suivre les versions de fichiers .py
; et utilisez les commandes normales (ligne de commande) pour ajouter, check-in (ect) pour cela. La plupart des autres (D) VCS le permettront.
Il est simple de suivre l’histoire maintenant; les .py
sont petits, textuels et simples à différencier. De temps en temps, nous avons besoin d'un clone (branchez-le simplement; démarrez un deuxième cahier-serveur), ou d'une version plus ancienne (extraction et importation dans un serveur de cahier), etc.
--script
) et faites le suivi des versions.py
-, mais ne l'enregistre pas . file@date+rev.py
) devrait être utile. Il serait trop compliqué d’ajouter cela; et peut-être que je le ferai une fois. Jusqu'à présent, je le fais simplement à la main.J'ai construit le package python qui résout ce problème
https://github.com/brookisme/gitnb
Il fournit une interface de ligne de commande avec une syntaxe inspirée de git pour suivre/mettre à jour/des cahiers de notes dans votre référentiel git.
Heres 'un exemple
# add a notebook to be tracked
gitnb add SomeNotebook.ipynb
# check the changes before commiting
gitnb diff SomeNotebook.ipynb
# commit your changes (to your git repo)
gitnb commit -am "I fixed a bug"
Notez que la dernière étape, dans laquelle j'utilise "gitnb commit", consiste à valider votre dépôt Git. C'est essentiellement une enveloppe pour
# get the latest changes from your python notebooks
gitnb update
# commit your changes ** this time with the native git commit **
git commit -am "I fixed a bug"
Il existe plusieurs méthodes supplémentaires, et peut être configuré de manière à nécessiter plus ou moins la participation de l'utilisateur à chaque étape, mais c'est l'idée générale.
Pour faire suite à l’excellent script de Pietro Battiston, si vous obtenez une erreur d’analyse Unicode comme celle-ci:
Traceback (most recent call last):
File "/Users/kwisatz/bin/ipynb_output_filter.py", line 33, in <module>
write(json_in, sys.stdout, NO_CONVERT)
File "/Users/kwisatz/anaconda/lib/python2.7/site-packages/IPython/nbformat/__init__.py", line 161, in write
fp.write(s)
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2014' in position 11549: ordinal not in range(128)
Vous pouvez ajouter au début du script:
reload(sys)
sys.setdefaultencoding('utf8')
Après avoir fouillé, j’ai finalement trouvé ce crochet de pré-sauvegarde relativement simple sur la documentation Jupyter . Il supprime les données de sortie de la cellule. Vous devez le coller dans le fichier jupyter_notebook_config.py
(voir les instructions ci-dessous).
def scrub_output_pre_save(model, **kwargs):
"""scrub output before saving notebooks"""
# only run on notebooks
if model['type'] != 'notebook':
return
# only run on nbformat v4
if model['content']['nbformat'] != 4:
return
for cell in model['content']['cells']:
if cell['cell_type'] != 'code':
continue
cell['outputs'] = []
cell['execution_count'] = None
# Added by binaryfunt:
if 'collapsed' in cell['metadata']:
cell['metadata'].pop('collapsed', 0)
c.FileContentsManager.pre_save_hook = scrub_output_pre_save
Si vous ne savez pas dans quel répertoire trouver votre fichier
jupyter_notebook_config.py
, vous pouvez taperjupyter --config-dir
[dans la commande Invite/terminal], et si vous n'y trouvez pas le fichier, vous pouvez le créer. en tapantjupyter notebook --generate-config
.
J'ai fait ce qu'Albert et Rich ont fait: ne pas utiliser la version .ipynb (car ceux-ci peuvent contenir des images, ce qui devient désordonné). Au lieu de cela, exécutez toujours ipython notebook --script
ou insérez c.FileNotebookManager.save_script = True
dans votre fichier de configuration afin qu'un fichier (versionable) .py
soit toujours créé lorsque vous enregistrez votre cahier.
Pour régénérer les cahiers (après avoir extrait un dépôt ou changé de branche), je mets le script py_file_to_notebooks.py dans le répertoire où je stocke mes cahiers.
Maintenant, après avoir extrait un dépôt, lancez simplement python py_file_to_notebooks.py
pour générer les fichiers ipynb. Après avoir changé de branche, vous devrez peut-être exécuter python py_file_to_notebooks.py -ov
pour écraser les fichiers ipynb existants.
Juste pour être sûr, il est bon d’ajouter également *.ipynb
à votre fichier .gitignore
.
Edit: je ne le fais plus parce que (A) vous devez régénérer vos cahiers à partir de fichiers py chaque fois que vous extrayez une branche et (B) il y a d’autres choses comme le démarquage dans les cahiers que vous perdez. Au lieu de cela, je supprime la sortie des ordinateurs portables en utilisant un filtre git. La discussion sur la façon de procéder est ici .
Ok, il semble donc que la meilleure solution actuelle, selon une discussion ici , consiste à créer un filtre git pour supprimer automatiquement la sortie des fichiers ipynb lors de la validation. .
Voici ce que j'ai fait pour que cela fonctionne (copié de cette discussion):
J'ai légèrement modifié le fichier nbstripout de cfriedline pour donner une erreur informative lorsque vous ne pouvez pas importer le dernier IPython: https://github.com/petered/plato/blob/fb2f4e252f50c79768920d0e7b870a8d799e92b/booksf/bookf/config/ strip_notebook_output Et l'a ajouté à mon référentiel, disons dans ./relative/path/to/strip_notebook_output
Également ajouté le fichier .gitattributes à la racine du référentiel, contenant:
*.ipynb filter=stripoutput
Et créé un setup_git_filters.sh
contenant
git config filter.stripoutput.clean "$(git rev-parse --show-toplevel)/relative/path/to/strip_notebook_output"
git config filter.stripoutput.smudge cat
git config filter.stripoutput.required true
Et a lancé source setup_git_filters.sh
. La bonne chose $ (git rev-parse ...) est de trouver le chemin local de votre repo sur n'importe quelle machine (Unix).
Cette extension jupyter permet aux utilisateurs de pousser des blocs-notes jupyter directement dans github.
S'il vous plaît regardez ici
Que diriez-vous de l’idée évoquée dans l’article ci-dessous, où le résultat du cahier devrait être conservé, avec l’argument selon lequel sa génération risque d’être longue, et c’est pratique puisque GitHub peut désormais rendre les cahiers. Des crochets de sauvegarde automatique ont été ajoutés pour exporter le fichier .py, utilisés pour les diffs et .html pour le partage avec les membres de l'équipe qui n'utilisent pas de portables ou de git.
https://towardsdatascience.com/version-control-for-jupyter-notebook-3e6cef13392d