Jujutsu para desarrolladores ocupados
(maddie.wtf)- Jujutsu (jj) es un sistema de control de versiones con conceptos y comandos más simples que Git, pero con funciones potentes
- Como usa Git como backend, tiene la ventaja de que puede usarse al mismo tiempo o volver fácilmente a Git
- Ofrece de forma natural funciones como diffs en pila, rebase sencillo y revisiones temporales
- Utiliza el concepto de marcadores (bookmarks) en lugar de ramas (branches), lo que resulta más intuitivo para flujos de trabajo reales
- Su forma de manejar conflictos es flexible y simplifica mucho las tareas cotidianas de control de versiones
Elevator Pitch
Jujutsu (jj) es un sistema de control de versiones que ofrece un modelo mental y una interfaz de línea de comandos mucho más simples que Git.
Sin sacrificar funciones, de hecho podría considerarse incluso más potente
Características como los diffs en pila, el rebase sencillo y las revisiones temporales funcionan de manera natural
Como usa Git como backend, puedes empezar a usarlo junto a un repositorio Git con una sola línea y volver a Git en cualquier momento
Tampoco afecta la forma en que otros usuarios manejan el repositorio
Getting Started
- Después de instalar la herramienta de línea de comandos
jj, se recomienda configurar la información del usuario y el autocompletado del shell - Si se aplica a un repositorio existente, conviene empezar desde un clon nuevo o desde un estado sin cambios
- En un repositorio existente, puedes crear en paralelo los repositorios de Git y Jujutsu con el comando
jj git init --colocate . - Los datos del repositorio de
jjse guardan en una carpeta.jj/, separada de la.gitde Git - La sincronización de datos entre Git y Jujutsu normalmente funciona sin problemas
- Pero hay que tener cuidado con el comando
git clean -fdx, porque elimina la carpeta.jj/ - El seguimiento de ramas remotas también puede configurarse de una vez con ese comando
How To Use It
La interfaz de línea de comandos de Jujutsu es más acotada y concisa que la de Git
Aunque sus conceptos básicos son simples, el flujo de trabajo requiere un pequeño periodo de adaptación
A continuación se explican las formas principales de uso con pasos y ejemplos sencillos
Starting A New Revision
- En Git, el trabajo nuevo normalmente empieza creando una rama, pero en Jujutsu se hace creando una nueva revisión
- Reflejar el estado más reciente del repositorio remoto:
jj git fetch - Ver el historial del repositorio:
jj log - Las revisiones del historial se distinguen por un ID de revisión propio de Jujutsu y el hash del commit de Git
- Para iniciar trabajo nuevo, usa
jj new mainpara crear una nueva revisión encima delmainactual - La revisión que se está editando actualmente se identifica con el símbolo
@ - Si desaparece un prefijo compartido, también puede cambiar el prefijo corto del ID de revisión
Making Changes
- Al modificar archivos, los cambios se incluyen de inmediato en esa revisión (no hay un área de staging separada)
- Cuando termines las modificaciones, agrega un mensaje a la revisión con
jj describe -m "mensaje" - Para crear una nueva revisión, usa
jj new(o especifica una revisión anterior) jj commites una operación combinada dejj describe+jj new
Navigating
- Las revisiones vacías creadas con
jj newse descartan automáticamente al moverse - Para eliminar explícitamente una revisión:
jj abandon <rev ID> - Deshacer la eliminación:
jj op undo - Para volver a una revisión existente, usa
jj edit <rev ID> - Al referirse a revisiones también pueden usarse expresiones revset:
@: revisión actualx-: revisión padrey+: revisión hijaz::: todos los descendientes de z
Branches (Bookmarks)
- Jujutsu no tiene un estado de “permanecer en una rama” como Git
- En su lugar, los bookmarks son solo punteros que señalan una revisión específica
- Crear un nuevo bookmark:
jj bookmark create <nombre> -r <revisión> - Aunque hagas commit, el bookmark no se mueve automáticamente, así que si hace falta debes moverlo aparte con
jj bookmark move <nombre> --to <revisión> - Al hacer push, la creación de una rama remota nueva se permite con la bandera
--allow-new - Si el bookmark difiere del remoto, se muestra con
*y puede sincronizarse conjj git push
Conflicts
- La forma en que Jujutsu maneja los conflictos es más flexible que en Git
- Cuando hay conflictos, aparece la marca
conflicteden la revisión y en sus revisiones descendientes - En los archivos en conflicto se insertan marcadores de conflicto, y se resuelven eliminando esos marcadores
- También puede resolverse editando directamente o haciendo cambios en una nueva revisión y luego combinándolos con
jj squash
Conclusión
Hasta aquí, una guía de cómo realizar con Jujutsu de forma más simple el 80% de las tareas más comunes que se hacen con Git
Más adelante se ofrecerán usos generales y avanzados en formato de manual de referencia (Quick Reference)
También es compatible con el comando jj status, igual que Git
Las consultas o comentarios pueden enviarse al correo indicado en el cuerpo del texto
1 comentarios
Opiniones de Hacker News
Quiero enfatizar algo para quienes se preguntan si vale la pena aprender jj.
Cuando se habla de jj en Hacker News, la gente suele dividirse en dos grupos: quienes aún no lo han probado y quienes lo recomiendan con mucha fuerza.
Casi nunca he visto a alguien usar jj por una semana y luego volver a git.
Casi todos los que lo probaron en serio terminaron quedándose con jj.
Ojalá hoy sea la ocasión para que lo pruebes.
El cambio es mucho más simple de lo que parece; yo pude mantener mi productividad desde el mismo día y en menos de una semana ya no tuve necesidad de volver a los comandos de git.
Me volví más productivo en poco tiempo y ahora hasta me sorprende cómo usaba git antes.
A mí git no me resulta para nada incómodo en términos de productividad.
Tampoco paso tanto tiempo al día usando comandos de git y, en la mayoría de los casos, no me causa problemas para trabajar.
Si tuvieras que manipular mucho el repo con git, jj podría ser mejor, pero ese no es mi caso.
Se parece a que te propongan usar sí o sí bim, que es muy parecido a vim pero con funciones extra.
Pero no me interesa escribir unas cuantas
imenos ni necesito autocompletado para Julia.Si jj les parece mejor, que lo disfruten.
jj sí es un avance claro en la UI de los VCS, pero para usuarios avanzados de git tiene algunas limitaciones.
Como no soporta gitattributes, resulta incómodo si necesitas filtros como git-crypt o git-lfs.
La compatibilidad en Windows, por ejemplo con el manejo de fin de línea, también puede ser peor.
Además, herramientas externas como git-annex o git-bug no se integran con el oplog y pueden ensuciar el historial.
Yo sí tuve la experiencia de usarlo más de una semana y volver a git.
Personalmente prefiero el flujo de trabajo del staging area; la mayoría odia el staging de git, pero para mí su ausencia en jj no compensa lo suficiente.
Podría cambiar mis hábitos, pero por ahora no siento necesidad de atarme a jj.
De todos modos, estoy abierto a volver a intentarlo algún día.
La razón por la que usé jj durante unos meses y luego regresé a git fue el rendimiento.
Las operaciones en jj tardaban varios segundos, mientras que git respondía al instante sin importar el tamaño del proyecto.
Además, usar jj me daba una sensación de mayor carga mental, y al volver a git todo se sintió más ligero.
Quizá eso tenga que ver con mi experiencia de largo plazo usando git.
Decir que “de todos modos solo hay dos grupos” ya suena al típico discurso de los evangelizadores de jj.
Lo que me desespera de jj es que los cambios quedan auto-staged sí o sí.
SVN funcionaba así antes, y siempre sentí que git mejoró mucho al hacer el staging explícito.
En un repo siempre quedan más cambios, y en git es normal elegir solo lo que quieres meter en el próximo commit.
En jj —y también en SVN— antes de commitear hay que hacer trabajo manual incómodo, como mover temporalmente cambios fuera del commit.
Para mí también es un dilema.
El staging siempre me ha parecido molesto; incluso cuando usaba Mercurial me costaba.
Si en más del 90% de los casos quieres autoagregar todo, en realidad resulta cómodo, pero el problema son los archivos que no puedes meter en
.gitignore.Si desactivas el auto add también se vuelve incómodo, y si lo dejas activado tampoco termina de convencer.
Ninguna de las dos opciones es completamente limpia.
El flujo de trabajo de JJ es un poco distinto; por ejemplo, primero creas un commit base y luego trabajas apilando commits anónimos encima.
Cuando ya estás listo, lo ordenas con
jj squashojj split.Yo uso
jj commit -i, la opción-ien varios comandos y ademássnapshot.auto-track="none()"en la config.Cuando usaba Mercurial hacía algo parecido.
En la práctica, si usas mucho la función absorb, los archivos o chunks innecesarios se ignoran automáticamente.
¿No encaja mejor con ese flujo de trabajo el comando
jj new?jj rastrea bien incluso ramas que no son el head dentro del árbol, así que rebase y merge funcionan de forma fluida sin necesidad de stash.
Me cuesta adaptarme a que los cambios se agreguen automáticamente.
A veces en local modifico temporalmente ciertos archivos durante el desarrollo sin intención de commitearlos.
Con git me quedo tranquilo porque, si no hago staging, jamás se van a commitear ni a pushear.
En jj siento que tendría que deshacer el stage o andar con más cuidado.
Puede ser un tema de costumbre, pero me resulta más cómodo indicar con claridad solo lo que quiero commitear.
Capaz estoy entendiendo mal cómo funciona jj.
En jj puedes dividir los cambios con
jj split.Lo que selecciones se organiza en la primera revisión y el resto queda en la segunda.
Si haces varias cosas a la vez y luego las separas en revisiones pequeñas, se vuelve mucho más fácil que en git.
Igual que en git, puedes crear una revisión nueva con
jj new, hacer cambios y luego mover solo la parte exacta que quieres conjj squash -i.Conceptualmente, “@” es la revisión actual y “@-” es la anterior, así que puedes pensarlo como la working copy y el stage de git.
El auto-staging de jj no siempre es algo bueno.
Ojalá la documentación oficial y quienes lo presentan dejaran más claro que el valor por defecto se puede desactivar fácilmente.
En
~/.jjconfigbasta con poner[snapshot]
auto-track = "none()"
listo.
Yo normalmente reviso después el contenido a commitear con
jj splity lo ordeno ahí.Ese flujo de trabajo se parece bastante a
add -pde git.El commit de arriba suele usarse como una especie de working copy que no se pushea.
También puedes cambiar de rama sin stash y el trabajo en progreso se conserva bien.
Sí, a mí tampoco me ha sido fácil cambiar ese hábito.
El argumento razonable a favor de cambiarlo es que no deberías ejecutar código en estado untracked.
Es más seguro dejar los cambios con sentido en commits y no registrar el resto.
El problema aparece cuando no están separadas la configuración de todo el repo y la configuración local; VSCode es el ejemplo típico.
Ahí necesitas cambios específicos del entorno que no deberían commitearse, y todo se vuelve más complicado.
Si quieres ese tipo de flujo de trabajo, puedes tratar “@” en jj como el index de git y, al momento de commitear, hacer squash hacia “@-” para obtener un efecto parecido a
git add --patch.Estoy intentando cambiar al equipo a jj, pero va mal.
Me gustaría que existiera una página donde se vea de un vistazo qué tan fácil es hacer en jj las tareas complejas de git.
Algo corto, como un elevator pitch.
También me hace pensar que quizá necesito mostrarlo yo mismo con una demo.
Da la impresión de que muchas de las tareas comunes en git no son necesariamente más fáciles en jujutsu.
Más bien, jujutsu ofrece flujos de trabajo nuevos que en git son imposibles o engorrosos.
Y eso puede no resultar tan convincente para desarrolladores ya acostumbrados a git.
A mí tampoco es que me encante git.
Antes usaba solo mercurial, pero ahora git ya lo tengo grabado en la cabeza.
Aunque Jujutsu tenga ventajas claras, todavía no me parece lo bastante atractivo como para ocuparme también del setup inicial, como colores complicados o scripts a los que ya estoy acostumbrado.
A mí me ayudó mucho una comparación de una tarea representativa entre jj y git.
https://lottia.net/notes/0013-git-jujutsu-miniature.html
También recomiendo algunos buenos enlaces que pueden servir.
https://v5.chriskrycho.com/essays/jj-init/
https://v5.chriskrycho.com/journal/jujutsu-megamerges-and-jj-absorb/
https://ofcr.se/jujutsu-merge-workflow
Probablemente te guste lo próximo que voy a agregar a este post.
Pienso publicarlo pronto al final de la página y también como una entrada aparte.
Después de unas semanas usando jj, hasta hice un script para mensajes de commit automáticos.
Hoy quise portar el mismo script a un repo viejo de git y me di cuenta de que en git necesitas código que detecte automáticamente si estás en momento de commit o de amend.
En jj, gracias a su estructura de commits inmutables, el script era muchísimo más simple.
Simplemente volví a clonar el repo con jj.
Me liberé casi de inmediato de la confusión entre commit y amend.
https://codeberg.org/jcdickinson/nix/src/branch/main/home/common/scripts/jj-auto.fish
git funciona lo suficientemente bien.
Tiene una pequeña curva de aprendizaje, pero una vez que lo entiendes, todo tiene sentido.
Si soy sincero, en el 95% de los casos basta con conocer pull, add, reset, branch y commit.
En mi flujo de trabajo los stacked diff son imprescindibles.
Sirven mucho para seguir apilando trabajo futuro sin quedar bloqueado esperando una review.
En git no es difícil configurarlo, pero agregar cambios a la parte baja del stack y luego reordenarlo todo es un infierno.
Si trabajas en equipo, dominar rebase y squash es imprescindible.
No puedes evitarlo hasta convencer a todo el equipo de no usarlos.
Sobre todo cuando varias personas desarrollan con urgencia sobre una misma rama, los hábitos de git se vuelven una molestia.
En lo básico es cómodo, pero en situaciones complejas tiene muchos problemas.
Basta con vivir algunas situaciones raras para notar las carencias de git.
Llevo unas dos semanas usando jj y es la primera vez que me siento cómodo gestionando versiones solo desde la línea de comandos.
Cuando usaba git siempre dependía de la GUI, sobre todo con clic derecho en Git Graph dentro de VSCode.
Con jj me bastó leer el tutorial para pasar enseguida a una línea de comandos coherente.
Eso sí, para dejar de copiar y pegar IDs todo el tiempo en el log de jj, parece que hay que aprender el lenguaje revset.
Estoy en una situación parecida.
Usé git durante mucho tiempo, pero lo complejo siempre lo resolvía desde una UI.
Llevo casi un mes usando solo el CLI de jj y la verdad me gusta bastante.
Eso sí, la documentación oficial de jj está escrita suponiendo ciertos conocimientos previos, así que a un principiante puede resultarle confusa.
Ojalá aparezcan más blogs y tutoriales.
Yo también quiero aprender mejor el lenguaje revset y rebase.
Me gustan mucho el CLI conciso, la resolución de conflictos y el oplog.
A mí también me molestaba tener que copiar y pegar IDs, pero desde que uso jjui(https://github.com/idursun/jjui) todo va mucho más fluido.
Terminas trabajando rapidísimo, casi como si fueras recorriendo el log.
Creo que la mejor función de jj es undo.
En git, deshacer depende del tipo de error y cada caso tiene su comando, así que se vuelve difícil.
En jj basta con
jj undo.Además, no estás atado al sistema backend, así que puedes usar jj solo localmente sin afectar a tus compañeros.
Me confunde qué es exactamente jj.
Me pregunto si es un frontend para gente que se enreda con git.
Dicen que abstrae el backend, pero está tan influido por git que me cuesta imaginarlo en sistemas como Perforce o Piper, donde los commits con numeración secuencial son obligatorios.
La idea de working copy = commit me parece que elimina el control de calidad.
El sentido original de un commit es subir solo lo intencional, y con esta estructura parece más difícil distinguir los “commits basura”.
Tanto git como jj parecen débiles para proyectos grandes y no da la impresión de que resuelvan el problema de la proliferación de commits.
Me gustaría entender qué problema resuelve realmente jj; no sé si es más que un frontend construido según las preferencias de su autor.
El backend de Piper en realidad se adapta de forma más natural que el backend de Git.
No hace falta confundir los commits de jj con los commits de git porque no se corresponden uno a uno.
En la práctica, cuando se habla de “commit”, se trata más bien del concepto de un “diff con nombre”, y antes de pushear siempre puedes rehacer y ordenar fácilmente los cambios.
Eso es mucho más cómodo que hacer rebase o editar historial en git.
Yo voy creando varios commits intermedios con experimentos, documentación y otras cosas, que en git habría tenido que meter a la fuerza en un stash o en una rama temporal.
jj es más que un frontend de git.
Es un sistema de control de versiones y es backend-agnostic.
Como su backend más representativo es git, puedes cambiar enseguida desde un repo existente.
Yo uso git desde antes de que existiera GitHub, pero después de usar jj ya no puedo volver atrás.
jj es más simple y al mismo tiempo más potente.
Lo de working copy = commit en realidad conviene entenderlo como “el index también es un commit”.
Por ejemplo, si empiezas a trabajar en la feature x con
jj new -m "working on feature x" trunk, creas un commit nuevo y luego apilas otro commit vacío encima.Tu trabajo entra en la working copy (
@) y luego lo “mueves” haciendo squash al commit anterior (@-), de modo que en vez de depender de opciones complejas como add-p o reset de git, todo se maneja como diffs entre commits.Gracias a esa estructura, dividir y ordenar commits es más flexible y potente que el index de git.
El problema de la proliferación de commits está más ligado a la cultura del pull request, y jj solo puede resolverlo hasta cierto punto.
La working copy no se pushea sin más a GitHub; cuando le agregas una descripción al commit puedes revisarla y ordenarla.
Probé jj durante unos días, pero ya estoy bastante satisfecho con lazygit, mi flujo de trabajo actual y mis scripts.
JJ es novedoso y está bien, pero salvo que estés empezando recién con control de versiones, no veo suficiente motivo para cambiar.
Sigo usando el diff de Lazygit, pero estar en estado detached HEAD no me causa ningún problema.
JJ se lleva muy bien con git y te permite hacer todo mucho más fácil sin tener que memorizar comandos complejos.
Que los archivos sin commitear se muevan automáticamente al cambiar entre ramas es probablemente su mejor función.
Hace muy fácil ir y venir entre desarrollo, corrección de bugs y cambios de copy.
Si un agente de IA va a hacer cambios, puedes separarlo usando worktrees.
También está buenísimo poder poner la descripción del cambio (= mensaje de commit) antes de terminar y verla por adelantado en el árbol.
En general, JJ es realmente muy bueno.