Principe RS-7 : « dès qu'on build un truc, plus personne n'a à le rebuild —
la seule chose à faire est l'optimisation. » (nexus/RepoVerse)
- bion-vc : horloges vectorielles + MvReg fork-visible (LA spec
xion-relativiste-v0 enfin codée — CRDT testé par permutations)
- bion-triplet : l'Adressage Génératif (gen_hash BLAKE3, coords, résidu ;
résidu vide quand déjà-su ; align décidable au bit)
- bion-tsoinlog: journal append-only rejouable (CRC32 maison, crash-recovery)
- bion-kv : magasin clé-valeur bitcask (compaction atomique, tombstones)
- bion-regex : moteur Thompson NFA linéaire (jamais exponentiel — Russ Cox)
- bion-git : mini-git content-addressed (SHA-1 maison + vecteurs officiels,
branches divergentes = le fork visible)
129 tests verts, clippy 0 warning, doc française = chaque bion est un cours.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.0 KiB
bion-git — mini-git from scratch, le bion du principe « fork = liberté »
Quoi
Le noyau conceptuel de git, réimplémenté depuis les principes en Rust std-only,
zéro dépendance, zéro unsafe — même le SHA-1 est maison (src/sha1.rs, ~90
lignes, vérifié contre les vecteurs officiels du NIST) :
- magasin d'objets content-addressed :
blob/tree/commit, nommés par le SHA-1 de leur contenu, stockés dans.bgit/objects/ab/cdef…(2 caractères de répertoire / 38 de fichier, comme git) ; - instantanés :
write_treephotographie un répertoire,checkout_treele rematérialise n'importe où ; - histoire : commits chaînés (0, 1 ou N parents — le merge est
représentable),
logen remontant le premier parent ; - branches : 41 octets dans
refs/heads/<nom>— forker est gratuit.
Les ids de blobs et trees sont bit-à-bit ceux du vrai git (testé contre
git hash-object et git write-tree, tri des entrées compris).
Pourquoi
C'est le principe RS-7 fait structure de données : « dès qu'on build un truc, plus personne n'a à le rebuild — des blocs, la seule chose à faire est l'optimisation ». Le content-addressing garantit exactement ça : un contenu déjà construit porte un nom absolu, universel, immuable — le rebuild est impossible par construction, on ne peut que référencer ou optimiser.
Le lien avec le xerboxion : une branche est un chemin dans l'espace des
états ; un fork une bifurcation visible. Deux branches divergentes partagent
tout leur passé commun sans copier un octet — la divergence n'écrase rien, ne
cache rien : elle existe dans le graphe, on peut la montrer, la comparer, la
refermer par un merge commit tracé. C'est la sémantique gelée par RS-7
(« divergence = branche, jamais d'écrasement silencieux » — voir bion-vc
pour son pendant causal/CRDT) et le socle du nexus/RepoVerse : contribution
= repo, reprise = fork, réplication = pull des ids manquants.
Exemple
use bion_git::Repo;
let repo = Repo::init(dir)?; // .bgit/
let tree = repo.write_tree(dir)?; // photographier
let racine = repo.commit(tree, &[], "premier instant", "rs-1")?;
repo.branch("main", racine)?;
repo.branch("fork", racine)?; // forker = 41 octets
// diverger…
let cm = repo.commit(tree2, &[racine], "chemin main", "rs-1")?;
let cf = repo.commit(tree3, &[racine], "bifurcation", "rs-7")?;
repo.branch("main", cm)?;
repo.branch("fork", cf)?;
for c in repo.log(repo.branch_target("fork")?)? { // l'histoire
println!("{} {} — {}", c.id, c.author, c.message);
}
repo.checkout_tree(&tree, dest)?; // rematérialiser
API complète : Repo::{init, open, hash_object, cat_object, write_tree, read_tree, checkout_tree, commit, commit_at, read_commit, log, branch, branch_target, branches} + Id, Kind, TreeEntry, Commit,
sha1::{sha1, to_hex}. Cette API est stable pour toujours (dogme des
bions) : on peut lui ajouter, jamais lui retirer.
Écarts assumés avec le vrai git
| Écart | Pourquoi |
|---|---|
| Objets non compressés (pas de zlib) | std-only et lisible ; git compresse en deflate après le hash, donc les ids restent identiques — seul l'octet sur disque diffère. C'est LE point d'optimisation invité (voir plus bas). |
Commits : auteur = chaîne libre, fuseau figé +0000, committer = copie d'author |
même format textuel que git, mais sans imposer Nom <email> ; les ids de commits ne coïncident avec git que si on reproduit son format exact |
| Pas d'index/staging | write_tree photographie le répertoire directement |
| Pas de packfiles, refs non packées | un objet = un fichier, un ref = un fichier |
Modes 100644 et 40000 seulement |
ni exécutables ni symlinks (refus explicite) |
log = premier parent |
le graphe complet est dans Commit::parents, à l'appelant d'explorer |
build-your-own-x
Correspond au chapitre « Build your own Git » de codecrafters-io/build-your-own-x :
- Write yourself a Git! (thblt) — la référence dont ce bion suit le périmètre ;
- Git Internals — Plumbing and Porcelain (Pro Git, ch. 10) — le format d'objets ;
- CodeCrafters « Build your own Git » — mêmes étapes (init, hash-object, cat-file, write-tree, commit-tree).
Et pour le SHA-1 : FIPS 180-1 (NIST, 1995) — l'implémentation suit la spec pas à pas, commentée en français.
Comment l'optimiser (l'invitation au fork)
Le contrat est l'API + le format d'objets ; tout le reste est forkable :
- zlib/deflate : compresser les objets à l'écriture (ids inchangés !) — c'est l'écart n°1 avec git, et un « build your own zlib » pédagogique en soi ;
- packfiles + delta-encoding : regrouper N objets en un fichier, encoder les versions successives en deltas — le vrai gain d'espace de git ;
- SHA-256 : brancher un second hash (git fait sa propre migration) ;
- index/staging : un cache
(chemin, mtime, id)pour ne pas re-hasher les fichiers inchangés à chaquewrite_tree; - transport :
pull(remote)= énumérer les ids manquants et les copier — la réplication pull/apply du xerboxion tombe naturellement du content-addressing ; - merge de trees : diff3 entre deux trees et leur ancêtre commun, avec
conflits visibles (jamais d'arbitrage silencieux — brancher
bion-vc).
Tests
cargo test -p bion-git — 15 tests unitaires + 2 doc-tests : vecteurs SHA-1
officiels (dont le million de « a »), ids identiques au vrai git (blobs, tree
vide, tree imbriqué vérifié contre git write-tree), roundtrips
blob/tree/commit, log d'une chaîne, fork de deux branches divergentes depuis
un parent commun, merge à deux parents, détection de corruption
(le magasin est auto-vérifiant), refus des traversées de chemin et de
l'injection dans le format commit.