Blog Open Source Was gibt es Neues in Git 2.49.0?
Aktualisiert am: April 8, 2025
13 Minuten Lesezeit

Was gibt es Neues in Git 2.49.0?

Erfahre mehr über die neueste Version von Git, einschließlich verbesserter Leistung dank zlib-ng, einem neuen Algorithmus zum Hashing von Namen und git-backfill(1).

git3-cover

Das Git-Projekt hat kürzlich Git 2.49.0 veröffentlicht. Werfen wir einen Blick auf die Highlights dieser Version, die Beiträge des Git-Teams von GitLab und der gesamten Git-Community enthält.

Das erwartet dich:

git-backfill(1) und die neue Pfad-API

Wenn du ein Git-Repository mit git-clone(1) klonst, kannst du die Option --filter übergeben. Mit dieser Option kannst du einen partiellen Klon erstellen. In einem partiellen Klon sendet der Server nur eine Teilmenge der erreichbaren Objekte gemäß dem angegebenen Objektfilter. Wenn du beispielsweise einen Klon mit --filter=blob:none erstellst, werden keine Blobs (Dateiinhalte) vom Server abgerufen und es wird ein blobless Klon erstellt.

Blobless-Klone haben alle erreichbaren Commits und Verzeichnisse, aber keine Blobs. Wenn du einen Vorgang wie git-checkout(1) durchführst, lädt Git die fehlenden Blobs herunter, um den Vorgang abzuschließen. Bei einigen Operationen wie git-blame(1) kann dies dazu führen, dass Objekte einzeln heruntergeladen werden, wodurch der Befehl deutlich langsamer wird. Diese Leistungseinbuße der Performance tritt auf, weil git-blame(1) den Commit-Verlauf durchsuchen muss, um herauszufinden, welche spezifischen Blobs benötigt werden. Dann muss es jeden fehlenden Blob einzeln beim Server anfragen.

In Git 2.49 wird der neue Unterbefehl git-backfill(1) eingeführt, der verwendet werden kann, um fehlende Blobs in einem partiellen Blobless-Klon herunterzuladen.

Im Hintergrund nutzt der Befehl git-backfill(1) die neue Pfad-API, die sich davon unterscheidet, wie Git normalerweise über Commits iteriert. Anstatt die Commits einzeln durchzugehen und die mit jedem Commit verbundenen Strukturen und Blobs rekursiv zu besuchen, durchläuft die Path-walk API die Daten nach Pfaden. Für jeden Pfad fügt sie eine Liste der assoziierten Strukturobjekte zu einem Verarbeitungsstapel hinzu. Dieser Verarbeitungsstapel wird dann in der Reihenfolge „Depth-First“ verarbeitet. Anstatt also jedes Objekt im Commit 1 zu verarbeiten, bevor sie zu Commit 2 weitergeht, verarbeitet die API alle Versionen von Datei A in allen Commits, bevor sie zu Datei B weitergeht. Dieser Ansatz verbessert die Leistung in Szenarien, in denen die Gruppierung nach Pfad unerlässlich ist, erheblich.

Ich verdeutliche dies in diesem Beispiel, indem ich einen Blobless-Klon von gitlab-org/git erstelle:

$ git clone --filter=blob:none --bare --no-tags [email protected]:gitlab-org/git.git
Cloning into bare repository 'git.git'...
remote: Enumerating objects: 245904, done.
remote: Counting objects: 100% (1736/1736), done.
remote: Compressing objects: 100% (276/276), done.
remote: Total 245904 (delta 1591), reused 1547 (delta 1459), pack-reused 244168 (from 1)
Receiving objects: 100% (245904/245904), 59.35 MiB | 15.96 MiB/s, done.Resolving deltas: 100% (161482/161482), done.

Oben verwenden wir --bare, um sicherzustellen, dass Git keine Blobs herunterladen muss, um den initialen Branch zu überprüfen. Wir können verifizieren, dass dieser Klon keine Blobs enthält:

