Jujutsu : Git sans les parties qui font mal

Un VCS compatible Git qui annule tout, rebase tout, et laisse les conflits tranquilles

Tout personne ayant utilisé git sur une plus ou longue durée a fini par faire un: on copie/coller tout le repo et on fait un gros push -f, pour pouvoir continuer de travailler en paix!

Au début tu essaie de faire bien, quand tu as pris la peine de souffrir la lecture de la doc git. Le git reflog quand un rebase foire. Le git stash list avec 47 stashes dont je sais plus lequel est quoi. Le git rebase --interactive qui me demande de résoudre 6 conflits dans la foulée. Le detached HEAD quand je voulais juste regarder une branche. Mais vite tu retombe sur le copie/colle et push -f, dégeu mais ça règle le problème!

Et à chaque fois, la même question revenait : mais pourquoi c’est aussi chiant ?

Puis j’ai vue passer Jujutsu. Le command c’est jj. C’est un VCS qui utilise Git comme data-layer. L’idée principal: arrêter de faire mal aux mouches et avoir un outil simple pensé pour la prod aujourd’hui. Pas de staging area. Pas de stash. Pas de rebase interactif de 15 minutes. Un bouton jj undo qui fonctionne sur n’importe quelle opération.

Et le tout pouvant être déployé en opt-in.

C’est là mes définitions et si elles vous déplaisent, cassez-vous je suis pas là pour discuter lexique.

  • Jujutsu (jj): VCS compatible Git, écrit en Rust, créé par Google. Interface alternative aux commandes Git. 30k+ stars sur GitHub.
  • Change: l’unité de travail chez jj. Un change a un ID stable (change-id) qui survit aux rebases/amends. Contrairement au commit hash de Git qui change à chaque modification.
  • Bookmark: l’équivalent d’une branche Git. Un label mutable qu’on place manuellement sur un commit. Optionnel — pas obligatoire pour travailler.
  • Working copy commit: chez jj, le working copy EST un commit. Pas besoin de git add ni git commit séparé.
  • Operation log: journal de toutes les opérations. jj undo inverse la dernière opération. Chaque commande est enregistrée.
  • Colocated repo: mode où .jj et .git coexistent dans le même répertoire. Tu peux utiliser jj et git en même temps.
  • First-class conflict: les conflits sont enregistrés comme des données dans les commits, pas comme un état bloquant.

Putain, c’est la bonne question.

La réponse tient en un mot : Git est un bon outil avec une horrible interface. Le stockage, le modèle de commits, la distribution — tout est bien pensé techniquement. Mais l’interface CLI ? C’est un crime contre la sanité mental des tech! Une ode à la maxime shaddokienne “Pourquoi faire simple, quand on peut faire compliqué?!”

jj ne remplace pas Git. C’est du sugar-coating. En local, tu utilises jj à la place de git. En remote, les commits que jj crée sont des commits Git standards. Tu push vers GitHub, GitLab, n’importe quel remote. Tes collègues continuent à utiliser git normalement. La CI/CD voit des commits Git.

L’adoption est individuelle. Pas de migration. Pas de “la semaine prochaine on migre!”. Tu installes jj en local, tu l’utilises sur un repo existant, et si t’aimes pas, tu arrêtes. Le repo reste un repo Git.

C’est comme passer de vim à neovim. Même base, meilleur UX.


Si vous avez cargo:

0# Linux (cargo)
1$ cargo install --locked jujutsu

Si vous pouvez prendre le binaire compilé, ou le compiler vous-même.

Le binaire fait ~10 Mo. Pas de dépendances. Pas de daemon. C’est un outil CLI, pas un framework.


C’est ici que jj brille par rapport à tout autre “Git killer”. Le mode colocated permet d’avoir .jj ET .git dans le même répertoire.

0# Sur un repo Git existant :
1$ jj git init --colocate
2
3# Ou cloner directement :
4$ jj git clone https://github.com/jj-vcs/jj

Maintenant, dans ce répertoire :

  • jj status fonctionne
  • git status fonctionne aussi
  • Les deux voient les mêmes commits
  • Tu peux switch entre jj et git à tout moment

