Git har tjent oss godt. Den har vært et viktig verktøy for å slippe mappe-versjonering av en kodebase, enkelt la flere jobbe mot samme kodebase og gi oss en god oversikt over historikken til et prosjekt. Så er det en gang slik at alt har sin tid - og nå har en mulig arvtager kommet. Jujutsu (med kommandoen jj) heter det, og her skal jeg fortelle litt om hvorfor jeg mener at du bør slutte med git og begynne med jj!
Hva er jujutsu?
Jujutsu er et nytt versjonskontrollverktøy som fungerer sømløst med et git-repo. Du kan se på det som at du bytter ut "porcelain", eller frontend, i git. Motsatt er altså git kun en backend for jujutsu, så du kan fint teste verktøyet og eventuelt bytte tilbake til git, uten å ha tapt historikk eller arbeid.
Når det gjelder læringskurven er det noen designvalg som hjelper deg på veien.
- Jujutsu justerer på den mentale modellen til git, så det føles fortsatt kjent
- Jujutsu jobber med git repos, så du fortsetter med samme kodebrønn som før
- Jujutsu endrer ikke på hvordan commits, branches og lignende oppfører seg i git
Som en følge av de to siste poengene, så kan du fint bruke jujutsu uten at resten av teamet merker det. Selv vil jeg anslå at jeg har brukt jujutsu siden våren 2024, uten at jeg har fått noen kommentarer eller opplevd problemer med hverken git eller github. Det eneste er vel noen spørrende blikk hvis kollegaer kikker litt ekstra på terminalen.
I starten er det bare å holde seg til enkle kommandoer som du vil se under, så har du nok til å komme i gang. Senere kan dette utvides med describe, abandon og aktiv bruk av "revsets" for å jobbe med samlinger av commits. Men til å starte med? Hold det enkelt. Git er uansett tilgjengelig som en trygghet hvis du skulle stå fast.
En liten smakebit
Du oppdager en bug i prod-loggene, som må fikses. Du ser noen feilmeldinger, du vet hva du må gjøre, du vet hvor det må fikses. Med git, så vil du kanskje kjøre
$ git stash
$ git checkout main
$ git pull
$ git checkout -b bugfix-prodfeilMed jj blir det
$ jj git fetch
$ jj new mainDet var alt, du er nå klar for å fikse bugen basert på main. Etterhvert som du har skrevet kode må vi få koden opp til Github. Med git, vil du kjøre noe slikt.
$ git add fil1 fil2
$ git commit -m "Beskrivende commitmelding"
$ git push
$ git switch <forrige branch>Og med jj
# Snarvei for å legge melding på nåværende commit, og lage en ny tom commit
$ jj commit -m "Beskrivende commitmelding"
# '@-' under henviser til foreldrecommit,
# ved at @ er nåværende commit og - er forrige commit. Samme som HEAD~ i git.
$ jj bookmark create bugfix-prodfeil -r @-
$ jj git push
$ jj new <forrige branch>Ganske likt så langt, og det er et viktig poeng - jujutsu er ikke noen stor omveltning i hvordan du håndterer versjonskontroll, det er en evolusjon.
Men git funker jo
Ja, det gjør det, og det vises i spennet av bruksområder. Både hobbyprosjekter og linux-kjernen bruker i bunn og grunn det samme verktøyet for å spore endringer. Når en holder seg til de kjente kommandoene er git heller ikke så veldig i veien. Du lager en branch, du skriver kode, du committer og du pusher. Kanskje man tar en liten rebase i ny og ne? Samtidig kan det være verdt å spørre seg - er det et godt verktøy, eller er det bare godt nok i mangel på alternativer som lar oss samhandle med resten?
Noen små ting vi har akseptert med git er slikt som at branch må opprettes før du setter igang, og ny kode legges på et av to steder(untracked eller unstaged) før det versjoneres manuelt. Dette er ikke store problemer eller smertepunkter, men små ting vi har akseptert, kanskje uten å spørre om det er riktig? Og hvorfor må vi stoppe helt opp for å løse merge conflicten med en gang? Det kan jo være at den egentlig kan løses ved å slå sammen to commits, uten at du må fikse samme conflict to-tre ganger. Så har du også kommandoen `git reset --hard`. Jeg tipper at de fleste har kjørt den minst en gang, for så å kjenne på den litt synkende følelsen i magen når en innser at der var det arbeid som gikk tapt. Hva om vi kunne ha et verktøy som fikser ting som dette, men uten at vi må bytte ut github og gitlab og lære opp alle på nytt?
Ting som er forskjellig fra git
Du pusher commits med hver sin melding, og branches peker på en spesifikk commit. Branches og commits kan rebases på main eller andre commits, alternativt kan du merge inn main hvis du foretrekker det. Du pusher branches til remote git, og kjøre fetch for å hente ting fra remote git. Høres kanskje likt ut?
Commits
Den første "baugen" jeg måtte justere hodet etter er at det er commits, eller changes, som er den primære "tingen" du jobber med. I git er det staging area, som du bruker som grunnlag for commits i branches. Dette viser seg via jj new, som oppretter en ny tom commit. Nåværende commit omtales som Working copy.
$ jj new
Working copy (@) now at: klknywxp 0b23d3db (empty) (no description set)
Parent commit (@-) : puuxlwzk c49a60e5 main | clippy: enable unnecessary_literal_bound lint
$ jj log
@ klknywxp max.mekker@sesamstasjon.no 2025-12-05 23:29:49 0b23d3db
│ (empty) (no description set)
◆ puuxlwzk 45661989+xtqqczze@users.noreply.github.com 2025-12-05 18:07:44 main git_head() c49a60e5
│ clippy: enable unnecessary_literal_bound lintHver gang du kjører en jj kommando, så vil jujutsu ta en snapshot av repoet, og legge til nye filer og endringer i working copy. Du jobber altså ikke lenger med staging area som i git, hvor du velger hvilke filer som skal legges til i hver nye commit. Umiddelbart kan dette høres tungvindt ut - hva om du lager flere endringer, men som du så vil dele opp i flere commits? jj split, som jeg skal vise litt senere, hjelper deg med det.
Og ref dette med commits vs change. Terminologimessig jobber du med "changes", som består av en liste med "commits". Dette er et av valgene jujutsu har tatt som gjør det enklere å spore endringer for en gitt change, men i praksis forholder jeg meg sjeldent til det og snakker fortsatt om commits med kollegaer - også hvis de er nysgjerrige på jujutsu. La oss ta en ting av gangen, og detaljene litt senere.
Branches
Branches kalles bookmarks i jujutsu, og rendyrkes som en merkelapp på en commit. Det betyr at de følger en commit frem til du manuelt flytter dem, altså følger de ikke automatisk med til neste commit når du kjører jj commit slik som de gjør med git commit. For å flytte dem er det kommandoen jj bookmark move, og for å følge en remote branch er det jj bookmark track. Siden bookmarks kun er merkelapp for en commit, så betyr det at du kan vente med å opprette en til rett før du skal pushe til remote. Dette er flyten jeg har lagt meg til, beskrivelse av commits holder fint for oversikt helt frem til jeg skal pushe til remote.
$ jj bookmark create jujutsu-eksempel-branch
$ jj log
@ klknywxp max.mekker@sesamstasjon.no 2025-12-05 23:29:49 jujutsu-eksempel-branch 0b23d3db
│ (empty) (no description set)
◆ puuxlwzk 45661989+xtqqczze@users.noreply.github.com 2025-12-05 18:07:44 main git_head() c49a60e5
│ clippy: enable unnecessary_literal_bound lint
$ jj new
Working copy (@) now at: lmxrrmxl bf797b28 (empty) (no description set)
Parent commit (@-) : klknywxp 0b23d3db jujutsu-eksempel-branch | (empty) (no description set)
$ jj log
@ lmxrrmxl max.mekker@sesamstasjon.no 2025-12-05 23:34:01 bf797b28
│ (empty) (no description set)
○ klknywxp max.mekker@sesamstasjon.no 2025-12-05 23:29:49 jujutsu-eksempel-branch git_head() 0b23d3db
│ (empty) (no description set)
◆ puuxlwzk 45661989+xtqqczze@users.noreply.github.com 2025-12-05 18:07:44 main c49a60e5
│ clippy: enable unnecessary_literal_bound linMerge conflicts
En annen ting som er løst på en praktisk måte er merge conflicts. Når de oppstår får du beskjed om at det har oppstått, og du vil se i loggen(jj log) hvilke commits som har en merge conflict. Når du så fikser en merge conflict blir denne fiksen i så stor grad som mulig videreført av jujutsu til senere commits i historikken, slik at du skal slippe å måtte håndtere den samme konflikten flere ganger. Den er ikke perfekt, men jeg har opplevd vesentlig færre tilfeller hvor jeg må løse samme problem flere ganger i en rebase.ganske digg
Og du må ikke håndtere conflicten med en gang. Den ligger der og må fikses før du kan pushe, men hvis det ikke passer av en eller annen grunn kan du hoppe videre. Eksempelvis har jeg fått merge conflict etter en rebase på siste main, men så underveis bytta over til main for å se på en bug med en kollega som kom bort. Stort sett fikser jeg merge conflicts med en gang, men jeg kan velge når det skjer - i motsetning til git hvor rebase stoppes og du får kontant beskjed om å fikse feilen.
Forskjellene gjør ting enklere
Som forrige avsnitt påpekte, med jujutsu kan du enkelt hoppe fra et punkt i historikken til en annen. Hoppe fra en merge conflict til bugfixing er et eksempel. Et annet eksempel kom senest i dag, hvor jeg eksperimenterte med hvordan vi kunne løse utforming av en klasse. Jeg forsøkte en løsning. Deretter jj new @- når jeg ville forsøke noe nytt og starte med blanke ark. Så en runde til med jj new @- og ny kode. Når jeg så ville sammenligne løsningene kunne jeg enkelt hoppe mellom de nye commitene, før jeg landet på løsningen som jeg gikk videre med. Tidligere har jeg også eksperimentert på eksperimentene, rett og slett laget et lite mulighetstre av løsninger, siden det er såpass lett å starte en ny commit.
Du kan også enkelt justere en eldre commit som ikke er beskytta(eksempelvis fordi den ligger i main). jj new <commitid>, skriv litt kode som oppdaterer den eldre commiten, og kjør jj squash. Senere commits blir automatisk rebaset, bookmarks ligger der fortsatt, og bortsett fra timestamps ser det ut som at du skrev koden i commiten akkurat slik første gang du skrev den. Med git har jeg lagd en ny commit(likt), så kjørt interactive rebase hvor jeg flytter commiten med vim/nano og squasha commiten ved å endre på ordet foran commit'iden. Kort prosess det og. Så lenge det ikke kommer en merge conflict, fordi jeg var borti en linje eller to som ble lagt til i en senere commit.
Andre praktiske eksempler
Først, for å sette igang med git, gå til øverste nivå i ønsket repo og kjør følgende;jj git init --colocate .
jj git init --colocate .Du kan også skrive inn clone-url i stedet for punktum for å klone et nytt repo. Med colocate-flagget vil du ha tilgang til både git og jujutsu i samme repo.
Oppdatere tidligere commits
Si at du har laget en commit, og nå har du skrevet litt nye kode som du ønsker å legge til i forrige commit. Det er ikke mer komplisert enn som så:
// git
$ git commit -a --amend --no-edit
$ git push --force-with-lease
// jujutsu
$ jj squash -u # -u beholder forrige commit-melding, fravær av -u vil la deg endre commit-meldingen
# Nå er både commit og branch automatisk oppdatert
$ jj git pushEn viktig styrke er at squash ikke må kjøre mot forrige commit - den kan kjøre mot en vilkårlig commit som ikke er beskyttet(det vil si, ikke i main, ikke i remote branch som du ikke sporer). Dette kan du spesifisere med --from <ny commitid> og --into <gammel commitid>.
En annen måte å endre på en eldre commit er ved å starte en ny endring på den gamle, noe som er spesielt nyttig hvis du skal flere commits tilbake i tid. Lag da en ny og tom commit på commiten du ønsker å oppdatere, og squash den nye.
$ jj new <commitid til første commit> # new funker både på bookmarks og commitid's
// skriv kode
$ jj squash -u # Eventuelle senere commits i din branch rebases automatisk på den oppdaterte commiten, slik at branchen inneholder den oppdaterte commiten
$ jj git push -b et-branch-navnDu må heller ikke lage en ny commit lengre ned - som jeg skrev over, så kan du bruke flaggene --from og --to. Samtidig er det litt lettere å unngå merge conflicts ved å jobbe med koden slik tilstanden var den gang da. Med git ville du måtte løse det med rebase i to omganger, en interactive for squashing, og en for å flytte de forrige commitene som nå ligger på den gamle commiten.
Rebasing av flere branches
Så har du tilfellet hvor du har to eller flere branches på main. Kollegaer har jobbet flittig, det ligger nye endringer på main, og du ønsker å få med disse endringene i dine branches. Det løses enkelt slik;
$ jj rebase -b 'bookmarks() & author(Oddsund)' -d mainDette tar alle dine lokale branches, og rebaser de på main. bookmarks() henter alle lokale bookmarks, author(navn) velger commits hvor du er forfatter og & finner commits som er felles blant de to - altså branches hvor du har siste commit. Dette er en liten smakebit på "revision set"/"revset" språket til Jujutsu, et kraftig språk for å hente ut spesifikke commits. Andre nyttige snarveier i revsetspråket er @ for nåværende commit/working copy og @- for foreldre commit - disse to bruker jeg mye, og de dukker opp relativt ofte i output fra jujutsu.
Splitte en commit
Hvis du ønsker å dele opp en commit, er dette også ganske rett frem.
$ jj splitDette vil åpne et vindu i terminalen med dette innholdet:

I vinduet har jeg valgt alle endringer, og så valgt bort en linje i eksempel2.txt. Når jeg så fortsetter vil dette opprette to commits, en som inneholder alt jeg har valgt, og en som inneholder resten. Du vil også bli bedt om en beskrivelse for første commit, eventuelt kan du la eksisterende beskrivelse stå.
Legg også merke til tittel her - en commit. Uten noen flag vil denne gå mot working copy, men ved hjelp av -r <changeid> kan du dele opp en tidligere commit. jj split kan dermed gi deg staging area funksjonaliteten som git hadde, hvor du selektivt velger hvilke filer som skal være i commits, for de gangene hvor du trenger det.
Angre en handling
I Jujutsu har du noe som heter operation log, eller oplog for kort. Denne kan minne veldig om git sin reflog, men der hvor reflog kun oppdateres når du endrer tilstanden i repo(bytte branch, rebase, commit etc), så sporer oplog alle handlinger som utføres i et repo. Sletta du en commit ved et uhell? Angre med jj undo. La du nettopp til en fil med en hemmelighet ved et uhell? jj undo og oppdater .gitignore. Alle jujutsu kommandoer du kjører i repo lagres, og kan enkelt omgjøres. For tidligere kommandoer sjekker du jj op log, og så jj op undo <operationid>. Det er en tilsynelatende liten forskjell(tilstand vs handling), men gir enda flere muligheter de gangene man er litt uheldig.
Begrensinger med jujutsu
Det er noen begrensninger sammenlignet med git, så for ryddighetens skyld tar jeg med dem - uavhengig av om jeg har oppelvd det som et problem.
Det er ikke støtte for git submodules, hooks og LFS - men det er issues for alle tre. Det er heller ikke støtte for partial clones, og shallow clones er kun delvis støttet. Tags er også kun delvis støttet - du kan lage tags, men du kan ikke legge til metadata som melding og forfatter. Se mer rundt hva som er støttet og ikke støttet her: https://docs.jj-vcs.dev/latest/git-compatibility/
All den tid dette er et verktøy i et relativt tidlig stadie, så skorter det på grafiske klienter. Så jujutsu er i hovedsak et verktøy for bruk på kommandolinja enn så lenge. Intellij og vscode plugins finnes, men er uoffisielle. Det finnes heller ikke 14 år gamle Stack Overflow diskusjoner som løser problemet du har akkurat nå. Når det er sagt, problemene og spørsmålene jeg har møtt har jeg løst ved hjelp av dokumentasjonen, eller kjappe søk i github issues til jujutsu-repoet.
Gi jujutsu et forsøk!
Min varme anbefaling er at du installerer jujutsu, setter det opp for et eller flere repos du skal jobbe med den neste uka, og forsøker i en uke. For min del var det mer enn nok.
Jujutsu utvider verktøykassa jeg har for versjonshistorikk, samtidig som den gjør det daglige arbeidet lettere. Jeg kan enkelt flytte rundt på og dele opp commits slik at historikken blir slik jeg ønsker den. Jeg krangler mindre med verktøyet. Jeg glemmer ikke å legge til ting i commits. I det hele tatt, jeg har et verktøy som fjerner knirk i hverdagen og spiller på lag med meg.
Og det kan du også få. Det er lov å prøve.