$ git cat-file --batch-all-objects --batch-check='%(objecttype)' | sort | uniq -c
  83977 commit
 161927 tree

Wenn du die Inhalte einer Datei im Repository anzeigen möchtest, muss Git sie herunterladen:

$ git cat-file -p HEAD:README.md
remote: Enumerating objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 1 (from 1)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.

[![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)

Git - fast, scalable, distributed revision control system
=========================================================

Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.

[snip]

Wie du oben sehen kannst, kommuniziert Git zuerst mit dem Remote-Repository, um den Blob herunterzuladen, bevor er angezeigt werden kann.

Wenn du git-blame(1) für die Datei ausführen möchtest, muss es viel mehr herunterladen:

$ git blame HEAD README.md
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.

[snip]

df7375d772 README.md (Ævar Arnfjörð Bjarmason 2021-11-23 17:29:09 +0100  1) [![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
5f7864663b README.md (Johannes Schindelin 	2019-01-29 06:19:32 -0800  2)
28513c4f56 README.md (Matthieu Moy        	2016-02-25 09:37:29 +0100  3) Git - fast, scalable, distributed revision control system
28513c4f56 README.md (Matthieu Moy        	2016-02-25 09:37:29 +0100  4) =========================================================
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  5)
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  6) Git is a fast, scalable, distributed revision control system with an
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  7) unusually rich command set that provides both high-level operations
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  8) and full access to internals.
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  9)

[snip]

Wir haben die Ausgabe abgeschnitten, aber du siehst, dass Git für jede Revision dieser Datei separat auf den Server zugreift. Das ist wirklich ineffizient. Mit git-backfill(1) können wir Git anweisen, alle Blobs herunterzuladen:

$ git backfill
remote: Enumerating objects: 50711, done.
remote: Counting objects: 100% (15438/15438), done.
remote: Compressing objects: 100% (708/708), done.
remote: Total 50711 (delta 15154), reused 14730 (delta 14730), pack-reused 35273 (from 1)
Receiving objects: 100% (50711/50711), 11.62 MiB | 12.28 MiB/s, done.
Resolving deltas: 100% (49154/49154), done.
remote: Enumerating objects: 50017, done.
remote: Counting objects: 100% (10826/10826), done.
remote: Compressing objects: 100% (634/634), done.
remote: Total 50017 (delta 10580), reused 10192 (delta 10192), pack-reused 39191 (from 1)
Receiving objects: 100% (50017/50017), 12.17 MiB | 12.33 MiB/s, done.
Resolving deltas: 100% (48301/48301), done.
remote: Enumerating objects: 47303, done.
remote: Counting objects: 100% (7311/7311), done.
remote: Compressing objects: 100% (618/618), done.
remote: Total 47303 (delta 7021), reused 6693 (delta 6693), pack-reused 39992 (from 1)
Receiving objects: 100% (47303/47303), 40.84 MiB | 15.26 MiB/s, done.
Resolving deltas: 100% (43788/43788), done.

Dadurch werden alle Blobs wieder aufgefüllt und der Blobless-Klon wird zu einem vollständigen Klon:

$ git cat-file --batch-all-objects --batch-check='%(objecttype)' | sort | uniq -c
 148031 blob
  83977 commit
 161927 tree

Dieses Projekt wurde von Derrick Stolee geleitet und mit e565f37553 zusammengeführt.

Einführung von zlib-ng

Alle Objekte im Ordner .git/ werden von Git mit zlib komprimiert. zlib ist die Referenzimplementierung für das RFC-1950-Format: ZLIB Compressed Data Format. zlib wurde 1995 entwickelt, hat eine lange Geschichte und ist unglaublich portabel, denn es unterstützt sogar viele Systeme, die älter als das Internet sind. Dank der breiten Unterstützung von Architekturen und Compilern ist es jedoch in seinen Fähigkeiten eingeschränkt.

