4 Punkte von GN⁺ 2023-08-02 | 2 Kommentare | Auf WhatsApp teilen
  • 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 stash ist 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 describe zum Bearbeiten der Message beliebiger Commits, jj diffedit zum Bearbeiten von Commit-Änderungen ohne Checkout, jj split zum Aufteilen von Commits sowie jj 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 rsync zu 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
  • jj ist 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

 
xguru 2023-08-03

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.

 
GN⁺ 2023-08-02
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 jj angebotene 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 log ist 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 in jj log das Changeset sehen, das man rebasen will, und dann etwas wie jj rebase -s qr -d master ausführen, was schneller ist, als den ganzen Branch-Namen zu tippen.

    • Ich frage mich, was passiert, wenn man versehentlich eine Datei mit geheimen Werten speichert und sie automatisch hineingezogen wird.
    • Ich frage mich, ob man es einfach zusammen mit einem GitHub-Repository verwendet.
      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.
    • Ich frage mich, ob es Teams gibt, die es erfolgreich einsetzen. Ich würde gern wissen, wie es sich anfühlt, wenn zwei Personen auf demselben Branch arbeiten.
  • 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...

    • Dass aus einem persönlichen Projekt bei Google ein Vollzeitprojekt geworden ist, dürfte ziemlich befriedigend sein.
  • 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|restore verschiebt oder kopiert man Änderungen aus einem Commit in einen anderen, mit jj split teilt man Commits auf, und mit jj rebase verschiebt 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 --amend im 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|restore aber 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.

    • Man sollte sich Pijul ansehen. Es implementiert im Grunde das meiste von dem, was hier beschrieben wurde. Pijul arbeitet auf Patches, die CRDTs sind, und behandelt Konflikte als First-Class-Konzept.
    • Ich frage mich, ob du in git bisect noch 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 bisect am 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 bisect an 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.
    • Ich frage mich, wie sich das in der Praxis entwickeln wird.
      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 .gitignore eingetragen 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 .gitignore und 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.

    • Ich sehe darin, dass dieses Projekt die verbreitete Annahme infrage stellt, es sei einfacher, nur einige 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.
    • Sehe ich genauso. Normalerweise liegen in einem Projektverzeichnis mehr als zehn zusätzliche Dateien wie Ausgabedateien, Notizen oder Einmal-Skripte. Wenn ich Dateien in einen Commit aufnehme, wähle ich sie immer nach Dateinamen aus; ich nehme nicht einfach alles Neue oder Geänderte hinein.
      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 .gitignore stehen, halte ich für ein Versagen von Git. Es sollte zusätzlich zu .gitignore so etwas wie eine .local.gitignore geben. Ignore-Dateien, die nur ich brauche, sind sehr verbreitet und gehören nicht in die .gitignore des Projekts. Es gibt zwar Wege dafür, aber sie sind alle umständlich; das sollte standardmäßig einfach bereitgestellt werden.
    • Beim ersten Problem müsste es doch reichen, bereits getrackte Dateien und neue Dateien leicht vergleichen zu können.
      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.
    • Soweit ich das nur aus dem README herausgelesen habe, verstehe ich es so: Wenn man solche Dateien zu .gitignore hinzufü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.
    • Ich verstehe nicht so recht, warum solche Dinge beim Design von jj ein Problem sein sollen, und auch nicht, warum das Stagen einzelner Dateien einfacher sein soll als das spätere Aufteilen eines Commits.
      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.

    • Ich habe es immer bevorzugt, Versionskontrollsysteme über die Kommandozeile zu bedienen, und werde das auch weiterhin tun.
      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.
    • Meine Erfahrung ist, dass IDE-Integrationen normalerweise nicht den vollen Funktionsumfang der Git-CLI implementieren. Das mag besser geworden sein, aber sobald es über grundlegendes clone/branch/commit/merge hinausging und die Zahl der Entwickler und Branches nur ein wenig zunahm, musste man zur Problemlösung am Ende doch wieder zur Git-CLI zurück.
    • Ob ich ein echter Programmierer bin, weiß ich nicht, aber ich habe eine irrationale Angst, dass eine Git-GUI etwas Falsches tun könnte. Auch in IntelliJ bediene ich Git über die integrierte CLI. Alte Gewohnheiten sterben schwer.
    • Viele Menschen, die gerade erst mit Git anfangen, verstehen nicht, wie Git funktioniert – also was Commits und Branches sind und was man damit tun kann. Dann geben sie dem CLI-Tool die Schuld und erwarten, dass mit GitHub Desktop alles verständlich wird.
      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...
    • Im Team gibt es nur zwei Arten von Menschen, die Git verstehen: diejenigen, die die CLI benutzen, und diejenigen, die anfangen, nur Buttons zu drücken, ohne Git zu verstehen.
  • 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?

    • Laut einer Fußnote im README gibt es derzeit kaum praktische Gründe, das native Backend zu verwenden.
      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.
    • Vermutlich wird erwartet, Jujutsu entweder immer als Frontend für ein Git-Repository zu verwenden oder ein vollständig Jujutsu-basiertes Remote-Repository zu nutzen.
      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“.

    • Hier gibt es eine etwas ausführlichere Erklärung: https://github.com/martinvonz/jj/blob/main/docs/technical/co...
      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.

    • Es wäre gut, wenn jemand diesen Punkt näher erklären könnte.
      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.