Réécrire l'histoire pour l'archiver

Je vais commencer par une petite note méta, qui explique le titre.

Je connais depuis le principe du « backup de Schrödinger », selon lequel une sauvegarde n'existe pas vraiment tant qu'on a pas essayé de la restaurer. Ou, dit autrement, il ne suffit pas de faire des actions qui sont censées sauvegarder, il faut essayer d'utiliser ces sauvegardes avant de considérer leur rôle comme rempli.

Je suis ces jours-ci en cours de découverte de la transposition de ce principe aux archives. Garder un tas d'informations, ce n'est pas vraiment une archive tant qu'on n'a pas vérifié qu'on peut effectivement utiliser ce tas d'information pour l'interroger et répondre à des questions sur le passé. Je sais depuis longtemps qu'archiviste c'est un vrai métier, je suis seulement en train d'en tirer toutes les conséquences.

J'imagine que c'est parce qu'au fil des années, le tas d'informations que je garde « au cas où » ne cesse de croître, et d'être donc plus difficile à exploiter, tandis que ma mémoire est de moins en moins apte à remplacer une organisation pertinente de ce tas d'information.

Dans l'exemple qui va occuper le reste de ce billet, je vais prendre le code que j'écris pour résoudre les exercices de l'Advent of Code.

Cet exemple est un peu nul par rapport aux réflexions ci-dessus, parce que j'utilise l'Advent of Code comme un prétexte pour assimiler un nouveau paradigme de programmation, ou à défaut de nouveaux concepts de programmation, ou à défaut un nouveau langage de programmation. Donc j'en tire plus un savoir-faire et une intuition que des choses qui sont vraiment utiles à revisiter.

En plus dans le reste ce billet, je ne vais pas développer ces considérations, je vais juste mettre les mains dans le cambouis en dessous de git. Mon lectorat non-technique peut donc sereinement arrêter la lecture de ce billet ici.

La fusion de dépôt

J'ai commencé à pratiquer l'Advent of Code en 2021, et l'année 2024 sera ma quatrième saison. En 2022, pour des raisons que j'ai complètement oubliées, j'ai choisi de faire un nouveau dépôt, complètement indépendant de ce que j'ai écrit en 2021. J'imagine que le fait d'avoir rempli le dépôt 2021 comme si c'était le seul Advent of Code que je ferais de ma vie a contribué à lancer cette structure d'un dépôt par an.

En 2023, je n'étais pas du tout satisfaite de ce choix, et c'est à reculons que j'ai créé un troisième dépôt.

Je crois que c'est 2024 que j'ai constaté le chaos pénible de ne plus savoir sur quels ordinateurs se trouvent quelles années, et j'ai eu la preuve par la pratique que ce n'est pas une « bonne » accumulation de données et qu'une « vraie » archive n'en a pas émergé.

Alors je me suis reprise en main, et j'ai décidé de préparer l'Advent of Code 2024 en construisant un nouveau dépôt, qui contient rétroactivement ce que j'ai fait dans le passé, et qui contiendra ce que je ferai dans le futur.

Comme j'imagine à quel point git permet de touiller l'historique dans tous les sens, et d'autant plus depuis que dans le cadre de pashage j'ai cherché comment modifier rétroactivement une clé cryptographique, je me suis dit : « Bah, ça ne doit pas être bien compliqué… »

Famous last words, comme on dit.

Plus concrètement, je cherche donc à construire un dépôt qui contient les mêmes commits que mon dépôt de 2022 et que mon dépôt de 2023, chacune dans son sous-répertoire, et éventuellement en enlevant les trucs qui n'ont plus d'intérêt dans le dépôt fusionné, comme le fichier LICENSE.

Je n'inclus pas le dépôt de 2021 parce qu'il est beaucoup trop fortement couplé à un outil propriétaire sur mon lieu de travail, et son intérêt me semble extrêmement douteux, aussi bien pour moi‐du‐futur que pour le reste du monde.

Le graphe orienté acyclique

C'est loin d'être évident pour tout le monde, mais je l'ai assimilé il y a tellement longtemps que je ne sais plus trop comment je le vivais avant, mais il me semble que pour utiliser efficacement git il faut le considérer comme un outil de manipulation d'un graphe orienté acyclique (ou DAG) dont les nœuds sont les commits.

Mon interprétation personnelle, mais je pèche peut-être par manque de charité, est que git est tellement mal foutu que c'est une abstraction pleine de fuites, au-dessus de cette structure de données.

Donc après un nettoyage basique de chacun des deux dépôts, l'opération principale va être une fusion de ces deux dépôts, qui va résulter en un graphe à deux composantes connexes indépendantes. L'opération suivante devra être la greffe de la racine du sous-graphe de 2023 sur la plus ancienne feuille du sous-graphe de 2022, pour revenir à un graphe connexe qui ressemble à un historique « normal » que l'on peut manipuler avec les commandes habituelles.

Sous le capot de git

Au risque de divulgâcher, la grande leçon que j'ai retenue de toute cette histoire, c'est que je n'étais pas rentré assez loin dans le détail de ce que sont exactement les commits qui servent de nœuds au graphe de git.

J'ai toujours considéré un commit comme étant intrinsèquement un changement, ou un patch, qui transforme l'état précédent du dépôt en l'état courant.

