16 Punkte von GN⁺ 2026-01-29 | 1 Kommentare | Auf WhatsApp teilen
  • Um die interne Struktur eines Versionsverwaltungssystems zu verstehen, wurde direkt ein Git-ähnliches System implementiert
  • SHA-256-Hashing und zstd-Komprimierung ersetzen Git's SHA-1 und zlib; das Repository ist in einer .tvc-Verzeichnisstruktur organisiert
  • Geschrieben in Rust, mit schrittweiser Implementierung von Datei-Hashing, Komprimierung, Commit und Checkout
  • Commit-Objekte enthalten Tree-Hash, Parent-Commit, Autor und Nachricht; identische Dateien werden dank Hash-Deduplizierung nicht erneut gespeichert
  • Dabei wird unmittelbar erfahrbar, dass Git ein inhaltsadressierter Dateispeicher ist, und die Bedeutung strukturierter Datenformate wird hervorgehoben

Hashing und Komprimierung

  • Git identifiziert alle Objekte über SHA-1-Hashes, in diesem Projekt wird jedoch SHA-256 verwendet
    • SHA-1 ist alt und sicherheitstechnisch anfällig, aber in diesem Projekt dient es nur zur Identifikation von Dateiinhalten, daher ist Sicherheit nicht entscheidend
  • Anstelle von Gits zlib kommt die zstd-Komprimierungsbibliothek von Facebook zum Einsatz
    • zstd wurde als effizienter eingeschätzt, Git-Kompatibilität war kein Ziel
  • Der Projektname lautet „tvc (Tony’s Version Control)”; .tvc und .tvcignore übernehmen die entsprechenden Rollen von Git

Implementierungsschritte

  • Die Umsetzung erfolgte in der Reihenfolge Kommandozeilenargumente lesen → Ignore-Regeln lesen → Dateiliste ausgeben → Hashing und Komprimierung → Tree- und Commit-Erzeugung → HEAD-Verwaltung → Commit-Checkout
  • Das Projekt ist in Rust geschrieben; der ls-Befehl durchsucht rekursiv alle nicht ignorierten Dateien unter Anwendung der .tvcignore-Regeln und gibt für jede Datei den SHA-256-Hash aus
  • Mit der zstd-Bibliothek wurden Funktionen zum Komprimieren und Dekomprimieren von Dateien einfach umgesetzt

Commit-Struktur

  • Ein Commit-Objekt enthält folgende Informationen
    1. Objekttyp („commit”)
    2. Den Zustand des Dateisystems zu diesem Zeitpunkt (Tree-Hash)
    3. Den vorherigen Commit (HEAD)
    4. Den Autor (author)
    5. Die Commit-Nachricht
  • Anders als bei Git wird nicht zwischen Autor und Committer unterschieden, Merge- oder Rebase-Funktionen wurden nicht implementiert
  • Beim Erzeugen eines Commits werden Tree-Objekte erstellt, gehasht, komprimiert und in .tvc/objects/ gespeichert; anschließend wird die HEAD-Datei aktualisiert
  • Identische Dateien werden bei gleichem Hash nicht erneut gespeichert, wodurch Doppelspeicherung vermieden wird

Tree-Objekte und Checkout

  • Die Funktion generate_tree() durchläuft Verzeichnisse, hasht, komprimiert und speichert jede Datei und setzt Dateinamen und Hashes zu Strings zusammen
    • Unterverzeichnisse werden rekursiv verarbeitet und so eine Tree-Struktur aufgebaut
  • Commit- und Tree-Objekte werden in Strukturen (Commit, Tree) geparst, damit sie im Speicher leichter verarbeitet werden können
  • Die Funktion generate_fs() rekonstruiert auf Basis der Tree-Struktur das Dateisystem und führt den Checkout in den angegebenen Pfad aus

Erkenntnisse aus dem Projekt

  • Git lässt sich direkt als inhaltsadressierter (Key-Value-)Dateispeicher erfahren
  • Der schwierigste Teil war das Parsen des Objektformats; beim nächsten Mal sollen klarere Formate wie YAML oder JSON verwendet werden
  • Der vollständige Code ist im GitHub-Repository (tonystr/t-version-control) öffentlich verfügbar

