Rendre chaque changement réversible
Annuler, pour un agent, c’est enregistrer ce que chaque écriture a remplacé, afin de remettre les fichiers à n’importe quelle étape. Un agent se trompe à l’étape sept, et l’erreur n’apparaît qu’à l’étape neuf.
Avec un journal des changements, on rembobine à l’étape six. Sans journal, on lit un diff et on devine.
À la fin de cette leçon
Remarque
Je peux enregistrer chaque changement d’une exécution, pour rembobiner les fichiers à n’importe quelle étape.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Instantané | une copie d’un fichier avant qu’il change |
| Journal des changements | la liste de tous les changements, dans l’ordre |
| Étape | une action numérotée d’une exécution |
| Annulation | remettre un fichier comme il était |
| Rembobiner | annuler tous les changements après une étape choisie |
| Texte d’avant | ce qu’un fichier contenait avant une écriture |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Annuler n’est pas reprendre
Dans le module « Construire des agents », un point de reprise sauvegarde une exécution pour la reprendre après un arrêt. Cela avance. Annuler fait revenir les fichiers en arrière, comme ils étaient.
Ce ne sont pas les mêmes enregistrements. Un point de reprise garde l’état de l’agent. Un journal garde ce que chaque écriture a remplacé.
Noter le texte d’avant
Avant chaque écriture ou suppression, notez le numéro d’étape, le chemin et ce que le fichier contenait. S’il n’existait pas, notez-le aussi, comme un vide.
Pour annuler jusqu’à l’étape n, parcourez le journal de la fin jusqu’à l’étape n + 1. Remettez chaque texte d’avant. Si l’avant était un vide, retirez le fichier. La dernière d’abord, sinon deux modifications d’un fichier s’annulent dans le mauvais ordre.
Exemple détaillé : quatre étapes
| Étape | Action | Texte d’avant |
|---|---|---|
| 1 | Écrire app.py | x = 1 |
| 2 | Écrire neuf.py | rien : il n’existait pas |
| 3 | Supprimer notes.txt | a faire |
| 4 | Écrire app.py | x = 2 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur courante
Attention
Beaucoup croient que git seul suffit à annuler un agent. Git ne revient qu’à un commit. Il ne peut pas rembobiner à l’étape six d’une exécution qui n’a fait aucun commit, et il ignore les fichiers qu’il ne suit pas. Tenez votre propre journal de chaque écriture.
Écrire le journal des changements, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fichiers = { \"app.py\": \"x = 1\", \"notes.txt\": \"a faire\" };' 'const ops = [' ' { op: \"ecrire\", chemin: \"app.py\", texte: \"x = 2\" },' ' { op: \"ecrire\", chemin: \"neuf.py\", texte: \"y = 1\" },' ' { op: \"supprimer\", chemin: \"notes.txt\" },' ' { op: \"ecrire\", chemin: \"app.py\", texte: \"x = 3\" },' '];' 'function copie(f) { return Object.assign({}, f); }' > annuler5.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le journal des changements, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Avant chaque op, noter ce que le fichier contenait : son texte, ou null s’il n’existait pas.' 'function appliquer(depart, liste) {' ' const maintenant = copie(depart);' ' const journal = [];' ' liste.forEach(function (o, i) {' ' journal.push({ etape: i + 1, chemin: o.chemin, avant: o.chemin in maintenant ? maintenant[o.chemin] : null });' ' if (o.op === \"ecrire\") maintenant[o.chemin] = o.texte;' ' else delete maintenant[o.chemin];' ' });' ' return { fichiers: maintenant, journal: journal };' '}' >> annuler5.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le journal des changements, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Annuler les entrées après une étape, la dernière d’abord.' 'function annulerJusqua(actuel, journal, etape) {' ' const retour = copie(actuel);' ' for (const e of journal.slice().reverse()) {' ' if (e.etape <= etape) continue;' ' if (e.avant === null) delete retour[e.chemin];' ' else retour[e.chemin] = e.avant;' ' }' ' return retour;' '}' >> annuler5.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le journal des changements, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function montrer(titre, f) { console.log(titre + \" \" + JSON.stringify(f)); }' 'const serie = appliquer(fichiers, ops);' 'montrer(\"début\", fichiers);' 'montrer(\"après 4 étapes\", serie.fichiers);' 'montrer(\"annuler jusqu’à 3\", annulerJusqua(serie.fichiers, serie.journal, 3));' 'montrer(\"annuler jusqu’à 1\", annulerJusqua(serie.fichiers, serie.journal, 1));' 'montrer(\"annuler jusqu’à 0\", annulerJusqua(serie.fichiers, serie.journal, 0));' 'console.log(\"journal : \" + serie.journal.map(function (e) { return e.etape + \":\" + e.chemin; }).join(\" \"));' >> annuler5.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer et rembobiner
docker exec -w /home/node/project agent node annuler5.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Les fichiers sont un dictionnaire ici
Les fichiers vivent dans un dictionnaire du script, donc rien ne change sur le disque. Un vrai harnais écrit de vrais fichiers, et garde le journal dans un fichier ou un stockage à lui.
Le Run n’appelle aucun modèle, donc il n’a besoin d’aucun assistant et n’a rien à remplacer.
Sortie du Run
| Ligne | Ce qu’elle montre |
|---|---|
| début {"app.py":"x = 1","notes.txt":"a faire"} | Les fichiers avant la série |
| après 4 étapes {"app.py":"x = 3","neuf.py":"y = 1"} | La série a écrit, ajouté et supprimé |
| annuler jusqu’à 3 {"app.py":"x = 2","neuf.py":"y = 1"} | Seule l’étape 4 est annulée |
| annuler jusqu’à 1 {"app.py":"x = 2","notes.txt":"a faire"} | Les étapes 2 à 4 sont annulées |
| annuler jusqu’à 0 {"app.py":"x = 1","notes.txt":"a faire"} | Les fichiers comme au départ |
| journal : 1:app.py 2:neuf.py 3:notes.txt 4:app.py | Une entrée par étape |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.