Der Fork zlib-ng wurde erstellt, um auf diese Einschränkungen einzugehen, denn zlib-ng ist für moderne Systeme optimiert. Dieser Fork verzichtet auf die Unterstützung von Legacy-Systemen und bietet stattdessen Patches für Intel-Optimierungen, einige Cloudflare-Optimierungen sowie mehrere kleinere Patches.

Die Bibliothek zlib-ng selbst bietet einen Kompatibilitätslayer für zlib. Der Kompatibilitätslayer macht es möglich, dass zlib-ng ein Drop-in-Ersatz für zlib ist, ist jedoch nicht auf allen Linux-Distributionen verfügbar. In Git 2.49 gibt es folgende Neuerungen:

  • Ein Kompatibilitätslayer wurde zum Git-Projekt hinzugefügt.
  • Build-Optionen wurden sowohl zur Datei Makefile als auch zur Meson-Build-Datei hinzugefügt.

Mit diesen Ergänzungen kann man einfacher von der verbesserten Performance von zlib-ng profitieren.

In lokalen Benchmark konnte die Geschwindigkeit um rund 25 % gesteigert werden, wenn zlib-ng anstelle von zlib verwendet wurde. Wir sind dabei, diese Änderungen auch für GitLab.com auszurollen.

Wenn du von den Vorteilen von zlib-ng profitieren möchtest, überprüfe zuerst, ob Git auf deinem Gerät bereits zlib-ng verwendet, indem du git version --build-options ausführst:

$ git version --build-options
git version 2.47.1
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
libcurl: 8.6.0
OpenSSL: OpenSSL 3.2.2 4 Jun 2024
zlib: 1.3.1.zlib-ng

Wenn die letzte Zeile zlib-ng enthält, verwendet dein Git bereits die schnellere Variante von zlib. Wenn nicht, kannst du folgendermaßen vorgehen:

  • Bitte den/die Betreuer(in) des Git-Pakets, das du verwendest, die Unterstützung für zlib-ng hinzuzufügen; oder
  • Erstelle Git selbst aus der Quelle.

Diese Änderungen wurden von Patrick Steinhardt eingeführt.

Weitere Iteration auf Meson

In unserem Artikel über die Git-Version 2.48 haben wir über die Einführung des Meson-Build-Systems gesprochen. Meson ist ein Tool für die Build-Automatisierung, das vom Git-Projekt genutzt wird und irgendwann Autoconf, CMake und sogar Make ersetzen könnte.

In diesem Release-Zyklus wurde weiter an der Nutzung von Meson gearbeitet und es wurden verschiedene fehlende Funktionen und Fixes zur Stabilisierung eingeführt:

All diese Bemühungen wurden von Patrick Steinhardt durchgeführt.

Einstellung von .git/branches/ und .git/remotes/

Du weißt wahrscheinlich, dass das Verzeichnis .git existiert und was es enthält. Hast du aber schon einmal von den Unterverzeichnissen .git/branches/ und .git/remotes/ gehört? Wie du vielleicht weißt, werden Referenzen auf Branches in .git/refs/heads/ gespeichert. Wozu dienen also .git/branches/ und .git/remotes/?

Bereits 2005 wurde .git/branches/ eingeführt, um Kurznamen für ein Remote zu speichern. Wenige Monate später wurden diese zu .git/remotes/ verschoben. Im Jahr 2006 lernte git-config(1), Remotes zu speichern. Dies wurde zur Standardmethode, um Remotes zu konfigurieren. 2011 wurden die Verzeichnisse .git/branches/ and .git/remotes/ als veraltet dokumentiert und nicht mehr in modernen Repositories verwendet.

Im Jahr 2024 wurde das Dokument BreakingChanges angelegt, um grundlegende Änderungen für die nächste große Git-Version (v3.0) darzulegen. Diese Release ist zwar in nächster Zeit nicht geplant, doch in diesem Dokument werden Änderungen dokumentiert, die wahrscheinlich Teil dieser kommenden Release sind. In 8ccc75c245 wurde die Verwendung der Verzeichnisse .git/branches/ und .git/remotes/ zu diesem Dokument hinzugefügt, wodurch sie offiziell als veraltet gelten und in Version Git 3.0 nicht mehr enthalten sein werden.

