← inicio

Jujutsu: el VCS que Git quiso ser

Si llevas años peleando con git rebase -i, maldiciendo el staging area y perdiendo commits por un reset --hard mal puesto, hay un proyecto que merece tu atención: Jujutsu (jj).

Jujutsu es un sistema de control de versiones distribuido escrito en Rust que usa repositorios Git como backend de almacenamiento. Eso significa que puedes usarlo hoy mismo en cualquier repo Git, sin migrar nada, sin que tu equipo tenga que cambiar de herramienta. Y si no te convence, vuelves a git sin perder historial.

El proyecto acaba de lanzar la versión 0.40.0 (2 de abril de 2026), acumula casi 28 000 estrellas en GitHub y el tutorial de Steve Klabnik sobre jj ha alcanzado más de 300 puntos en Hacker News esta misma semana. No es un experimento: gente de Google, Meta y varias empresas de infraestructura lo usan en producción.

Qué lo hace diferente

1. Working copy como commit

En Git, modificas archivos, haces git add para stagear y luego git commit. El staging area es una capa extra que complica el modelo mental y genera fricción constante.

En jj, los cambios en tu working copy son automáticamente un commit. No hay staging area. No hay git add. Simplemente editas ficheros y jj refleja esos cambios como un commit en progreso que se va amendmentando automáticamente.

# Creas un repo igual que con git
jj git init

# Editas un fichero... y ya. jj lo detecta como cambio
echo "console.log('hello jj')" > main.ts

# El estado muestra tu cambio automático
jj st
# Working copy changes:
# M main.ts

# Describes el cambio cuando quieres
jj describe -m "añadir punto de entrada"

Cada vez que guardas un fichero, jj actualiza el commit de working copy. No hay paso intermedio. Si quieres commitar parcialmente, usas jj squash --path para mover cambios específicos a otro commit.

2. Undo nativo con operation log

¿Alguna vez has hecho un rebase que destrozó tu rama y te has quedado mirando git reflog como quien mira un diagnóstico médico? En jj no hace falta reflog ni magia oscura.

Cada operación que realizas — commit, rebase, push, pull, lo que sea — queda registrada en el operation log. Puedes inspeccionarlo y deshacer cualquier paso con un solo comando:

# Ves el log de operaciones
jj op log
# @: 2026-04-14 15:30 rebase commits 3 -> 5
# o: 2026-04-14 15:28 new commit abc123
# o: 2026-04-14 15:20 git pull origin main

# Deshaces la última operación
jj undo

# O deshaces una operación específica
jj op restore --at @--

Esto es radical. En Git, corregir un error del historial requiere saber qué comandos inversos ejecutar. En jj, simplemente dices “deshaz lo último” y ya. Es como tener Ctrl+Z en tu control de versiones.

3. Rebase automático y transparente

Cuando modificas un commit en Git — por ejemplo, haces un commit --amend — los commits hijos no se actualizan automáticamente. Tienes que acordarte de hacer rebase --update-refs o gestionar las ramas a mano.

En jj, cuando modificas un commit, todos sus descendientes se rebasan automáticamente. No hay que hacer nada. Es transparente:

# Creas una cadena de commits
jj new -m "primera feature"
jj new -m "segunda feature sobre la primera"

# Modificas el primer commit
jj edit @--
jj describe -m "primera feature (versión corregida)"

# El segundo commit se rebasa solo. No hay que hacer nada.
jj log
# ◉ segunda feature (rebasado automáticamente)
# ◉ primera feature (versión corregida)
# ◉ main

Esto hace que los workflows basados en parches — muy comunes en empresas que usan stacked PRs — sean infinitamente más fáciles.

4. Ramas anónimas y revsets

En Git necesitas darle un nombre a cada rama, aunque sea para un cambio de tres líneas que vas a borrar en cinco minutos. En jj, los cambios forman una cadena y no necesitan nombre. Cuando quieres publicar, creas un bookmark (el equivalente a una branch en Git):

# Trabajas en tu cambio sin nombre
jj new -m "fix: off-by-one en el parser"
# ... editas ficheros ...

# Cuando quieres publicar, creas un bookmark
jj bookmark create fix-parser
jj git push -b fix-parser

Y para buscar commits, jj trae los revsets, un lenguaje de consulta inspirado en Mercurial. En vez de memorizar flags de git log, escribes expresiones declarativas:

# Commits míos de la última semana que toquen ficheros Rust
jj log -r 'author(me) & date(after:"1 week ago") & file("*.rs")'

Compatibilidad con Git: sin sacrificio

El punto clave es que jj usa Git por debajo. Tus repos son .git normales. Puedes alterna entre jj y git en el mismo repo sin problema. Tus compañeros pueden seguir usando git como siempre. Los CI/CD, los hooks, los forge (GitHub, GitLab) — todo funciona porque el formato de almacenamiento es Git.

Lo que jj almacena separado es: bookmarks, el operation log y la resolución de conflictos. Pero los commits y el árbol de ficheros son Git puro.

Mi veredicto

jj es de esas herramientas que cambian tu forma de trabajar. No porque te obliguen a pensar de otra manera, sino porque quitan fricción donde Git la genera. El staging area es un artefacto histórico que complica más de lo que ayuda. El rebase manual es un ritual que debería ser automático. Y el reflog como mecanismo de recuperación es una chapuza que el operation log resuelve de raíz.

Dicho esto, jj sigue siendo versión 0.x. La API de comandos puede cambiar entre releases. No hay soporte nativo para submodules todavía (está en progreso). Y la curva de aprendizaje existe — no porque jj sea complicado, sino porque tienes que desaprender costumbres de Git.

Mi recomendación: instálalo en un proyecto personal, sigue el tutorial de Steve Klabnik (que es excelente), y dale una semana. Si vuelves a git add sin pensar, nada se ha perdido. Pero apuesto a que no vuelves.