4 puntos por GN⁺ 2023-08-02 | 2 comentarios | Compartir por WhatsApp
  • Jujutsu es un sistema de control de versiones usado para copiar código, rastrear cambios y publicar cambios en proyectos de software, y fue diseñado para que sea fácil de usar tanto para usuarios nuevos como experimentados, así como para proyectos personales nuevos y equipos grandes con historiales extensos
  • Separa internamente la interfaz de usuario y los algoritmos de control de versiones del sistema de almacenamiento que provee el contenido, y actualmente usa repositorios Git como capa de almacenamiento, por lo que es compatible con herramientas basadas en Git
  • En Git solo se almacenan los commits y los archivos; los bookmarks (branches) y otros metadatos de nivel superior se guardan en un almacenamiento personalizado fuera de Git
  • El backend de Git predeterminado usa la biblioteca Rust gitoxide, puede traer branches desde repositorios remotos de Git y hacer push al remoto, y genera commits que se ven como commits normales de Git
  • Usa el modelo working-copy-as-a-commit, donde la copia de trabajo se representa como un commit real; los cambios en archivos se registran automáticamente como un commit normal y luego se van actualizando con cada cambio mediante amend
    • Los comandos no fallan por tener una working copy sucia
    • No hace falta git stash
    • Se puede definir un mensaje de commit para el commit de la copia de trabajo incluso antes de terminar el trabajo
  • Todas las operaciones del repositorio se registran junto con una instantánea del estado del repositorio después de cada operación, lo que permite restaurar estados anteriores del repositorio, hacer undo por operación y revertir una operación específica que no sea la más reciente
  • Los conflictos se registran como objetos de primera clase al igual que los commits, por lo que las operaciones que generan conflictos también tienen éxito y luego pueden resolverse con un flujo de trabajo unificado de resolución de conflictos
  • Cuando se modifica un commit, todos sus descendants se rebasean automáticamente sobre el nuevo commit; los resultados de resolución de conflictos también se propagan a los descendants, y los bookmarks y la copia de trabajo que apuntan a los commits rebaseados también se actualizan
  • No tiene un index ni staging area explícitos, usa el lenguaje revset para seleccionar commits, y el formato de salida se maneja con un lenguaje de plantillas configurable por el usuario
  • Entre sus funciones de reescritura de historial ofrece jj describe para editar el mensaje de cualquier commit, jj diffedit para editar cambios de un commit sin hacer checkout, jj split para dividir commits y jj squash -i --from X --into Y para mover parte de los cambios a otro commit
  • La función de replicación segura concurrente es experimental; está diseñada para que el repositorio no se corrompa al usar sistemas de archivos distribuidos o herramientas de respaldo como Dropbox o rsync, y en el peor de los casos busca exponer conflictos entre el estado local y el remoto
  • jj es un sistema de control de versiones experimental; su compatibilidad con Git es estable, pero puede haber funciones en desarrollo, una UX no óptima y vacíos de flujo de trabajo en ciertos modos de uso
  • Aunque sus funciones están bastante completas, el soporte para submodules de Git todavía no está terminado, hay varios bugs de rendimiento y no cuenta con soporte nativo para flujos de trabajo basados en correo electrónico
  • Antes de la versión 1.0.0 habrá cambios en el flujo de trabajo y cambios incompatibles con versiones anteriores en el formato en disco; para esos cambios de formato se intentará ofrecer una actualización transparente o un comando o script de actualización bajo solicitud
  • Es software de código abierto bajo licencia Apache 2.0, no es un producto respaldado por Google y el soporte lo ofrece la comunidad

2 comentarios

 
xguru 2023-08-03

Jujutsu - un DVCS simple y potente compatible con Git

