- Para entender la estructura interna de un sistema de control de versiones, implementó directamente un sistema similar a Git
- Usa hashes SHA-256 y compresión zstd en lugar de SHA-1 y zlib de Git, y organiza el repositorio con una estructura de directorios
.tvc
- Está escrito en Rust, e implementa paso a paso funciones de hash de archivos, compresión, commit y checkout
- El objeto commit incluye el hash del árbol, el commit padre, el autor y el mensaje, y los archivos idénticos no se vuelven a guardar gracias a la deduplicación por hash
- Permite experimentar directamente que Git es un almacén de archivos basado en direccionamiento por contenido, y destaca la importancia de los formatos de datos estructurados
Método de hashing y compresión
- Git identifica todos los objetos con hashes SHA-1, pero en este proyecto se usa SHA-256
- SHA-1 es antiguo y tiene debilidades de seguridad, pero en este proyecto solo se usa para identificar el contenido de archivos, así que la seguridad no era importante
- En lugar de zlib de Git, adoptó la biblioteca de compresión zstd de Facebook
- Consideró que zstd era más eficiente, y la compatibilidad con Git no era un objetivo
- El nombre del proyecto es “tvc (Tony’s Version Control)”, y usa los archivos
.tvc y .tvcignore como equivalentes de la estructura correspondiente en Git
Etapas de implementación
- El proceso de implementación sigue el orden leer argumentos del comando → leer reglas de exclusión → mostrar la lista de archivos → hashear y comprimir → crear árbol y commit → gestionar HEAD → hacer checkout del commit
- Está escrito en Rust, y el comando
ls aplica las reglas de .tvcignore para recorrer recursivamente los archivos no ignorados y mostrar el hash SHA-256 de cada uno
- Con la biblioteca zstd implementó de forma sencilla funciones de compresión y descompresión de archivos
Estructura del commit
- El objeto commit incluye la siguiente información
- Tipo de objeto (“commit”)
- Estado del sistema de archivos en ese momento (hash del árbol)
- Commit anterior (HEAD)
- Autor (author)
- Mensaje del commit
- A diferencia de Git, omite la distinción entre autor y committer, y no implementa funciones como merge o rebase
- Al crear un commit, genera, hashea y comprime el objeto árbol, lo guarda en
.tvc/objects/ y actualiza el archivo HEAD
- Si un archivo idéntico tiene el mismo hash, no se vuelve a guardar, lo que permite evitar almacenamiento duplicado
Objetos árbol y checkout
- La función
generate_tree() recorre directorios, hashea, comprime y guarda cada archivo, y construye una cadena con el nombre del archivo y su hash
- Los subdirectorios se procesan recursivamente para formar la estructura de árbol
- Los objetos commit y árbol se parsean como estructuras (
Commit, Tree) para poder manejarlos fácilmente en memoria
- La función
generate_fs() reconstruye el sistema de archivos con base en la estructura de árbol y realiza el checkout en la ruta especificada
Lecciones del proyecto
- Permitió experimentar de primera mano que Git es un almacén de archivos basado en contenido direccionable (clave-valor)
- La parte más difícil fue el parseo del formato de objetos, y la próxima vez planea usar un formato más claro como YAML o JSON
- El código completo está publicado en el repositorio de GitHub (tonystr/t-version-control)
1 comentarios
Opiniones en Hacker News
Es interesante que Git sea el único SCM que soporta la estrategia de merge recursivo
Este método recuerda automáticamente resoluciones de conflictos pasadas, así que es muy útil
Mucha gente todavía prefiere rebase, pero al implementar merge, hay que incluir sí o sí un mecanismo para guardar el historial de resolución de conflictos
Referencia relacionada: Merge made by recursive strategy
Referencia: Git Tools - Rerere
Enlace
git mergeno tiene una estrategia “null”Incluso cuando ya resolviste los conflictos y solo quieres dejar registro del merge, Git intenta ayudarte de todos modos
Ojalá existiera una opción para simplemente registrar el merge sin tocar el índice ni el working tree
Por ejemplo, Pijul hace eso
No puedes ver los intentos de varios commits, es más difícil revertirlo y cuesta seguir trabajando sobre una rama que ya fue mergeada
Cuando varios PR son piezas de un mismo rompecabezas, me parece mucho mejor hacer merge normal
Siempre es divertido aprender el funcionamiento interno de una herramienta que usas todos los días
En especial, Git from the Bottom Up es un texto excelente que explica claramente la estructura interna de Git
En unos 20 minutos puedes entender el mecanismo opaco detrás de los comandos de Git
cat-filepuedes inspeccionar directamente IDs hash, y está bastante genialSi te da curiosidad cómo planifican los agentes de código, este tipo de textos forma parte de sus datos de entrenamiento
Aunque si el autor recibió ayuda de un LLM, entonces podría volverse una situación recursiva
Parece que sí existen bots que raspan repositorios públicos
Es una sensación extraña pensar que mi código podría usarse para entrenar LLM
El texto en sí no tiene salida de LLM, pero sí usé ChatGPT para consultar convenciones de código en Rust o consejos comparando algoritmos
El tutorial CodeCrafters “Build your own Git” es realmente excelente
También recomiendo el video en vivo de Jon Gjengset, donde lo implementa directamente en Rust
Yo también quisiera que el control de versiones se usara mucho más fuera del software
GotVC es un proyecto interesante con cifrado E2E, importación paralela y una estructura compatible con archivos grandes
Al final tienes que abrirlas en el programa original para compararlas
Este texto me hizo pensar en ugit: DIY Git in Python
Es uno de los mejores recursos para profundizar en el interior de Git sin dejar de ser fácil de seguir
Sapling VCS, el fork de Mercurial de Meta, usa compresión con diccionario Zstd
En la documentación explicativa se puede comparar con el packfile comprimido por delta de Git
En repositorios pequeños, la compresión delta de Git es más eficiente, pero en repositorios grandes es mejor la compresión con diccionario basada en rutas
Hace poco Git también añadió una función similar llamada “path-walk”
Yo también hice un intento parecido, y mi proyecto se llama “shit”
Enlace de GitHub
Hace tiempo intenté hacer un framework SPA y recuerdo haberme sorprendido por la complejidad oculta
Imagino que los desarrolladores de React o Angular también deben haber pasado por esa madriguera de conejo
Git también oculta muy bien su complejidad
Vi un cliente de Git escrito en PHP que puede leer packfile y reftable, y también soporta diff basado en LCS
gipht-horse
Y además hoy me enteré de que puedes usar
@en lugar de HEAD, lo cual sintácticamente tiene bastante sentido