C’est la clé de l’adoption en entreprise. Pas de big bang ou de migration de toute la boite à organiser. Tu l’installes pour toi, tu l’utilises, et si ça marche, tu en parles. Si ça marche pas, tu git et c’est tout.

Si tu est VP/Head of/Program Manager, tu fais un petit programme pilote, sans risque ni surcoût autre que l’apprentissage de quelques heure. Et une fois réussi, quelques prez internes et tu phagocite toute la tech une équipe à la fois!


En Git, le workflow c’est : modifier → git addgit commit. La staging area, c’est un état intermédiaire qui apporte presque rien appart des emmerdes. Tu as oublié de stage un fichier ? Tu stage trop ? Tu te retrouves avec un commit incomplet.

En jj, le working copy est un commit. Tu modifies des fichiers, jj les snapshot automatiquement, et tu fige (describe en jj) quand t’es prêt.

0# En Git :
1$ git add src/auth.go src/auth_test.go
2$ git commit -m "feat: add auth"
3
4# En jj :
5$ jj describe -m "feat: add auth"
6# C'est tout. jj a déjà snapshot tes changements.

Pas de git add. Pas de git add -p. Pas de git add --all. Le working copy est toujours commité. Et c’est tout.

Si tu veux séparer tes changements en plusieurs commits :

0$ jj split    # Sépare le working copy en deux commits
1$ jj squash   # Déplace des changements vers le commit parent

C’est plus flexible que git add -p et plus sûr. Tu commites tout, et tu organises après.

C’est la feature qui m’a convaincu. jj undo fonctionne sur n’importe quelle opération.

 0# Rebase raté ?
 1$ jj undo
 2
 3# Abandonné la mauvaise branche ?
 4$ jj undo
 5
 6# Push accidentel sur main ?
 7$ jj undo
 8
 9# Revenir à l'état d'il y a 5 opérations ?
10$ jj op log          # Voir l'historique des opérations
11$ jj op restore @-   # Restaurer l'état précédent

En Git, git reflog existe mais c’est un outil forensic. Faut comprendre comment Git marche, fouiller dans les hashes, et espérer que t’as la bonne référence. C’est comme déchiffrer un codex pour retrouver ton mdp WiFi.

Chez jj, c’est une commande. jj undo. Simple, basique!

C’est le truc qui rend les stacked changes utilisables.

0# Tu as un stack de 5 commits :
1A → B → C → D → E
2
3# Tu modifies le commit C :
4$ jj edit ccccc

A, B, D, E sont automatiquement rebasés. Pas de git rebase -i. Pas de --update-refs. Les bookmarks suivent automatiquement.

En Git, corriger un commit au milieu d’un stack de 10 commits, c’est une aventure. git rebase -i, marquer edit, amend, rebase --continue, répéter pour chaque commit descendant, résoudre les conflits à chaque étape.

En jj ? Tu modifies le commit. Les descendants se rebasent tout seuls. Si un conflit apparaît, il est enregistré dans le commit et tu résous plus tard.

En Git, un merge conflict met le repo dans un état mi-fait. Tu peux rien faire. Tu peux pas commit. Tu peux pas switch de tâche. Tu peux pas abandonner. Tu dois résoudre maintenant.

En jj, un conflit = une donnée dans le commit. Tu peux :

  • Résoudre le conflit
  • Continuer à travailler sur autre chose
  • Revenir résoudre le conflit demain
0# Le conflit est dans le commit. Pas bloquant.
1$ jj status    # Montre le conflit
2$ jj resolve   # Résout les conflits du commit courant

C’est comme ça que les conflits devraient marcher depuis le début. Tu bloques pas un dev parce que deux branches touchent le même fichier. Tu enregistres le conflit et tu résous quand t’as le temps!

En Git, le commit hash change à chaque amend. Tu note un hash dans un ticket, tu amend le commit, le hash n’est plus bon.

En jj, le change-id est stable. Amend, rebase, edit — le hash Git change, mais le change-id reste.

0$ jj log
1@ qpvuntsm d3934288 feature-auth
2│  feat: add auth
3├── mzkmortm 8a7bcdef fix: typo in auth
4├── yostqsxw 3e2d1f0a feat: add user model
5└── rokmyywl 1b2e3c4d initial commit

