git-bug guarda tus issues en Git y enseña a usar los refs como base de datos
El tracker distribuido ha llegado a la portada de Hacker News y deja una idea aprovechable: Git ya es un almacén direccionable por contenido con replicación incorporada

git-bug es un bug tracker distribuido que guarda los issues dentro del propio repositorio Git: sin servidor, sin base de datos y sin sincronizar nada contra Jira. Esta semana ha llegado a la portada de Hacker News, y lo interesante no es el tracker en sí, sino la técnica que usa. Git admite refs personalizados, y con ellos se puede almacenar cualquier cosa —metadatos de CI, notas de revisión, logs de auditoría, configuración de despliegue— pegada a un commit y viajando con el push como el código.
Por debajo hay cuatro objetos
Conviene olvidarse un momento de git add y git commit. En la capa de abajo Git solo tiene blob (contenido de archivo), tree (lista de archivos), commit (tree, parent y mensaje) y tag. Cada objeto se identifica por el hash de su propio contenido, SHA-1 o SHA-256 si el repositorio se creó con --object-format=sha256. Un ref no es más que un archivo de texto con un hash dentro: refs/heads/main es una rama y refs/tags/v1.0 es una etiqueta, pero nada obliga a que el ref viva en heads o en tags. Un refs/issues/123 es igual de válido, no aparece en git branch ni ensucia el working tree, y aun así se empuja, se descarga y entra en el recolector de basura como cualquier otro objeto.
git-bug lo explota con un ref por issue, refs/bugs/<id>. Cada cambio —crear el bug, comentar, etiquetar— genera un commit nuevo con las operaciones en JSON y el commit anterior como parent. Cada issue es un log de eventos inmutable, y Git se come el almacenamiento, la deduplicación y la sincronización. Para saber el estado actual se replican los eventos desde el principio: event sourcing con un motor de almacenamiento que todo el mundo tiene instalado.
Montarlo a mano son cuatro comandos de plumbing, sin tocar el índice:
BLOB=$(echo '{"op":"create"}' | git hash-object -w --stdin)
TREE=$(printf '100644 blob %s\tops.json\n' "$BLOB" | git mktree)
COMMIT=$(git commit-tree "$TREE" -m "create issue")
git update-ref refs/issues/login-timeout "$COMMIT"
El tercer argumento de update-ref merece atención: es un compare-and-swap. Si otro proceso movió el ref mientras tanto, el comando falla en lugar de machacar el cambio ajeno. Bloqueo optimista sin escribir una línea extra.
Sincronizar y el problema del merge
Para que esos refs viajen hay que declarar los refspec a mano, porque por defecto Git solo empuja y trae heads y tags: git push origin 'refs/issues/*:refs/issues/*' y el fetch equivalente, o dejarlo fijo con remote.origin.fetch. GitHub, GitLab y Gitea aceptan refs personalizados, solo que no los muestran en la interfaz, y GitHub reserva espacios como refs/pull/*, así que conviene elegir un nombre propio que no choque.
La parte difícil es otra. Si dos personas editan el mismo issue sin conexión, sus refs divergen. Con código, Git resuelve el merge línea a línea; con un log de eventos esa semántica no existe y la política de fusión hay que definirla. Es el punto que el autor deja abierto.
La utilidad va más allá de un tracker: metadatos de CI, notas de revisión o registros de auditoría atados a un commit y replicados con el repositorio, sin añadir infraestructura. La pregunta pendiente es qué reglas de merge hacen falta antes de meter ahí datos que importen.