Vielen Dank an Patrick Steinhardt, der diese Einstellung formalisiert hat.

Rust-Datenbindungen für libgit

Beim Kompilieren von Git wird die interne Bibliothek libgit.a erstellt. Diese Bibliothek enthält einige Kernfunktionen von Git.

Diese Bibliothek ist (wie der Großteil von Git) zwar in C geschrieben, in Git 2.49 wurden jedoch Datenbindungen hinzugefügt, damit einige dieser Funktionen auch in Rust zur Verfügung stehen. Dazu wurden zwei neue Cargo-Pakete erstellt: libgit-sys und libgit-rs. Diese Pakete befinden sich im Unterverzeichnis contrib/ im Git-Quellbaum.

Es ist üblich, eine Bibliothek in zwei Pakete zu unterteilen, wenn ein Foreign Function Interface verwendet wird. Das Paket libgit-sys bietet die reine Schnittstelle zu C-Funktionen und verknüpft zur nativen Bibliothek libgit.a. Das Paket libgit-rs bietet eine Schnittstelle zu den Funktionen in libgit-sys mit einem für Rust typischen Gefühl.

Bisher ist die Funktionalität in diesen Rust-Paketen sehr begrenzt. Es wird nur eine Schnittstelle zur Interaktion mit git-config(1) geboten.

Diese Initiative wurde von Josh Steadmon geleitet und mit a4af0b6288 zusammengeführt.

Neuer Namenshashing-Algorithmus

Die Git-Objektdatenbank in .git/ speichert die meisten ihrer Daten in Paketierungsdateien. Packfiles wurden verwendet, um Objekte über Kabel zwischen dem Git-Server und dem Client zu übertragen.

Alles über das Format erfährst du unter gitformat-pack(5). Ein wichtiger Aspekt der Paketierungsdateien ist die Delta-Komprimierung. Bei der Delta-Komprimierung wird nicht jedes Objekt so gespeichert, wie es ist, sondern manche Objekte werden als delta einer anderen base gespeichert. Anstatt also den gesamten Inhalt der Objekte zu speichern, werden die Änderungen im Vergleich zu einem anderen Objekt gespeichert.

Ohne auf die Details einzugehen, wie diese Deltas berechnet oder gespeichert werden, kannst du dir vorstellen, dass es wichtig ist, sehr ähnliche Dateien zu gruppieren. In v2.48 und früheren Versionen verglich Git die letzten 16 Zeichen der Pfadnamen, um festzustellen, ob Blobs ähnlich sind. Dieser Algorithmus wird Version 1 genannt.

Ab Git 2.49 ist Version 2 verfügbar. Dies ist eine Iteration von Version 1, jedoch so verändert, dass die Auswirkungen des übergeordneten Verzeichnisses reduziert werden. Du kannst die Version des Namenshashing-Algorithmus, die du verwenden möchtest, mit der Option --name-hash-version von git-repack(1) festlegen.

Derrick Stolee, der dieses Projekt vorangetrieben hat, verglich die resultierende Größe der Paketierungsdateien, nachdem er git repack -adf--name-hash-version=<n> ausgeführt hatte:

Repository Größe Version 1 Größe Version 2
fluentui 440 MB 161 MB
Repository B 6.248 MB 856 MB
Repository C 37.278 MB 6.921 MB
Repository D 131.204 MB 7.463 MB

Weitere Details findest du im Patch-Set, das in aae91a86fb zusammengeführt wurde.

Promisor-Remote-Fähigkeit