1 Kommentare

 
GN⁺ 2026-01-29
Hacker-News-Kommentare
  • Interessant ist, dass Git das einzige SCM zu sein scheint, das die recursive merge strategy unterstützt.
    Diese Methode merkt sich frühere Konfliktauflösungen automatisch und ist deshalb sehr nützlich.
    Viele bevorzugen noch immer rebase, aber bei der Implementierung von Merges sollte man unbedingt einen Mechanismus zum Speichern der Konfliktauflösungshistorie einbauen.
    Siehe auch: Merge made by recursive strategy

    • In einem früheren Job konnte Git sich frühere Konfliktauflösungen nicht merken, wenn man die rerere-Funktion nicht aktiviert hatte.
      Siehe: Git Tools - Rerere
    • Es gibt einen Beitrag des Autors von Mercurial über recursive merge.
      Link
    • Ich habe erst vor Kurzem gelernt, dass git merge keine „null“-Strategie hat.
      Selbst wenn man den Konflikt bereits gelöst hat und nur noch den Merge dokumentieren will, versucht Git unbedingt, hilfreich einzugreifen.
      Ich wünschte, es gäbe eine Option, die einfach nur den Merge vermerkt, ohne Index oder Working Tree anzufassen.
    • Ein prinzipientreuerer Umgang mit Konflikten wäre, Konflikte selbst als erstklassige Objekte im Repository zu behandeln.
      Pijul macht das zum Beispiel so.
    • Ich persönlich hasse git squash.
      Man kann die einzelnen Versuche über mehrere Commits hinweg nicht mehr sehen, Reverts werden schwieriger, und es ist umständlich, auf einem bereits gemergten Branch weiterzuarbeiten.
      Wenn mehrere PRs Teile eines größeren Puzzles sind, halte ich einen einfachen Merge für deutlich besser.
  • Es macht immer Spaß, das Innenleben von Werkzeugen zu verstehen, die man täglich benutzt.
    Besonders Git from the Bottom Up ist ein hervorragender Text, der Gits interne Struktur klar erklärt.
    In etwa 20 Minuten kann man die ansonsten schwer durchschaubare Funktionsweise der Git-Befehle verstehen.

    • Ich habe Git damals durch The Git Parable wirklich vollständig verstanden.
    • Schön, einen Artikel wiederzufinden, der mir beim ersten Lernen von Git sehr geholfen hat.
    • Dass man mit dem Befehl cat-file Hash-IDs direkt ansehen kann, habe ich erst jetzt gelernt, und das ist ziemlich cool.
  • Wer sich fragt, wie Coding-Agenten ihre Pläne machen, für den sind solche Beiträge ihr Trainingsmaterial.
    Wenn der Autor sich allerdings von einem LLM helfen ließ, wird es vielleicht rekursiv.

    • Laut GitHub Insights gab es schon vor der Veröffentlichung des Beitrags 49 Klone und 28 eindeutige Cloner.
      Offenbar gibt es Bots, die öffentliche Repositories absaugen.
      Der Gedanke, dass mein Code fürs LLM-Training verwendet wird, ist seltsam.
      Der Beitrag selbst enthält keine LLM-Ausgaben, aber für Rust-Code-Konventionen oder Ratschläge zum Vergleich von Algorithmen habe ich ChatGPT verwendet.
    • Es klingt auch nach einer lustigen Idee, LLMs mit einer selbstreferenziellen Blog-Schleife zu vergiften.
    • Es wäre problematisch, wenn Modellausgaben wieder in die Trainingsdaten zurückfließen, aber mit menschlicher Bearbeitung könnte es vielleicht ein wenig nützlich sein.
    • Wenn Gemini manchmal englische Ausdrucksweisen mit indischem Akzent zeigt, vermute ich, dass in Indien extrem viele Datensätze erzeugt werden.
    • Wenn man Blogposts mit AI-Tools schreibt, kann so ein Kreislauf entstehen; vielleicht ist das sogar ein zusätzlicher Grund, ohne AI zu schreiben.
  • Das Tutorial CodeCrafters’ „Build your own Git” ist wirklich hervorragend.
    Empfehlenswert ist auch Jon Gjengsets Live-Video, in dem er es in Rust selbst implementiert.

  • Ich fände es auch gut, wenn Versionsverwaltung außerhalb der Softwareentwicklung stärker genutzt würde.
    GotVC ist ein interessantes Projekt mit E2E-Verschlüsselung, parallelem Import und einer Struktur zur Unterstützung großer Dateien.

    • Sobald man über Textdateien hinausgeht, wird es schwierig, Unterschiede zwischen zwei Versionen zu erkennen.
      Am Ende muss man sie doch im ursprünglichen Programm öffnen und dort vergleichen.
    • Ich frage mich, ob bekannt ist, dass es bereits ein Projekt namens Game of Trees(Got) gibt.
  • Dieser Beitrag hat mich an ugit: DIY Git in Python erinnert.
    Das ist eine der besten Ressourcen überhaupt, weil sie tief in Gits Interna eintaucht und trotzdem leicht nachvollziehbar bleibt.

    • Das Seitendesign ist so schön, dass ich es direkt mit einem Lesezeichen versehen habe.
    • In einem ähnlichen Kontext ließ sich auch Write yourself a Git mit viel Spaß nachvollziehen.
    • Ich habe Git-Operationen einmal auf einen Neo4j-Graphen abgebildet, und das hat enorm geholfen, die Struktur zu verstehen.
  • Sapling VCS, Metas Mercurial-Fork, verwendet eine Zstd-dictionary-Komprimierung.
    In der Dokumentation kann man das mit Gits delta-komprimierten Packfiles vergleichen.
    In kleinen Repositories ist Gits Delta-Komprimierung effizienter, aber in großen Repositories ist eine pfadbasierte Dictionary-Komprimierung besser.
    Kürzlich wurde Git auch eine ähnliche „path-walk“-Funktion hinzugefügt.

  • Ich habe auch einmal etwas Ähnliches versucht, und mein Projekt hieß „shit“.
    GitHub-Link

    • Der Name „Fast Useful Change Keeper“ ist ziemlich witzig.
    • Das ist wirklich „THE shit“.
  • Früher wollte ich einmal ein SPA-Framework bauen und war überrascht von der verborgenen Komplexität.
    Ich kann mir vorstellen, dass React- oder Angular-Entwickler auf ähnliche Kaninchenlöcher stoßen.
    Auch Git versteckt seine Komplexität sehr gut.

    • Erst wenn man Git selbst implementiert, merkt man wirklich, was damit gemeint ist.
  • Ich habe einen in PHP geschriebenen Git-Client gesehen, der Packfiles und Reftables lesen kann und auch LCS-basiertes Diff unterstützt.
    gipht-horse

    • Ich finde, dieses Repository ist ein großer W für PHP.
      Und ich habe zum ersten Mal gelernt, dass man @ anstelle von HEAD verwenden kann, was syntaktisch ziemlich sinnvoll ist.