Lo publiqué hace un año y medio, pero volvió a aparecer como tema en HN.

 
GN⁺ 2023-08-02
Opiniones de Hacker News
  • Me alegra verlo por acá. Me pasé a jj hace unas 2 o 3 semanas y no pienso volver. Tuve que cambiar bastante mi forma mental de trabajar, pero me gusta mucho el flujo que ofrece jj
    Ya no tengo que dedicar tiempo a pensar qué voy a commitear; todos los cambios de archivos se reflejan automáticamente en el commit de la copia de trabajo. Claro, a veces no es lo que quieres, así que hay funciones para dividir un commit en dos o mover cambios, y me gusta que todas las operaciones trabajen sobre commits
    jj log es excelente para ver el mapa completo de varias ramas; puedes resolver solo parte de un conflicto y luego moverte a otra rama, y los ID abreviados de changesets también son cómodos. Por ejemplo, puedes ver en jj log el changeset al que quieres hacer rebase y ejecutar algo como jj rebase -s qr -d master, lo que es más rápido que escribir el nombre completo de la rama

    • Me pregunto qué pasa si por error guardas un archivo con secretos y se lo traga automáticamente
    • Me pregunto si simplemente se usa junto con un repositorio de GitHub
      Dice que puede usarse con backends como Dropbox, pero ojalá aparezca un sistema que se pueda usar fácilmente con IPFS. Para datos grandes no puedes almacenar 1 TB en GitHub, y descarto LFS porque es de pago
      Quien quiera usar un dataset de todos modos lo tendrá localmente, así que IPFS, donde la fuente de descarga no es una sola sino miles, es una solución natural. Si usara IPFS para repositorios de datos, me cambiaría de inmediato; si no, no creo que valga mucho la pena revisarlo
    • Me pregunto si hay casos de equipos que lo estén usando con éxito. Quisiera saber cómo se siente cuando dos personas trabajan en la misma rama
  • Jujutsu empezó como un proyecto personal del autor y ahora es su proyecto de tiempo completo en Google. Se presentó en Git Merge 2022
    Jujutsu: A Git-Compatible VCS - Git Merge 2022:
    Video:
    https://youtu.be/bx_LGilOuE4
    Slides:
    https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...

    • Debe dar bastante orgullo que un proyecto personal se haya convertido en un proyecto de tiempo completo en Google
  • He usado jj todos los días durante casi un año y definitivamente lo considero mucho mejor que la experiencia de usuario de Git. Soy bastante quisquilloso con dividir los cambios en commits atómicos, así que el rebase automático es una función clave
    Como los conflictos se pueden registrar en un commit, no hace falta detenerse a arreglar conflictos a mitad de un rebase como en Git o Mercurial. Aunque modifiques o borres un commit, jj hace rebase automáticamente de todos los commits descendientes y, a diferencia de Git rebase, también maneja múltiples hijos como Mercurial evolve
    Los comandos básicos para manejar el historial de commits también son buenos. Con jj move|restore puedes mover o copiar cambios de un commit a otro, con jj split dividir commits y con jj rebase mover commits. Como todo está dentro del historial de commits, sin árbol de trabajo ni índice, se puede manejar con los mismos comandos y flags
    El log de operaciones también es excelente, así que puedes deshacer la mayoría de las acciones sin problemas. Contrasta con Git, donde para arreglar un mal rebase o un commit --amend tienes que rebuscar el commit correcto en el reflog y corregir refs manualmente
    Lo más incómodo es que a veces el commit automático de la copia de trabajo intenta commitear incluso archivos no deseados, y que no hay detección de renombres, por lo que no maneja las fusiones tan elegantemente como Git. Aun así, los commits rotos se pueden arreglar relativamente fácil con jj split|restore, y los desarrolladores de jj también están pensando en el problema de los renombres

  • Me alegra que, aunque el resultado de una operación genere conflictos, esa información se registre en el commit, la operación se considere exitosa y puedas resolver los conflictos después. He trabajado con algoritmos de edición colaborativa durante unos 10 años y, idealmente, me gustaría que herramientas como Git terminaran siendo reemplazadas por un enfoque basado en CRDT
    Con CRDT se podría hacer pair programming con la misma herramienta y también manejar mejor fusiones complejas, como los autoconflictos que pueden darse en Git. Además, es un modelo más potente
    Sin embargo, los CRDT de texto modernos resuelven automáticamente las ediciones simultáneas sin conflictos, mientras que al colaborar en código offline normalmente quieres que los conflictos se hagan visibles para que una persona los resuelva manualmente. Los CRDT tienen más información sobre el historial de edición que Git, así que en teoría sería posible, pero para hacerlo bien parece que habría que incluir el conflicto mismo en el modelo de datos del archivo de texto, y no estoy seguro de cómo encajaría eso con los editores modernos
    Suena como si jj hubiera encontrado una técnica similar, así que tengo ganas de ver qué tan bien funciona en la práctica. Se siente como un paso más cerca del sueño de un repositorio de código basado en CRDT

    • Valdría la pena revisar Pijul. En la práctica implementa la mayor parte de lo que se menciona aquí. Pijul funciona sobre parches, que son un CRDT, y trata los conflictos como conceptos de primera clase
    • Me pregunto si nunca le has encontrado valor a git bisect
      Si tienes un bug reproducible y la causa es compleja, parece útil encontrar qué commit introdujo el bug y ver qué cambio lo causó. Al menos sirve para tener una primera intuición sobre qué hay que arreglar
      Actualmente git bisect funciona mejor cuando todos los commits se pueden compilar y ejecutar. Si hay commits que contienen marcadores de conflicto y por eso no compilan o no se ejecutan, se necesita mucha más intervención manual
      Me pregunto cómo podría adaptarse una función equivalente a git bisect en este escenario. Simplemente escanear las cadenas de marcadores de conflicto quizá no sea adecuado. Podría haber documentación que explique esos marcadores, o en algunos lenguajes de marcado ======= podría usarse legalmente como subrayado
    • Me pregunto cómo se desarrollará esto en la práctica
      Parece chocar con la idea de que el historial de control de código fuente es un artefacto significativo y que un conjunto de parches debería ordenarse como una secuencia de cambios incrementales y atómicos antes de salir de la máquina del desarrollador
      Dicho eso, la mayoría de los desarrolladores a mi alrededor ya tratan el historial de Git como un historial infinito de deshacer de un editor, así que este enfoque tal vez simplemente consolide ese hecho
      Me pregunto cómo se supone que funcionará el historial a largo plazo y si será más o menos útil que ahora
  • Que la copia de trabajo se confirme automáticamente suena bien al principio, pero hay muchas situaciones en las que no conviene.
    Si se genera un nuevo archivo de salida y todavía no lo agregaste a .gitignore, se confirma automáticamente; y si agregas un archivo ignorado en una rama y luego cambias a otra, puede quedar en la copia de trabajo pero no estar en .gitignore, así que podría confirmarse automáticamente.
    Además, es mucho más fácil preparar solo algunos archivos y confirmarlos que dividir un commit más tarde.

    • Creo que este proyecto desafía la idea común de que preparar solo algunos archivos y confirmarlos es más fácil que dividir un commit después.
      Yo también tengo esa idea metida en la cabeza, pero no sé si realmente sea cierta. En la práctica, es bastante común corregir el índice y hacer amend al commit después, y también se podría hacer antes.
      Si de todos modos no se puede evitar por completo editar a posteriori, quizá sea mejor tener un único flujo de trabajo para ese tipo de edición. Todavía no lo probé, pero me parece un enfoque bastante convincente para cuestionar que una costumbre que dábamos por sentada quizá en realidad no ayude.
    • Pienso igual. Normalmente tengo más de 10 archivos adicionales en el directorio del proyecto, como archivos de salida, notas o scripts descartables. Cuando incluyo archivos en un commit, siempre los elijo por nombre; no meto todo lo nuevo o modificado.
      No tengo ningún caso de uso en el que todos los archivos creados o modificados deban entrar siempre en el commit.
      Que al cambiar a otra rama un archivo quede en la copia de trabajo pero no esté en .gitignore me parece una falla de Git. Debería existir algo como .local.gitignore que se sume a .gitignore. Los archivos que solo necesito ignorar yo son muy comunes y no corresponden al .gitignore del proyecto. Hay formas de hacerlo, pero todas son incómodas; debería ofrecerse algo simple por defecto.
    • Me parece que el primer problema se resolvería comparando fácilmente los archivos ya rastreados con los nuevos.
      Aun así, es interesante. Nunca me gustó el staging de Git, pero como no había pasado por los casos que mencionan aquí, recién ahora entiendo que hace falta una etapa intermedia entre el sistema de archivos y los commits.
    • Por lo que leí en el README, entiendo que si agregas esos archivos a .gitignore, en el siguiente commit automático vuelven a salir del commit anónimo de la copia de trabajo.
      El flujo de que el archivo primero entre al commit anónimo de la copia de trabajo y luego salga, en vez de que nunca se agregue, no es intuitivo; pero si ese commit anónimo casi nunca es visible para el usuario, quizá no haya diferencia.
    • No termino de ver por qué estas cosas serían un problema en el diseño de jj, ni por qué preparar algunos archivos sería más fácil que dividir un commit después.
      La documentación aborda explícitamente varios de los casos que preocupan:
      https://github.com/martinvonz/jj/blob/main/docs/git-comparis...
  • Salvo por algunos casos especiales, hace años que casi no uso Git desde la línea de comandos. En el día a día dependo de la integración incorporada del IDE, como la de IntelliJ, y me cuesta entender por qué alguien se sometería a hacerlo manualmente.
    Dentro del editor puedo elegir líneas concretas para hacer commits parciales, ver todas las ramas, fusionar, hacer cherry-pick, confirmar o hacer amend, traer actualizaciones con pull y editar etiquetas, todo con un atajo de teclado.
    Pero parece que aquí estoy en minoría. Más aún si se considera el ambiente de que Git es una habilidad esencial y que un programador de verdad debería poder teclear los comandos con los ojos cerrados; me da curiosidad saber por qué.

    • Siempre he preferido manejar los sistemas de control de versiones desde la línea de comandos, y seguiré haciéndolo.
      Esa habilidad no depende del entorno: conectarse a una máquina remota para revisar un repositorio es igual que hacerlo en local, no hay que aprender de nuevo cómo integra VCS cada editor, y se pueden usar las herramientas conocidas tal como están.
      Las tareas que mencionas son igual de fáciles desde la línea de comandos, así que no veo la integración con el editor como algo especial. Si necesitas una operación rara y avanzada, como tocar el reflog, es muy probable que al final tengas que abrir la línea de comandos.
      Claro que no tengo nada contra quienes usan GUI. Si con una herramienta eres productivo, no hay nada que juzgar; solo respondo porque preguntaste por qué, y no afirmo que mi forma sea mejor.
    • Mi experiencia es que las integraciones de los IDE normalmente no implementan toda la funcionalidad de Git CLI. Puede que hayan mejorado, pero en cuanto pasas de clone/branch/commit/merge básicos y crecen apenas el número de desarrolladores y ramas, para resolver problemas al final terminas volviendo a Git CLI.
    • No sé si soy un programador de verdad, pero tengo un miedo irracional a que una GUI de Git haga algo incorrecto. Incluso en IntelliJ manejo Git abriendo la CLI integrada. Las viejas costumbres no mueren fácilmente.
    • Mucha gente que recién empieza a usar Git no entiende cómo funciona, es decir, qué son los commits y las ramas y qué se puede hacer con ellos. Luego culpan a la herramienta CLI y esperan que usando GitHub Desktop todo vaya a entenderse.
      Ese es el contexto más común en el que la gente dice “hay que aprender Git CLI”.
      Hace unos días también hubo un ejemplo de alguien que proponía usar GitHub Desktop para no aprender comandos de Git: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...
    • En un equipo solo hay dos tipos de personas que entienden Git: las que usan la CLI y las que empiezan a apretar botones sin entender Git.
  • Gracias al enfoque que pone primero la compatibilidad con Git, Jujutsu se ve más fuerte que otros candidatos a reemplazar Git que he visto hasta ahora.
    Al leer la explicación sobre la compatibilidad con Git, me dio curiosidad que parece centrarse principalmente en el caso de uso en que los usuarios de Jujutsu acceden con jj a repositorios Git alojados en GitHub u otros servicios. Me pregunto si, a la inversa, también se admite acceder con Git a repositorios nativos de Jujutsu.
    Como la mayoría de los desarrolladores ya está familiarizada con la CLI de Git, en operaciones reales probablemente habrá desarrolladores usando jj y Git juntos sobre la misma base de código. O me pregunto si el escenario operativo realista es usar siempre Git como repositorio backend, y no la base de datos nativa de Jujutsu, para permitir el acceso desde ambas CLI.

    • Según una nota al pie del README, actualmente casi no hay razones prácticas para usar el backend nativo.
      Ese backend existe principalmente para garantizar que más adelante se puedan agregar funciones que no serían fáciles de añadir al backend de Git.
    • Parece esperable usar siempre Jujutsu como frontend de un repositorio Git, o bien usar un repositorio remoto completamente basado en Jujutsu.
      Si voy a trabajar y contribuir en un proyecto que ya usa Jujutsu, lo razonable es que yo me adapte al proyecto, no que el proyecto se adapte a mi flujo de trabajo.
  • Gracias al enfoque de compatibilidad con Git primero, Jujutsu se ve más fuerte que otros candidatos a reemplazar Git que he visto hasta ahora.
    Me intriga la gestión de conflictos. Sé que pmeunier puso mucho esfuerzo en formalizar la teoría de parches para Pijul, y los conceptos poco sólidos o problemáticos de parches y conflictos pueden terminar causando grandes problemas. Creo que así surgieron los problemas de rendimiento de Darcs.
    Me gustaría que la página comparativa de la wiki del repositorio explicara el manejo de conflictos de Pijul con un poco más de detalle que simplemente decir que es “similar”.

    • Aquí hay una explicación un poco más detallada: https://github.com/martinvonz/jj/blob/main/docs/technical/co...
      Al almacenar los conflictos simbólicamente de esta forma, más adelante se pueden reproducir los conflictos e incluso resolver automáticamente algunos específicos, pero eso no aborda el problema de resolver el contenido real del conflicto.
      Si alguien lo implementara, quizá se podría usar Pijul como backend de jj para obtener lo mejor de ambos mundos.
  • Qué bien eso de la replicación segura mediante rsync, Dropbox y sistemas de archivos distribuidos. Últimamente me inclino cada vez más por sistemas compatibles con sincronización simple.
    Porque uno puede soltarlos en cualquier lado, con cualquier herramienta, y simplemente funcionan. Siempre da gusto ver que una herramienta nueva tome esta dirección.

    • Me gustaría que se explicara más esta parte.
      He oído que existe este problema, pero no lo he experimentado directamente. Me da curiosidad qué condiciones de carrera pueden surgir entre Git y herramientas como rsync/Dropbox que causen problemas.
  • Qué bueno que la copia de trabajo se confirme automáticamente. Es una función que hacía falta desde hace mucho.
    Aplausos para Martin por dedicar tanto esfuerzo a crear una herramienta elegante.