Git 2.55: git reftable backend, 10.000 refs en 40ms
En pocas palabras: Git 2.55.0 (29 de junio de 2026) hace que su backend reftable cree 10.000 refs en 39-42ms contra 436-650ms del backend de archivos clásico, y ocupe 272KB en disco contra 40MB. Con 150 escritores simultáneos, sin embargo, hasta el 63% de las escrituras falló con “cannot lock references”.
Git 2.55.0, lanzado el 29 de junio de 2026, incorpora el backend reftable ya maduro. Crear 10.000 refs tardó 39-42ms contra 436-650ms del backend de archivos clásico, pero con 150 escritores concurrentes hasta el 63% de las escrituras falló con el error cannot lock references.
El backend reftable de Git es un formato de almacenamiento binario para referencias (ramas y tags) que reemplaza el árbol de archivos .git/refs/ por un número reducido de tablas ordenadas y buscables por búsqueda binaria bajo .git/reftable/. Lo desarrolla el proyecto Git y, según las notas técnicas oficiales, la intención es que sea el formato por defecto en Git 3.0.
En este artículo:
- En 30 segundos
- ¿Qué cambia el backend reftable respecto al sistema de archivos tradicional de Git?
- ¿Qué tan rápido es reftable escribiendo y leyendo referencias?
- ¿Por qué git update-ref falla con “cannot lock references” en reftable?
- ¿Cómo se soluciona el error de bloqueo y qué cuesta la solución?
- Ejemplo hipotético: cómo pensar la decisión de migrar un repo de CI
- ¿Cómo migrar un repositorio existente a reftable y qué limitaciones tiene?
- ¿Qué está confirmado y qué queda por verificar?
- Errores comunes al evaluar o migrar a reftable
- Preguntas Frecuentes
- Conclusión
- Fuentes
En 30 segundos
- Velocidad de escritura: crear 10.000 refs pasó de 436-650ms a 39-42ms; a 50.000 refs, de hasta 12,4s a 199-213ms.
- Ahorro de disco: 10.000 refs ocupan 272KB en reftable contra 40MB en archivos sueltos bajo
.git/refs/. - Lectura, ganancia menor:
git for-each-refsolo mejora 1,3-1,4x, lejos del salto que muestran las escrituras. - Concurrencia, el punto débil: con 100 y 150 escritores simultáneos, entre el 30% y el 63% de las operaciones falló en siete pruebas.
- Migración:
git refs migrate --ref-format=reftableconvirtió 10.000 refs en 168ms, pero no funciona en repos con un segundo worktree.
¿Qué cambia el backend reftable respecto al sistema de archivos tradicional de Git?
Reftable reemplaza un archivo por cada referencia bajo .git/refs/ por un puñado de tablas binarias ordenadas bajo .git/reftable/, buscables por búsqueda binaria en vez de escaneo secuencial de directorio. El ahorro de espacio es directo: a 10.000 refs, el backend de archivos ocupa 40MB repartidos en 10.000 archivos sueltos (la mayoría es overhead de bloque de filesystem para archivos de apenas 41 bytes), mientras reftable usa una tabla de 266KB más una lista de 43 bytes, 272KB en total, según midió el análisis publicado en dev.to.
A 50.000 refs la diferencia se vuelve más notoria todavía: 198MB contra 1,4MB. Ponele que administrás un repositorio con miles de ramas de feature branches acumuladas, algo típico en monorepos grandes. Ahí el ahorro de inodos no es un detalle menor, sobre todo si tenés muchos repositorios en el mismo filesystem.
¿Qué tan rápido es reftable escribiendo y leyendo referencias?
Reftable gana por paliza en escritura masiva pero apenas por margen modesto en lectura. Escribir 10.000 refs tomó 39-42ms contra 436-650ms del backend clásico (entre 10 y 16 veces más rápido), y a 50.000 refs la brecha se amplía: 199-213ms contra un rango de 2,1 a 12,4 segundos. Leer con git for-each-ref, en cambio, solo mejora 1,3-1,4x.
La asimetría tiene una explicación bastante directa si se piensa en qué hace cada operación. Escribir 10.000 refs sueltos significa 10.000 llamadas al sistema de archivos para crear otros tantos archivos; reftable las convierte en una escritura secuencial a un puñado de tablas, así que el ahorro escala con la cantidad de refs. Leer, en cambio, ya era relativamente barato en ambos formatos para una sola consulta: el backend de archivos abre un archivo por ref, pero for-each-ref necesita recorrer todas las refs igual, con o sin reftable de por medio. La ganancia existe, pero no tiene el mismo margen para crecer.
| Refs | Backend files | Backend reftable |
|---|---|---|
| 10.000 (escritura) | 436-650ms | 39-42ms |
| 50.000 (escritura) | 2,1-12,4s | 199-213ms |
| 10.000 (for-each-ref) | 180-183ms | 132-134ms |
| 50.000 (for-each-ref) | 897-943ms | 636-714ms |

