Jujutsu ¿alternativa a Git?

Creo que no conozco a nadie que trabaje en el mundo del desarrollo y que no utilice un sistema de control de versiones. En este aspecto Git es el rey indiscutible, sin embargo, a pesar de su extendido uso, su enorme cantidad de comandos y opciones hacen que la curva de aprendizaje sea un tanto elevada para algunos usuarios. Esta fue la motivación de Martin von Zweigbergk de crear Jujutsu: un Sistema de Control de Versiones compatible con Git que propone un modelo más simple, flexible y reversible.

¿Qué es Jujutsu?

Como comentaba anteriormente, Jujutsu es un Sistema de control de versiones(VCS) creado por Martin von Zweigbergk, ex trabajador de Google y coautor del libro Git Pro, con la idea de ser compatible con Git pero ofrecer una experiencia de usuario diseñada desde cero y mejorada.

Una de las principales características de Jujutsu es que internamente abstrae la interfaz de usuario y los algoritmos de control de versiones, de los sistemas de almacenamiento que se utilizan para gestionar el contenido. Esto le permite poder operar con múltiples backends, que pueden tener sus propios modelos de datos o de red, como Mercurial o Breezy, o sistemas híbridos como el diseño basado en la nube de Google, Piper/CitC.

Funciones clave de Jujutsu

Como comentan en su documentación, han combinado diferentes conceptos de diseño de otros VCS en una única herramienta.

De Git
  • Se busca mantener la velocidad y eficiencia con algoritmos optimizados y estructuras de datos correctas.
De Mercurial y Sapling
  • Se incorpora el lenguaje revset, que permite hacer consultas complejas sobre el historial de commits.
  • No existe un índice ni una zona de staging explícita, haciendo que todos los cambios ya pertenezcan a tu working copy que es editable.
  • Las ramas por defecto son anónimas, las ramas son una referencia de commits por lo que no es necesario nombrar las ramas agilizando los pequeños cambios. Aunque si se desea se pueden crear nombres a las ramas.
  • Los comandos para reescribir el historial son potentes y simples, ya que el historial es mutable por diseño. Por ejemplo puedes cambiar un commit y, automáticamente sus descendientes se rebasan sin romper el historial, sin operaciones complicadas.
  • Jujutsu usa un lenguaje de plantillas (template language) para dar formato a la salida de sus comandos. Todas las plantillas se pueden configurar y editar al gusto del trabajador, decidiendo cómo quieres que se muestre la información, los colores, el formato de cada commit, etc.
Darcs
  • Los conflictos se tratan como objetos de primera clase, al igual que los commits. En vez de ser diffs de texto, Jujutsu los moldea explícitamente, lo que permite que la resolución de conflictos se realice y propague de manera automática en muchos casos.

Funcionalidades de cosecha propia

Jujutsu también incorpora varias funcionalidades novedosas que marcan la diferencia.

Working-copy-as-a-commit

Cada cambio que realizamos en los archivos se guarda automáticamente como un commit normal, que se actualiza con cada modificación. Gracias a este modelo basado en “snapshots” simplifica el modelo de datos visible al usuario y los algoritmos internos reemplazando completamente funcionalidades como los stashes o el index/staging área de Git.

Operación de log y revert

Jujutsu almacena todas las operaciones que se realizan en el repositorio facilitando entender qué ha pasado y cómo se ha llegado a esa situación. De esta manera permiten deshacer fácilmente cualquier acción realizada.

Rebase y resolución automática de conflictos

Cuando modificamos un commit, todos los descendientes se rebasan automáticamente sobre la nueva versión. Esto hace que los flujos de trabajo basados en patches sean extremadamente sencillos.

Además, si resuelves un conflicto en un commit, la resolución de ese conflicto también se propaga a todos los commits descendientes.

Ejemplo práctico

Para el ejemplo práctico, vamos a utilizar algunas de las principales funcionalidades de Jujutsu con un flujo de trabajo sencillo que te permitirá entender por qué está ganando tanto interés entre los desarrolladores.

Instalación de Jujutsu

Para instalar Jujutsu sigue las instrucciones oficiales de instalación disponibles en su documentación:
👉 https://jj-vcs.github.io/jj/latest/install-and-setup/

La guía cubre todos los sistemas operativos y métodos de instalación, incluidos Homebrew, Cargo y binarios precompilados.

Inicialización de repositorio

Creamos el repositorio:

				
					jj git init demo-jj
cd demo-jj
				
			
Working-copy-as-commit

Añadimos un archivo y observamos cómo Jujutsu guarda los cambios automáticamente como parte del working commit.

Creamos un fichero main.py dentro del repositorio.

				
					 echo 'print("Hola mundo")' > main.py

				
			

Y después introducimos el comando jj log.

Y se puede observar que hay un commit ya realizado sin necesidad de una staging área y hacer un add o un commit.

Configuramos los datos de usuario

Para que en el desglose del log podamos ver más detalles del commit vamos introducir nuestros datos de usuario.

				
					jj config set --user user.name "Vuestro nombre"