Es ist bekannt, dass Git nicht gut mit großen Dateien umgehen kann. Es gibt einige Lösungen für dieses Problem wie Git LFS, die jedoch immer noch Mängel aufweisen. Einige davon sind Folgende:

  • Mit Git LFS muss der/die Benutzer(in) konfigurieren, welche Dateien in den LFS kommen sollen. Der Server hat keine Kontrolle darüber und muss alle Dateien bereitstellen.
  • Wenn eine Datei ins Repository committet wird, gibt es keine Möglichkeit, sie wieder herauszuholen, ohne den Verlauf neu zu schreiben. Das ist vor allem bei großen Dateien ärgerlich, da sie dadurch für immer dort festhängen.
  • Benutzer(innen) können nicht ändern, welche Dateien im Git LFS abgelegt werden sollen.
  • Es ist schwierig, ein Tool wie Git LFS richtig einzurichten, den Umgang damit zu erlernen und es zu nutzen.

Seit einiger Zeit verfügt Git über das Konzept der Promisor Remotes. Diese Funktion kann für große Dateien genutzt werden und wurde in Git 2.49 noch einen Schritt weiterentwickelt.

Die Idee hinter der neuen Promisor-Remote-Fähigkeit ist relativ einfach: Anstatt alle Objekte selbst zu senden, kann ein Git-Server dem Git-Client sagen: „Lade diese Objekte von XYZ herunter.“ XYZ ist der Promisor Remote.

Git 2.49 ermöglicht es dem Server, die Informationen des Promisor Remote an den Client weiterzugeben. Diese Änderung ist eine Erweiterung von gitprotocol-v2. Während der Server und der Client Daten hin und her übertragen, kann der Server Namen und URLs der Promisor Remotes senden, die er kennt.

Derzeit verwendet der Client die Promisor-Remote-Infos, die er vom Server während des Klonens erhält, nicht, sodass weiterhin alle Objekte vom Remote zu dem Klon übermittelt werden, von dem aus der Vorgang initiiert wurde. Wir planen, diese Funktion weiter zu verbessern, sodass die Promisor-Remote-Info vom Server genutzt werden kann und die Funktion benutzerfreundlicher wird.

Dieses Patch-Set wurde von Christian Couder eingereicht und mit 2c6fd30198 zusammengeführt.

Flacher Klon mit --revision

Die neue Option --revision wurde zu git-clone(1) hinzugefügt. Auf diese Weise kannst du einen flachen Klon eines Repository erstellen, der nur den Verlauf der jeweiligen Revision enthält. Die Option ist ähnlich wie --branch, akzeptiert aber einen ref-Namen (wie refs/heads/main, refs/tags/v1.0 und refs/merge-requests/123) oder eine hexadezimale Commit-Objekt-ID. Der Unterschied zu --branch ist, dass kein Tracking-Branch erstellt und HEAD abgetrennt wird. Diese Option ist also nicht geeignet, wenn du wieder zu diesem Branch beitragen möchtest.

Du kannst --revision in Kombination mit --depth verwenden, um einen minimalen Klon zu erstellen. Ein vorgeschlagener Anwendungsfall ist das automatisierte Testen. Wenn du ein CI-System hast, bei dem ein Branch (oder eine beliebige Referenz) ausgecheckt werden muss, um autonome Tests am Quellcode durchzuführen, brauchst du nur einen minimalen Klon.

Diese Änderung wurde von Toon Claes vorangetrieben.

Mehr erfahren

Wir möchten gern von dir hören

Hat dir dieser Blogbeitrag gefallen oder hast du Fragen oder Feedback? Erstelle ein neues Diskussionsthema im GitLab Community-Forum und tausche deine Eindrücke aus. Teile dein Feedback

Bist du bereit?

Sieh dir an, was dein Team mit einer einheitlichen DevSecOps-Plattform erreichen könnte.

Kostenlose Testversion anfordern

Finde heraus, welcher Tarif für dein Team am besten geeignet ist

Erfahre mehr über die Preise

Erfahre mehr darüber, was GitLab für dein Team tun kann

Sprich mit einem Experten/einer Expertin