En réalité, quand on regarde de plus près l'organisation interne des objets de git, on voit qu'un commit c'est essentiellement un tree, c'est-à-dire le contenu d'un ensemble de fichiers, avec un jeu de méta-données autour.

Donc ce que je prenais pour un commit est en réalité une arrête du graphe (dans le cas facile mais fréquent où le commit n'a qu'un seul parent).

J'aurais dû le savoir, puisque je connaissais déjà pijul, dont le pitch est précisément de manipuler des patchs et non pas des états.

Donc en particulier, si je me contente d'utiliser la plomberie de git pour ajouter simplement une arrête d'un sous-graphe à un autre, sans changer le tree auquel il fait référence, j'ajoute implicitement dans mon nœud racine greffé la suppression complète des fichiers de la feuille sur laquelle il est greffé.

Si vous voulez essayer chez vous, les commandes pour ce faire sont documentées dans git-filter-branch :

git replace --graft $FIRST_2023_ID $LAST_2022_ID
git filter-branch $LAST_2022_ID $LAST_2023_ID

Il y a bien une opération git qui « applique des patchs », et qui représenterait la connexion de l'historique de 2023 à la suite de l'historique de 2022 : git-rebase. Sauf qu'un rebase ne peut se faire que dans un dépôt avec un checkout, alors j'avais fait toutes mes expérimentations dans un dépôt bare, pour justement ne pas avoir à me soucier de l'état d'un checkout.

J'imagine qu'on doit pouvoir le faire en restant au niveau de la plomberie, en utilisant le fait qu'un tree c'est en réalité un répertoire, donc je n'ai pas besoin de me préoccuper de tous les fichiers, je peux juste concaténer le tree à la base de 2022 et le tree à la base de 2023. J'ai lâchement abandonné parce que j'arrive au bout du temps avant le début de l'[Advent of Code][].

Filtrage de branche et de dépôt

Pour le reste du nettoyage, j'ai utilisé beaucoup d'options de git-filter-branch, malgré l'immense section “SAFETY” et la recommandation d'utiliser git-filter-repo, parce que je n'ai pas du tout envie de me gérer une dépendance supplémentaire.

J'imagine que le fait d'avoir un bête historique linéaire évite plein de problèmes, et les opérations que je veux faire sont tellement simples qu'elles se retrouvent directement dans les exemples :

Chaque appel à git-filter-branch génère une sauvegarde de la référence originale, pour repêcher le sous-graphe avant modification et pouvoir annuler la commande, ce qui empêche de les enchaîner. J'ai été agréablement surprise de trouver une variante de git update-ref qui efface une référence après avoir vérifié qu'elle pointe bien sur le nœud auquel on s'attend.

Archive et partage des commandes

J'ai rassemblé toutes ces modifications de dépôts dans un script que j'ai ajouté au dépôt fusionné, pour pouvoir retrouver tout ça si un jour j'en ressens le besoin.

Ce qui me permet de revenir au sujet initial, sur la différence entre un tas de données qu'on garde et une archive. Je garde un historique de la totalité des commandes que j'ai entrées dans un terminal, et j'arrive souvent à trouver ce que je cherche en combinant ma mémoire et une capacité à construire des expressions régulières pertinentes.

Dans les cas très itératifs, comme ici, ou pour partager avec d'autres gens, je construis un script comme celui que j'ai lié au début de cette section. Pour situer, j'ai lancé 25 fois une version ou une autre de ce script, 31 fois git-filter-branch en dehors du script, et 6 fois git-replace en dehors du script.

Autant mon historique perpétuel est un bon tas de données que j'arrive (encore) à interroger assez facilement pour retrouver des commandes individuelles ou des séquences brèves (je dirais trois ou quatre commandes), autant l'enchaînement ici mérite un script pérenne, même s'il n'est lancé « pour de vrai » qu'une seule fois.

Il n'aura pas échappé au lecteur assidu qu'une faute de frappe malheureuse s'est glissée dans ce script, et que le fichier LICENSE existe encore dans le sous-répertoire 2023, parce que le script efface un hypothétique fichier LICENCE. C'est triste, mais j'ai trop construit dans ce dépôt fautif pour tout reprendre à zéro, et ça restera éternellement une verrue dans mon dépôt fusionné tout neuf.

Commentaires

Pas de commentaire pour le moment.

Poster un commentaire

Mise en forme historique : pour mettre en valeur un mot ou un groupe de mot, tapez une étoile de part et d'autre, sans mettre d'espace entre l'étoile et le mot, comme ceci : *mise en valeur*. Pour insérer un lien, mettez le entre crochets, comme ceci : [http://instinctive.eu/].

Mise en forme markdown : voir le guide détaillé, en résumé c'est le Markdown traditionnel, sans HTML ni titres, mais avec les tables PHP-Markdown-Extra.

Attention : les balises HTML entrées dans les commentaires seront affichées telles quelles, et non pas interprétées.

Autour de cette page

 

Autour de cet article

  • Publié le 30 novembre 2024 à 22h33
  • État de la bête : embrouillée
  • Pas de commentaire
  • Tag : Évènement
  • Tag : Geek

Weblog

Derniers commentaires

Tags

Archives

Site-level navigation and features

 

Instinctive.eu

Contact

Sections

Validation

Copyright © 2008-2024 Natacha Kerensikova

Butterfly images are copyright © 2008 Shoofly