jj config set --user user.email "vuestro mail"
				
			
Commit explícito y modificación del historial

A continuación vamos a proceder con el primer commit.

				
					jj commit -m "Primer commit con Jujutsu"
				
			

Y ahora vamos a cambiar el mensaje.

				
					jj describe -m "Actualización del mensaje del primer commit"

				
			
Operation log y revert

Una de las grandes ventajas de Jujutsu es su registro de operaciones, que permite entender y revertir cualquier acción. Con la siguiente acción vamos a poder ver todas las operaciones recientes (commits, pulls, merges, etc.).

 

				
					jj op log
				
			

En Jujutsu cada acción queda registrada como una operación que puede revertirse fácilmente, pasándole al comando de revert el ID de la operación. De esta manera podemos revertir cualquier acción, ya sea un commit, un rebase o un squash.

				
					jj op revert <idOperacion>

				
			

Se puede ver que finalmente hemos revertido la actualización del mensaje de commit. Como se ve en la siguiente imagen el commit c2915df0115d ha desaparecido de nuestro histórico local. Cuando hagamos push, podremos subir una versión limpia de nuestro histórico. Para ello vamos a usar el comando log sin la opción op, que nos enseña el grafo de actual de nuestro repositorio, sin las operaciones.

				
					jj log

				
			
Conflictos como objetos de primera clase

Supongamos que creas dos ramas y editas el mismo archivo en ambas. Primero crearemos la rama main:

				
					jj bookmark create main
				
			

Esto crea la primera rama con los cambios y acto seguido creamos la rama feature-A y nos movemos a ella con el comando new para hacer un cambio y un commit.

				
					jj new main -m "Inicio de feature-A"
jj bookmark create feature-A
echo 'print("Cambio desde feature-A")' > main.py
jj commit -m "Cambio en feature-A"

				
			

Ahora nos movemos a main y hacemos cambios también.

				
					jj new main
echo 'print("Cambio desde main")' > main.py
jj commit -m "Cambio en main"

				
			

Una vez que estamos en este punto con cambios sobre la misma línea en diferentes ficheros, hacemos un merge de ambos commits usando un squash en la rama main.

				
					

jj squash --from feature-A -m "Merge feature-A into main"
				
			

El fichero main.py se ve de la siguiente manera, indicando el conflicto.

Acto seguido lo solucionamos, guardamos el archivo y hacemos el squash.

				
					jj squash -m "Merge feature-A into main"

				
			

Jujutsu al tratar el conflicto como objeto de primer nivel, ya dentro de nuestra working copy se marca como resuelto.

Propagación automática de conflictos resueltos

Para ello vamos a crear dos ramas, la Feature-B y la Feature-C que van a tocar el mismo fichero.

				
					jj new -m "Feature-B"
printf 'print("Solucionamos el conflicto")\nprint("feature b")\n' > main.py
jj new -m "Feature-C"
printf 'print("Solucionamos el conflicto")\nprint("feature b")\nprint("feature-c")\n' > main.py

				
			

Acto seguido volvemos al commit de antes de crear las Features, modificamos el fichero y actualizamos el commit. De esta manera al actualizar un commit inferior nos saldrán conflictos.

				
					jj edit @--
printf 'print("CAMBIO EN INITIAL")\n' > main.py
jj describe -m "Initial modified"
jj log

				
			

Como se puede ver en las imagen de arriba, se han rebasado automáticamente los commits descendientes como se puede leer en la frase “Rebased 2 descendant commits onto updated working copy” y tenemos conflictos en el Feature-B y el Feature-C.

A continuación vamos a resolver los conflictos en Feature-B para que se propaguen a Feature-C de manera automática. Para ello subiremos al commit de Feature-B, actualizaremos el fichero de Feature-B y actualizaremos el commit.

				
					jj edit @+
printf 'print("CAMBIO EN INITIAL")\nprint("feature b")\n' > main.py
jj describe -m "Feature-B resolved"
jj log

				
			

¿Es Jujutsu una alternativa a Git?

No, a día de hoy Jujutsu no es una alternativa a Git. Git sigue siendo el estándar consolidado, con una comunidad gigantesca, herramientas maduras y soporte en prácticamente cualquier entorno profesional.

Dicho esto, Jujutsu ofrece un planteamiento más moderno: commits automáticos, historial mutable, resolución de conflictos como objetos de primera clase y un registro completo de operaciones que facilita deshacer cambios. Estas características lo hacen más intuitivo y flexible.

Quién sabe si en el futuro estas ideas podrían influir en cómo evolucionan los sistemas de control de versiones. Por ahora, Git sigue siendo insustituible, y Jujutsu más bien se perfila como un laboratorio de innovación que apunta hacia el futuro.

Compartir:

Artículos relacionados

Rol del QA Lead

La transformación digital de las empresas ha hecho crecer la demanda de perfiles IT. Los cambios en los procesos y metodologías de trabajo han supuesto retos que han abierto la

Leer más »