El tema es que las notas de la versión 2.51 de Git prometen fetch 22 veces más rápido y push 18 veces más rápido en un repositorio de 10.000 refs. El autor de la prueba no logró reproducir esos multiplicadores en una sola máquina sobre file://: el mejor caso con git ls-remote a 50.000 refs fue una mejora de 2 a 4 veces, y a 10.000 refs la diferencia casi desapareció. Esos números de la release oficial probablemente midan una red real, donde el costo fijo por round-trip pesa más que acá. Tomalo con pinzas si tu escenario es local o sobre una LAN rápida.
¿Por qué git update-ref falla con “cannot lock references” en reftable?
Falla porque cada escritura necesita el mismo lock sobre tables.list, y con muchos procesos simultáneos ese archivo único se convierte en cuello de botella. En pruebas con 150 procesos de git update-ref lanzados en paralelo contra reftable, entre 54 y 70 tuvieron éxito en cinco corridas distintas; sumando esas corridas a las realizadas con 100 escritores concurrentes, las tasas de falla oscilaron entre el 30% y el 63% a lo largo de siete pruebas separadas. El backend de archivos, en la misma prueba con 150 escritores, completó 150 de 150 siempre.
¿Por qué pasa esto y no con el formato clásico? El backend de archivos bloquea un archivo por referencia, así que ramas distintas no compiten entre sí. Reftable, en cambio, escribe todo a una pila compartida y la compactación geométrica por defecto (reftable.geometricFactor, valor 2) refolda las tablas chicas después de casi cada escritura, pidiendo ese mismo lock una y otra vez. Cuando llega una ráfaga de escritores genuinamente simultáneos, se forma una cola sobre un único archivo y la paciencia por defecto de Git para esa cola es corta.
¿Cómo se soluciona el error de bloqueo y qué cuesta la solución?
Se soluciona subiendo reftable.lockTimeout, documentado con default de 100ms (0 significa no reintentar, -1 significa reintentar indefinidamente). Con el valor en 5000, la prueba de 150 escritores concurrentes pasó a completarse 150 de 150 veces, sin errores, en tres corridas seguidas. Si tu CI usa Jenkins o GitHub Actions para levantar ramas automáticamente en cada job, este es justo el tipo de parámetro que conviene chequear antes de migrar un repositorio compartido, no después: lo desarrollamos en Jenkins vs. GitHub Actions, cuál elegir.
Eso sí: el trabajo que antes tardaba 106-119ms con el backend de archivos pasó a tomar entre 1,05 y 1,15 segundos con reftable y el timeout ajustado. El workaround no elimina la serialización de fondo, solo hace que todos los procesos esperen su turno en vez de que un tercio se rinda y devuelva error. Si tu carga es “un solo proceso escribe a la vez”, esto nunca te va a molestar. Si es “CI crea una rama por job y varios terminan al mismo tiempo”, conviene revisar el valor por defecto de reftable.lockTimeout antes de migrar, no después de que empiecen los reportes de fallas.
Ejemplo hipotético: cómo pensar la decisión de migrar un repo de CI
Ejemplo hipotético, no es un caso medido ni real, sirve solo para ilustrar el razonamiento: imaginá un equipo con un monorepo donde cada pull request abierto dispara un job de CI que crea una rama de trabajo temporal, y que un viernes a la tarde, antes del corte de sprint, se abren o actualizan unos 40 PRs en una ventana de pocos minutos. Ese pico está lejos de los 100-150 escritores simultáneos que en las pruebas del artículo hicieron fallar entre el 30% y el 63% de las escrituras, pero ya se acerca a la zona de 50 escritores donde reftable, incluso sin fallar, es más lento que el backend de archivos (74-108ms contra 37-43ms).
Con esos umbrales como referencia, el criterio de decisión quedaría así:
- Si el pico real de escritores simultáneos en tu repo se mantiene claramente por debajo de 50, migrar a reftable con la configuración por defecto no debería traer sorpresas de concurrencia.
- Si el pico se acerca a 100 o más, medí tu propia tasa de fallas con una prueba como la del artículo antes de decidir, en vez de asumir que tu caso es distinto.
- Si igual decidís migrar con picos altos, subir
reftable.lockTimeoutde 100 a algo como 5000 elimina las fallas, pero cambia el problema: en vez de errores, vas a tener jobs que esperan más tiempo en cola.
El número de “40 PRs” de este ejemplo es inventado para ilustrar cómo se aplica el criterio, no una medición del artículo ni de ningún repositorio real: lo que importa es contrastar tu propio pico de escritores concurrentes contra los umbrales de 50 y de 100-150 que sí están medidos en las pruebas citadas arriba.
¿Cómo migrar un repositorio existente a reftable y qué limitaciones tiene?
Se migra con git refs migrate --ref-format=reftable, un comando que convierte el repositorio en el lugar. Sobre un repositorio de 10.000 refs en formato archivos, la migración tomó 168ms y cada referencia llegó intacta al otro lado. La primera limitación aparece rápido: el comando rechaza migrar repositorios con un segundo worktree conectado, con el error migrating repositories with worktrees is not supported yet.
Hay un efecto colateral menos obvio. La migración reescribe .git/HEAD con el texto literal ref: refs/heads/.invalid, aunque la rama real (por ejemplo refs/heads/master, apuntando a un commit válido) siga resolviendo bien con git symbolic-ref HEAD, git rev-parse HEAD o un git clone nuevo. Esto es comportamiento documentado y deliberado según la documentación técnica de reftable: el archivo dummy existe para que herramientas que solo chequean “¿esto parece un repo Git?” sigan funcionando, sin filtrar el nombre real de la rama a un formato que no sabe de reftable. El problema es para cualquier script que lea .git/HEAD directamente como texto en vez de preguntarle a Git: después de migrar, va a recibir .invalid.
¿Qué está confirmado y qué queda por verificar?
- Confirmado: Git 2.55.0 salió el 29 de junio de 2026 con reftable ya maduro, según las notas de versión oficiales.
- Confirmado: las ganancias de escritura masiva (10-60x según el tamaño) y de espacio en disco están medidas de forma independiente y son consistentes entre corridas.
- Confirmado: la falla de concurrencia con
cannot lock referencesse reprodujo en siete pruebas separadas, no fue un evento aislado. - Pendiente de verificar: los multiplicadores de 22x en fetch y 18x en push que citan las notas de la versión 2.51 no se reprodujeron en pruebas locales sobre
file://; podrían depender de condiciones de red específicas que no están documentadas en detalle. - Pendiente de verificar: si Git 3.0 efectivamente hace reftable el formato por defecto para repos nuevos, algo que las notas de la 2.51 anuncian como intención pero todavía no es un hecho consumado.
Errores comunes al evaluar o migrar a reftable
- Asumir que reftable acelera todo por igual: el salto grande es en escritura masiva. En lectura con
for-each-refla mejora es de apenas 1,3-1,4x, no el 10-60x que se ve en escritura. - Migrar un repo de CI sin testear concurrencia real: si varios jobs crean ramas al mismo tiempo, corré tu propia prueba de concurrencia a la escala que realmente ves antes de migrar, no después de que empiecen los fallos en producción.
- Dar por hecho que el 22x de fetch se replica en tu red: ese número es de un benchmark específico de las notas oficiales. En pruebas locales sobre
file://la ganancia real fue de 2 a 4 veces. - Leer
.git/HEADcomo texto plano después de migrar: vas a encontrarref: refs/heads/.invalid. Uságit symbolic-ref HEADpara obtener el nombre de rama real.
Preguntas Frecuentes
¿Qué es el backend reftable de Git?
Es un formato de almacenamiento binario para referencias de Git (ramas, tags) que guarda todas las refs en un número reducido de tablas ordenadas dentro de .git/reftable/, en vez de un archivo por referencia bajo .git/refs/. Llegó maduro en Git 2.55.0 el 29 de junio de 2026.
¿Reftable es más rápido que el backend de archivos tradicional en Git?
En escritura masiva sí, y por mucho: crear 10.000 refs tomó 39-42ms contra 436-650ms del backend clásico. En lectura la diferencia es mucho más chica, apenas 1,3-1,4x con git for-each-ref, así que la respuesta depende de qué operación te importa más.
¿Por qué git update-ref falla con el error cannot lock references?
Porque reftable serializa las escrituras contra un único archivo, tables.list, y la compactación automática por defecto compite por ese mismo lock. Con 100 a 150 procesos escribiendo al mismo tiempo, entre el 30% y el 63% de las operaciones falla con ese error en pruebas repetidas.
¿Cómo se migra un repositorio existente a reftable en Git 2.55?
Con el comando git refs migrate --ref-format=reftable, que convierte el repositorio en el lugar. En un repo de 10.000 refs la migración tomó 168ms, aunque falla si el repositorio tiene un segundo worktree conectado.
¿Conviene activar reftable si varios procesos escriben refs al mismo tiempo?
No sin antes ajustar reftable.lockTimeout (default 100ms) y probar tu carga real de concurrencia. Subir ese valor a 5000 elimina los fallos, pero un job que tardaba 106-119ms con el backend de archivos puede pasar a 1,05-1,15 segundos con reftable.
Conclusión
Git 2.55.0 confirma que reftable ya está listo para cargas de escritura masiva y espacio en disco, con ganancias medidas de 10 a 60 veces según el tamaño del repositorio. Lo que no está resuelto es la concurrencia: si tu pipeline de CI crea ramas desde varios procesos al mismo tiempo, migrar sin ajustar reftable.lockTimeout te puede meter en un problema que el backend de archivos nunca tuvo.
El criterio práctico es simple. Antes de migrar un repositorio compartido, corré vos mismo una prueba con la cantidad real de escritores concurrentes que ves en producción, no con el número que aparece en un benchmark ajeno. Si el repo es de un solo escritor a la vez, la migración con git refs migrate --ref-format=reftable es barata y el beneficio es directo.






