Git suele tratarse como un problema de sistema de archivos: el directorio `.git` parece una carpeta, así que la forma natural de alojar Git en la nube es darle un sistema de archivos y poner ese sistema de archivos sobre almacenamiento de objetos. Un desarrollador lo intentó exactamente así y descubrió que no funcionaba para repositorios del mundo real. El resultado es objgit, un servidor Git de código abierto que almacena los objetos de Git directamente en almacenamiento de objetos, específicamente Tigris, y reemplaza la maquinaria de packfiles de Git con un formato personalizado diseñado para almacenamiento remoto direccionable por bytes.

Para entender por qué es necesario, conviene partir de lo que Git almacena realmente. Un repositorio es, en esencia, dos cosas: un mar de objetos y referencias con nombre que apuntan a objetos individuales dentro de él. Cada commit, árbol y blob de archivo es un objeto independiente, direccionado por su contenido y comprimido, cuyo nombre de archivo es el hash de sus bytes; el mismo contenido es siempre el mismo objeto, en todas partes. En un repositorio diminuto esto es irrelevante: tres objetos en disco, cada uno un archivo comprimido con zlib. Pero un checkout del kernel de Linux contiene aproximadamente 11,8 millones de objetos, y Git no los mantiene como 11,8 millones de archivos. Los agrupa en packfiles, archivadores comprimidos que almacenan muchos objetos en un solo archivo, precisamente porque mantenerlos como archivos sueltos chocaría con los límites de inodos del sistema de archivos.

Meme de cerebro expandido que contrasta ideas ingenuas y correctas sobre cómo Git almacena versiones de archivos
Git no almacena cada versión de cada archivo como una copia completa separada; almacena objetos direccionados por su contenido, agrupados en packfiles.www.tigrisdata.com

El artículo explica que un repositorio es, en esencia, un mar de objetos direccionados por su contenido más referencias con nombre que apuntan a ellos, y que Git los agrupa en packfiles en lugar de mantener millones de archivos sueltos. El meme captura la parte contraintuitiva: la suposición de cerebro pequeño de que Git guarda archivos completos para cada versión es incorrecta, y es precisamente por eso que el formato packfile importa para el almacenamiento de objetos.

Los packfiles son donde falla la traducción al almacenamiento de objetos. Git fue diseñado pensando en almacenamiento local: escribe packfiles en disco y los lee inmediatamente después, y los memory-mapea para que la caché de páginas del sistema operativo sirva los datos calientes como si fueran RAM. Una lectura de sistema de archivos en ese mundo toma alrededor de diez nanosegundos. Una ida y vuelta a la red hacia almacenamiento de objetos toma alrededor de diez milisegundos, como mínimo, lo que el autor describe como al menos un millón de veces más lento. Git también depende de un idioma de Unix que consiste en escribir un archivo y leerlo de vuelta inmediatamente para calcular su hash, algo que el almacenamiento de objetos no puede hacer, ya que no se puede recuperar un objeto que aún no ha terminado de escribirse.

El obstáculo más profundo es el índice del packfile. Cada packfile tiene un archivo `.idx` acompañante que le indica a Git dónde vive cada objeto dentro del pack. El índice registra, para cada objeto, su desplazamiento y su tamaño descomprimido, pero no su tamaño comprimido. Esa distinción es fatal para el almacenamiento de objetos. El almacenamiento de objetos se direcciona mediante solicitudes de rango HTTP: para recuperar un solo objeto de un pack de 128 megabytes, un cliente necesita conocer el byte inicial y la longitud del objeto, y luego pedir al bucket exactamente ese rango. Como el índice omite la longitud comprimida, no se puede calcular ese rango solo con el índice, ni descubrirlo de otra forma económica. Esta es, según el autor, la razón fundamental por la que los intentos anteriores de almacenar Git en almacenamiento de objetos fracasaron: no porque las solicitudes de rango no estén disponibles, sino porque el formato de índice de Git no proporciona la información necesaria para usarlas.

La solución del autor toma prestada una idea de los medios físicos. Una pista de CD es un archivo de texto plano que enumera las pistas y los desplazamientos en que comienzan dentro de un archivo WAV más grande, para que un reproductor pueda saltar directamente a cualquier pista sin escanear todo el disco. El formato de reemplazo de objgit hace lo mismo para los objetos de Git, pero con un diseño binario de anchura fija. Un archivo `objects.cue` pequeño contiene un encabezado de 16 bytes, número mágico, versión, tamaño de registro y cantidad de registros, seguido de registros de 58 bytes cada uno, de modo que el registro N queda en el byte `16 + N * 58`. Cada registro lleva el hash del objeto, su tipo, método de compresión, tamaño, y el desplazamiento y longitud de sus bytes dentro de un solo archivo `objects.bin` grande. Recuperar cualquier objeto requiere entonces una búsqueda en el archivo cue, una consulta y una solicitud de rango HTTP al blob binario: 366 bytes en la red en lugar del pack completo.

La ventaja es que esto funciona a escala de producción sin cambiar nada del lado del cliente. El autor reporta que el enfoque funciona bien en repositorios de tamaño real, y el diseño se describe como la forma menos intrusiva de hacer que los objetos de Git se comporten como objetos de almacenamiento de objetos. La capa de almacenamiento subyacente mantiene las referencias con nombre a puntos de ese mar de objetos en FoundationDB, un almacén distribuido de pares clave-valor, lo que el autor llama una especie de recursión: almacenamiento de objetos guardando objetos, con referencias a esos objetos almacenadas en otro sistema similar a objetos.

El proyecto detrás de la entrada de blog, objgit, es un panorama más completo de lo que esto se convierte como producto. Hospedado en tigrisdata/objgit, es un servidor Git, no un proxy para un forge existente, que habla los protocolos SSH, HTTP y Git, ejecuta ganchos post-receive dentro de entornos aislados de espacio de usuario, y admite Git LFS con transferencias con firma previa para que los archivos grandes se muevan directamente entre el cliente y Tigris en lugar de pasar por el servidor. También toma instantáneas EROFS por árbol, expone métricas básicas de Prometheus y distribuye un despliegue de Kubernetes mediante Kustomize. Actualmente no tiene autenticación, lo que el autor señala como una carencia conocida por cubrir si el proyecto resulta útil.

Dos advertencias deben acompañar esto. Primero, las afirmaciones de rendimiento, de que el adaptador de sistema de archivos era demasiado lento para repositorios del mundo real y de que el nuevo formato funciona bien a tamaño de producción, provienen del propio autor y su blog, no de una benchmark independiente; la página del repositorio suministrada corrobora el conjunto de funciones del proyecto pero no valida las mediciones. Segundo, la brecha del ecosistema que señala el autor es real: los servidores Git principales como GitLab, Forgejo y Gitea aún esperan un sistema de archivos local para sus repositorios, así que objgit es actualmente una alternativa de etapa temprana para autogestión, no un reemplazo directo. Si la idea de la pista de CD resulta lo bastante general para ser adoptada fuera de aquí es, por ahora, una pregunta abierta.

Lo que demuestra este trabajo es menos que Git pueda moverse a la nube, eso ya se había intentado antes, y más que el cuello de botella específico nunca fue la red ni el protocolo, sino el formato de índice que Git heredó de una era centrada en disco. Reemplazar ese solo componente, en lugar de envolver Git en una capa de sistema de archivos, es lo que hace viable el almacenamiento de objetos como backend para Git.