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>
117 lines
6.0 KiB
Markdown
117 lines
6.0 KiB
Markdown
# 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_tree` photographie un répertoire, `checkout_tree`
|
|
le rematérialise n'importe où ;
|
|
- **histoire** : commits chaînés (0, 1 ou N parents — le merge est
|
|
représentable), `log` en 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
|
|
|
|
```rust
|
|
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](https://github.com/codecrafters-io/build-your-own-x#build-your-own-git) :
|
|
|
|
- *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 :
|
|
|
|
1. **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 ;
|
|
2. **packfiles + delta-encoding** : regrouper N objets en un fichier, encoder
|
|
les versions successives en deltas — le vrai gain d'espace de git ;
|
|
3. **SHA-256** : brancher un second hash (git fait sa propre migration) ;
|
|
4. **index/staging** : un cache `(chemin, mtime, id)` pour ne pas re-hasher
|
|
les fichiers inchangés à chaque `write_tree` ;
|
|
5. **transport** : `pull(remote)` = énumérer les ids manquants et les copier —
|
|
la réplication pull/apply du xerboxion tombe naturellement du
|
|
content-addressing ;
|
|
6. **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.
|