Jujutsu – Einfaches und leistungsfähiges Git-kompatibles verteiltes Versionsverwaltungssystem
(github.com/martinvonz)- Jujutsu ist ein Versionsverwaltungssystem zum Kopieren von Code in Softwareprojekten, Nachverfolgen von Änderungen und Veröffentlichen von Änderungen. Es ist so konzipiert, dass es für neue und erfahrene Nutzer sowie für neue Einzelprojekte ebenso wie für große Historien und Teams einfach zu verwenden ist
- Die Benutzeroberfläche und die Versionsverwaltungsalgorithmen sind intern vom Speichersystem getrennt, das die Inhalte bereitstellt; derzeit wird ein Git-Repository als Speicherschicht verwendet, wodurch Kompatibilität mit Git-basierten Tools besteht
- In Git werden nur Commits und Dateien gespeichert; Bookmarks (Branches) und weitere übergeordnete Metadaten werden in einem benutzerdefinierten Speicher außerhalb von Git abgelegt
- Das standardmäßige Git-Backend verwendet die Rust-Bibliothek gitoxide, kann Branches aus Git-Remote-Repositories abrufen und in Remotes pushen und erzeugt Commits, die wie normale Git-Commits aussehen
- Es verwendet ein working-copy-as-a-commit-Modell, bei dem die Arbeitskopie als echter Commit repräsentiert wird; Dateiänderungen werden automatisch als normaler Commit aufgezeichnet und bei weiteren Änderungen jeweils per amend aktualisiert
- Befehle schlagen nicht wegen einer dirty working copy fehl
git stashist nicht nötig- Schon bevor die Arbeit abgeschlossen ist, kann für den Arbeitskopie-Commit eine Commit-Message gesetzt werden
- Alle Operationen im Repository werden zusammen mit einem Snapshot des Repository-Zustands nach der Operation aufgezeichnet; dadurch sind das Wiederherstellen früherer Repository-Zustände, Undo auf Operationsebene und das Revert einer bestimmten Operation möglich, die nicht die neueste sein muss
- Konflikte werden wie Commits als First-Class-Objekte aufgezeichnet; auch Operationen, bei denen Konflikte entstehen, gelten als erfolgreich und können anschließend in einem einheitlichen Workflow zur Konfliktlösung behandelt werden
- Wird ein Commit geändert, werden alle Descendants des bestehenden Commits automatisch auf den neuen Commit rebased; auch Ergebnisse der Konfliktlösung werden an Descendants weitergegeben, und Bookmarks sowie Arbeitskopien, die auf rebased Commits zeigen, werden aktualisiert
- Es gibt keinen expliziten Index und keine Staging Area; zur Auswahl von Commits wird eine Revset-Sprache verwendet, und Ausgabeformate werden über eine vom Nutzer konfigurierbare Template-Sprache verarbeitet
- Für das Umschreiben der Historie bietet es
jj describezum Bearbeiten der Message beliebiger Commits,jj diffeditzum Bearbeiten von Commit-Änderungen ohne Checkout,jj splitzum Aufteilen von Commits sowiejj squash -i --from X --into Y, um Teile von Änderungen in einen anderen Commit zu verschieben - Sichere gleichzeitige Replikation ist ein experimentelles Feature; sie ist dafür ausgelegt, bei der Nutzung verteilter Dateisysteme oder Backup-Tools wie Dropbox oder
rsynczu verhindern, dass das Repository in einen beschädigten Zustand gerät, und zielt im schlimmsten Fall darauf ab, Konflikte zwischen lokalem und Remote-Zustand offenzulegen jjist ein experimentelles Versionsverwaltungssystem; die Git-Kompatibilität ist stabil, es kann aber noch Features in Arbeit, nicht optimale UX und Lücken in Workflows für bestimmte Nutzungsweisen geben- Der Funktionsumfang ist weitgehend vollständig, aber die Unterstützung für Git-Submodule ist noch nicht abgeschlossen, es gibt mehrere Performance-Bugs, und es gibt keine native Unterstützung für E-Mail-basierte Workflows
- Vor 1.0.0 sind Änderungen an Workflows und nicht abwärtskompatible Änderungen am On-Disk-Format vorgesehen; bei Formatänderungen wird versucht, transparente Upgrades oder auf Anfrage Upgrade-Befehle bzw. -Skripte bereitzustellen
- Es ist Open-Source-Software unter der Apache-2.0-Lizenz, kein von Google unterstütztes Produkt; Support wird von der Community geleistet
2 Kommentare
Jujutsu - ein einfaches und leistungsstarkes Git-kompatibles DVCS
Ich habe das vor anderthalb Jahren gepostet, und jetzt ist es bei HN wieder zum Thema geworden.
Meinungen auf Hacker News
Schön, das hier zu sehen. Ich bin vor etwa 2–3 Wochen zu jj gewechselt und habe nicht vor, zurückzugehen. Ich musste meine Arbeitsweise im Kopf ziemlich umstellen, aber der von
jjangebotene Flow gefällt mir ziemlich gut.Die Zeit, in der ich gesondert darüber nachdenke, was ich committen will, fällt weg, und alle Dateiänderungen landen automatisch im Working-Copy-Commit. Natürlich ist das manchmal unerwünscht, daher gibt es Funktionen, um Commits aufzuteilen oder Änderungen zu verschieben; gut gefällt mir, dass alle Operationen auf Commits arbeiten.
jj logist großartig, um die gesamte Karte mehrerer Branches zu sehen; Konflikte kann man nur teilweise lösen und dann zu einem anderen Branch wechseln, und auch die verkürzten Changeset-IDs sind bequem. Man kann zum Beispiel injj logdas Changeset sehen, das man rebasen will, und dann etwas wiejj rebase -s qr -d masterausführen, was schneller ist, als den ganzen Branch-Namen zu tippen.Es heißt, dass man es mit Backends wie Dropbox nutzen kann; schön wäre ein System, das sich leicht mit IPFS verwenden lässt. Bei großen Datenmengen kann man nicht 1 TB auf GitHub speichern, und LFS lasse ich außen vor, weil es kostenpflichtig ist.
Wer einen Datensatz verwenden will, hat ihn ohnehin lokal, daher ist IPFS, bei dem es nicht eine, sondern Tausende Download-Quellen gibt, eine natürliche Lösung. Wenn es IPFS für Datenspeicher nutzt, würde ich sofort wechseln; andernfalls sehe ich wenig Grund, es mir näher anzuschauen.
Jujutsu begann als persönliches Projekt des Autors und ist inzwischen bei Google zum Vollzeitprojekt des Autors geworden. Es wurde auf der Git Merge 2022 vorgestellt.
Jujutsu: A Git-Compatible VCS - Git Merge 2022:
Video:
https://youtu.be/bx_LGilOuE4
Slides:
https://docs.google.com/presentation/d/1F8j9_UOOSGUN9MvHxPZX...
Ich nutze jj seit fast einem Jahr täglich und halte es definitiv für deutlich besser als die Git-User-Experience. Ich bin recht pingelig dabei, Änderungen in atomare Commits aufzuteilen, daher ist automatisches Rebase für mich eine Kernfunktion.
Konflikte können in Commits aufgezeichnet werden, sodass man nicht wie bei Git oder Mercurial mitten im Rebase anhalten und Konflikte beheben muss. Wenn man Commits ändert oder löscht, rebaset jj automatisch alle Nachfolge-Commits; anders als Git rebase kann es, ähnlich wie Mercurial evolve, auch mehrere Kinder verarbeiten.
Auch die grundlegenden Befehle zum Bearbeiten der Commit-Historie sind gut. Mit
jj move|restoreverschiebt oder kopiert man Änderungen aus einem Commit in einen anderen, mitjj splitteilt man Commits auf, und mitjj rebaseverschiebt man Commits. Da alles ohne Working Tree oder Index in der Commit-Historie liegt, lässt es sich mit denselben Befehlen und Flags bearbeiten.Auch das Operations-Log ist hervorragend, sodass man die meisten Aktionen problemlos rückgängig machen kann. Das steht im Gegensatz zu Git, wo man nach einem schlechten Rebase oder
commit --amendim Reflog den passenden Commit suchen und Refs manuell korrigieren muss.Am störendsten ist, dass der automatische Working-Copy-Commit manchmal auch unerwünschte Dateien committen will, und dass es keine Umbenennungserkennung gibt, sodass Merges nicht so elegant behandelt werden wie in Git. Kaputte Commits lassen sich mit
jj split|restoreaber relativ leicht reparieren, und auch die jj-Entwickler denken über das Umbenennungsproblem nach.Ich begrüße, dass bei Konflikten durch eine Operation diese Information im Commit aufgezeichnet wird, die Operation als erfolgreich gilt und man die Konflikte später lösen kann. Ich habe in den letzten etwa zehn Jahren an Algorithmen für kollaboratives Editing gearbeitet, und idealerweise würden Tools wie Git irgendwann durch einen CRDT-basierten Ansatz ersetzt.
Mit CRDTs könnte man mit demselben Tool Pair Programming betreiben und auch komplexe Merges wie Selbstkonflikte, die in Git entstehen können, besser handhaben. Es ist auch ein leistungsfähigeres Modell.
Moderne Text-CRDTs lösen gleichzeitige Bearbeitungen jedoch automatisch konfliktfrei auf; bei Offline-Code-Zusammenarbeit möchte man normalerweise, dass Konflikte sichtbar werden und ein Mensch sie manuell löst. Da CRDTs mehr Informationen über die Edit-Historie besitzen als Git, sollte das theoretisch möglich sein, aber um es richtig zu machen, müsste man Konflikte wohl selbst in das Datenmodell von Textdateien aufnehmen; wie das mit modernen Editoren zusammenspielen würde, ist mir nicht ganz klar.
Es klingt, als hätte jj einen ähnlichen Kniff gefunden, daher bin ich gespannt, wie gut es in der Praxis funktioniert. Es fühlt sich wie ein Schritt näher an den Traum eines CRDT-basierten Code-Repositorys an.
git bisectnoch nie einen Nutzen gesehen hast.Wenn es einen reproduzierbaren Bug gibt und die Ursache komplex ist, ist es doch nützlich herauszufinden, welcher Commit den Bug eingeführt hat, und sich anzusehen, welche Änderung der Auslöser war. Zumindest bekommt man so ein erstes Gefühl dafür, was repariert werden muss.
Derzeit funktioniert
git bisectam besten, wenn alle Commits gebaut und ausgeführt werden können. Wenn ein Commit Konfliktmarker enthält und sich dadurch nicht bauen oder ausführen lässt, ist deutlich mehr manuelles Eingreifen nötig.Ich frage mich, wie man eine Entsprechung zu
git bisectan dieses Szenario anpassen könnte. Einfach nach Konfliktmarker-Strings zu scannen, ist möglicherweise nicht angemessen. In Dokumenten können Konfliktmarker erklärt werden, und in manchen Markup-Sprachen kann=======legal als Unterstreichung verwendet werden.Es scheint im Widerspruch zu der Vorstellung zu stehen, dass die Historie der Quellverwaltung ein sinnvolles Artefakt ist und ein Bündel von Patches zu einer Folge inkrementeller, atomarer Änderungen aufgeräumt werden sollte, bevor es die Entwicklungsmaschine verlässt.
Allerdings behandeln die meisten Entwickler in meinem Umfeld die Git-Historie bereits wie eine unendliche Undo-Historie des Editors, und dieser Ansatz könnte diese Tatsache einfach verfestigen.
Ich frage mich, wie langfristige Historie dabei funktionieren soll und ob sie nützlicher oder weniger nützlich wäre als heute.
Dass die Arbeitskopie automatisch committet wird, wirkt anfangs gut, führt aber in vielen Situationen zu Problemen.
Wenn eine neue Artefaktdatei entsteht, bevor sie in
.gitignoreeingetragen wurde, wird sie automatisch committet; und wenn man auf einem Branch eine Ignore-Datei hinzufügt und dann zu einem anderen Branch wechselt, kann sie in der Arbeitskopie bleiben, steht dort aber nicht in.gitignoreund kann daher automatisch committet werden.Außerdem ist es viel einfacher, nur bestimmte Dateien zu stagen und zu committen, als später einen Commit aufzuteilen.
Auch ich habe diesen Gedanken fest im Kopf, weiß aber nicht, ob er wirklich stimmt. In der Praxis kommt es ziemlich häufig vor, dass man den Index korrigiert und einen Commit nachträglich per amend ändert; man könnte das auch im Vorfeld tun.
Wenn sich nachträgliche Bearbeitung ohnehin nicht vollständig vermeiden lässt, wäre dann nicht ein einheitlicher Workflow für solche Bearbeitungen besser? Ich habe es noch nicht ausprobiert, aber der Ansatz wirkt ziemlich plausibel darin, eine für selbstverständlich gehaltene Konvention anzustechen, die womöglich gar nicht hilfreich ist.
Einen Use Case, bei dem alle erzeugten oder geänderten Dateien immer in einen Commit gehören, habe ich nicht.
Dass Dateien beim Wechsel auf einen anderen Branch in der Arbeitskopie bleiben, aber nicht in
.gitignorestehen, halte ich für ein Versagen von Git. Es sollte zusätzlich zu.gitignoreso etwas wie eine.local.gitignoregeben. Ignore-Dateien, die nur ich brauche, sind sehr verbreitet und gehören nicht in die.gitignoredes Projekts. Es gibt zwar Wege dafür, aber sie sind alle umständlich; das sollte standardmäßig einfach bereitgestellt werden.Trotzdem interessant. Ich mochte das Staging von Git nie, aber die hier genannten Fälle habe ich noch nicht erlebt; erst jetzt verstehe ich, dass zwischen Dateisystem und Commit eine Zwischenstufe nötig ist.
.gitignorehinzufügt, fallen sie beim nächsten automatischen Commit wieder aus dem anonymen Arbeitskopie-Commit heraus.Der Ablauf, dass eine Datei nicht von Anfang an ausgeschlossen wird, sondern zunächst in den anonymen Arbeitskopie-Commit hineingerät und dann wieder herausfällt, ist nicht intuitiv; wenn dieser anonyme Commit für die meisten Nutzer aber unsichtbar bleibt, dürfte es keinen Unterschied machen.
In der Dokumentation werden mehrere der befürchteten Fälle ausdrücklich behandelt:
https://github.com/martinvonz/jj/blob/main/docs/git-comparis...
Abgesehen von ein paar Spezialfällen nutze ich Git seit Jahren kaum noch über die Kommandozeile. Im Alltag verlasse ich mich auf die integrierte IDE-Integration in Tools wie IntelliJ und verstehe schwer, warum man sich das absichtlich manuell antut.
Im Editor kann ich zeilenweise auswählen und Teil-Commits erstellen, alle Branches ansehen, mergen und cherry-picken, committen oder amend ausführen, Updates pullen und Tags bearbeiten – alles mit einem Tastenkürzel.
Allerdings scheine ich hier in der Minderheit zu sein. Erst recht, wenn man die Stimmung berücksichtigt, Git sei eine unverzichtbare Fähigkeit und echte Programmierer müssten die Befehle im Schlaf tippen können; mich würde interessieren, warum das so ist.
Diese Fertigkeit ist umgebungsunabhängig: Auf einer entfernten Maschine ein Repository anzusehen ist dasselbe wie lokal, man muss nicht für jeden Editor neu lernen, wie dessen VCS-Integration funktioniert, und man kann die vertrauten Werkzeuge weiterverwenden.
Die genannten Aufgaben sind über die Kommandozeile genauso einfach möglich, daher sehe ich Editor-Integrationen nicht als etwas Besonderes. Wenn man seltsame, fortgeschrittene Dinge wie das Bearbeiten des reflog braucht, muss man am Ende wahrscheinlich ohnehin zur Kommandozeile greifen.
Natürlich habe ich nichts gegen Leute, die eine GUI nutzen. Wenn man mit einem Werkzeug produktiv ist, ist das nicht zu verurteilen; du hast aber gefragt, warum, also habe ich geantwortet. Ich behaupte nicht, dass meine Art besser ist.
Das ist der häufigste Kontext, in dem Leute sagen: „Man muss die Git-CLI lernen.“
Vor ein paar Tagen gab es ebenfalls ein Beispiel dafür, GitHub Desktop verwenden zu wollen, um keine Git-Befehle lernen zu müssen: https://www.reddit.com/r/learnprogramming/comments/15b7pra/s...
Dank seines Ansatzes, Git-Kompatibilität in den Vordergrund zu stellen, wirkt Jujutsu stärker als die meisten Git-Ersatzkandidaten, die ich bisher gesehen habe.
Beim Lesen der Erklärung zur Git-Kompatibilität fragte ich mich, ob der Fokus vor allem auf dem Anwendungsfall liegt, dass Jujutsu-Nutzer mit jj auf Git-Repositories zugreifen, die etwa auf GitHub gehostet werden. Umgekehrt interessiert mich, ob auch unterstützt wird, mit Git auf native Jujutsu-Repositories zuzugreifen.
Die meisten Entwickler sind bereits mit der Git-CLI vertraut, daher dürfte es im realen Betrieb Entwickler geben, die jj und Git gemeinsam auf derselben Codebasis verwenden. Oder ist das realistische Betriebsszenario, für CLI-Zugriff auf beiden Seiten immer Git als Backend-Repository zu nutzen, statt einer nativen Jujutsu-Datenbank?
Dieses Backend existiert vor allem, um sicherzustellen, dass später Funktionen hinzugefügt werden können, die sich nicht leicht in das Git-Backend integrieren lassen.
Wenn ich an einem Projekt arbeite und dazu beitrage, das bereits Jujutsu verwendet, ist es sinnvoll, dass ich mich an das Projekt anpasse – nicht dass das Projekt sich an meinen Workflow anpasst.
Dank des Ansatzes Git-Kompatibilität zuerst wirkt Jujutsu stärker als andere Git-Ersatzkandidaten, die ich bisher gesehen habe.
Mich interessiert das Konfliktmanagement. Ich weiß, dass pmeunier viel Aufwand betrieben hat, um für Pijul die Patch-Theorie formal auszuarbeiten, und unsolide oder problematische Konzepte von Patches und Konflikten können zu großen Problemen führen. Ich meine, so seien auch die Performance-Probleme von Darcs entstanden.
Es wäre gut, wenn die Vergleichsseite im Repository-Wiki Pijuls Konfliktbehandlung etwas detaillierter erklären würde als nur mit „ähnlich“.
Konflikte auf diese Weise symbolisch zu speichern ermöglicht es, sie später zu reproduzieren und bestimmte Konflikte vielleicht sogar automatisch zu lösen; das eigentliche Auflösen des Konfliktinhalts selbst behandelt es aber nicht.
Wenn das jemand implementiert, könnte man vielleicht Pijul als jj-Backend verwenden und so die Vorteile beider Seiten kombinieren.
Sicheres Klonen über rsync, Dropbox oder verteilte Dateisysteme klingt gut. In letzter Zeit neige ich immer mehr zu Systemen, die einfache Synchronisierung unterstützen.
Denn man kann sie mit beliebigen Tools irgendwohin werfen, und sie funktionieren einfach. Es ist immer erfreulich, wenn ein neues Tool diesen Weg einschlägt.
Ich habe gehört, dass es solche Probleme gibt, sie aber nie selbst erlebt. Mich interessiert, welche Race Conditions zwischen Git und Tools wie rsync/Dropbox entstehen und Probleme verursachen.
Dass die Working Copy automatisch committet wird, ist erfreulich. Das ist eine Funktion, die schon lange nötig war.
Applaus für Martin, der sich die Mühe gemacht hat, ein elegantes Tool zu bauen.