Les IDs à 8 caractères (qpvuntsm, mzkmortm…) sont les change-IDs. Stables. Prédictibles. Tu peux les référencer dans des scripts, dans des tickets, dans ta mémoire. Ils changent jamais.


 0# Git                          # jj
 1# ──────────────────────────── ────────────────────────────
 2# git clone <url>              jj git clone <url>
 3# git init                     jj git init --colocate
 4# git add <file>               (pas nécessaire)
 5# git commit -m "..."          jj describe -m "..."
 6# git log                      jj log
 7# git diff                     jj diff
 8# git status                   jj status
 9# git checkout <branch>        jj checkout <change>
10# git switch -c <branch>       jj new
11# git stash                    (pas nécessaire)
12# git rebase -i                jj squash -i
13# git reset HEAD~1             jj undo
14# git reflog                   jj op log
15# git push                     jj git push --bookmark <name>

Les lignes où jj dit “pas nécessaire”, c’est là que jj supprime de la complexité. Pas de staging. Pas de stash. Pas de detached HEAD. Le workflow est plus court.


Le fichier de config se trouve: ~/.config/jj/config.toml

 0[ui]
 1default-commit-description = "WIP: no description set"
 2pager = "delta"  # ou "less"
 3
 4[git]
 5colocate = true
 6
 7[aliases]
 8l = ["log", "--limit", "20"]
 9s = ["status"]
10d = ["diff"]

La config minimale tient en queqlues lignes. Pas de .gitconfig de 200 lignes. Pas de git-extras pour avoir des alias basic.


jj est encore tout jeune, et immature. Il a quelques problèmes :

  • IDE support limité — JetBrains a un plugin en cours de dev, VS Code passe par le terminal. Le colocated mode permet de garder le support Git existant pour l’IDE.
  • Submodules non supportés — si ton projet utilise des git submodules, jj ne les gère pas encore. Workaround : jj pour le reste, git pour les submodules.
  • Courbe d’apprentissage — le modèle mental est différent. Pas de git add, pas de branches obligatoires, les change-IDs au lieu des hashes. C’est plus simple une fois compris, mais faut le temps d’appréhender.
  • Écosystème plus petit — moins de plugins, moins de tutos, moins de réponses Stack Overflow. Mais jj est compatible Git, donc tous les outils Git fonctionnent.

Le meilleur argument pour jj, c’est le coût d’essai : zéro.

  • Installer jj sur 2-3 devs volontaires
  • Utiliser en colocated mode sur un projet existant
  • Documenter les gains et les frictions
  • Pas de pression, pas de deadline
  • Chaque dev intéressé installe jj en local
  • Session de partage d’expérience (30 min)
  • Les CI/CD, les PRs, les reviews ne changent pas
  • jj devient le VCS recommandé (pas obligatoire)
  • Documentation interne (cheatsheet, FAQ)
  • Monitoring des retours d’expérience

Si jj te plaît pas ? Tu arrêtes d’utiliser jj. Le repo reste un repo Git. Aucune donnée n’est perdue. Tes collègues ne sont pas affectés.

C’est la sécurité ultime. Tu peux pas planter l’infra en essayant un outil local.


jj n’est pas un “Git killer”. C’est une interface pensée pour des humains. Il résout les problèmes quotidiens sans toucher à l’écosystème existant.

Le maître mot est “dev-experience”, et les features tournent autour.

Thoughtworks l’a mis dans leur Technology Radar en “Assess”. Google l’utilise en interne. Martin von Zweigbergk, son créateur, le développe à temps plein depuis 2021.

Essaie jj sur un de tes projets pendant une/deux semaine. Un repo existant, en colocated mode. Si t’aimes pas, tu arrêtes. Le repo reste un repo Git.

See ya space-cowboy!


A minimalist illustration of a katana sword slicing cleanly through a tangled knot of Git command-line concepts: branch icons, merge conflict symbols, a staging area box, and a stash icon — all being cut apart and falling away. Behind the katana, a clean, simple workflow line flows straight from "edit" to "describe" to "push" without obstacles. Dark navy background (#0d1117). The katana has a subtle teal glow along its edge. Style: clean flat vector, Japanese ink wash influence, tech blog header aesthetic. No text, no logos, no watermarks. 16:9 aspect ratio.