Das Git-Projekt hat kürzlich Git v2.46.0 veröffentlicht. Werfen wir einen Blick auf die wichtigsten Highlights dieser Version, die Beiträge des Git-Teams von GitLab und der gesamten Git-Community enthält.
Tools zur Migration von Referenz-Backends
In der vorherigen Git 2.45.0-Release wurde das Format „reftables“ als neues Backend zum Speichern von Referenzen eingeführt. Dieses neue Referenzformat behebt einige Schwierigkeiten, die Repositories hatten, wenn die Anzahl der Referenzen stieg. Wenn du mit dem reftables-Backend noch nicht vertraut bist, lies dir unseren letzten Blogbeitrag zur Git-Release durch, in dem das Feature vorgestellt wurde. Auch unsere Anfängerleitfaden ist toll, um mehr darüber zu erfahren, wie reftables funktionieren.
Das reftable-Backend hat ein anderes Festplattenformat als das vorhandene files-Backend. Daher ist bei der Verwendung von reftables in einem bestehenden Repository eine Konvertierung zwischen den verschiedenen Formaten erforderlich. Dazu wurden der neue Befehl git-refs(1) und der Unterbefehl migrate
eingeführt, um Referenz-Backend-Migrationen auszuführen. Hier findest du ein Beispiel dafür, wie dieser Befehl verwendet werden kann.
# Initialisiere ein neues "bare" Repository, damit es keine Reflogs enthält.
$ git init --bare .
$ git commit --allow-empty -m "init"
# Erstelle ein paar Referencen.
$ git branch foo
$ git branch bar
$ tree .git/refs
.git/refs
├── heads
│ ├── bar
│ ├── foo
│ ├── main
└── tags
# Migriere Referenzen zum reftable Format.
$ git refs migrate --ref-format=reftable
# Überprüfe, ob jetzt das reftable Backend benutzt wird.
$ tree .git/reftable
.git/reftable
├── 0x000000000001-0x000000000001-a3451eed.ref
└── tables.list
# Überprüfe die Repository Konfiguration um das neue `refstorage` Format zu sehen.
$ cat config
[core]
repositoryformatversion = 1
filemode = true
bare = true
ignorecase = true
precomposeunicode = true
[extensions]
refstorage = reftable
Sobald ein Repository migriert wurde, wird das Festplattenformat geändert, sodass du dann das reftable-Backend nutzen kannst. Git-Abläufe im Repository funktionieren und interagieren wie gewohnt mit Remotes. Die Migration wirkt sich nur darauf aus, wie Referenzen intern für das Repository gespeichert werden. Wenn du zum file-Referenz-Backend zurückkehren möchtest, führe einfach den gleichen Befehl aus und spezifiziere dabei --ref-format=files
.
Das Migrationstool hat derzeit einige signifikante Einschränkungen. Die Reflogs in einem Repository werden auch über das Referenz-Backend gespeichert und würden ebenfalls eine Migration erfordern. Leider ist das Tool noch nicht in der Lage, Reflogs zwischen den files- und reftables-Backends zu konvertieren. Außerdem hat ein Repository mit Worktrees im Grunde mehrere Ref-Stores, und das Migrationstool kann dieses Szenario derzeit noch nicht bewältigen. Wenn ein Repository also Reflogs oder Worktrees enthält, ist die Migration derzeit nicht verfügbar. Diese Einschränkungen werden voraussichtlich in zukünftigen Versionen behoben.
Da ein “bare” Git-Repository keine Reflogs hat, ist es einfacher zu migrieren. Um ein nicht-”bare” Repository zu migrieren, müssen die Reflogs zuerst gelöscht werden. Es kann also jedes Repository ohne Reflogs oder Worktrees migriert werden. Wenn du diese Einschränkungen im Hinterkopf behältst, kann dieses Tool ein guter Helfer sein, um die Vorteile des reftables-Backends in deinen bestehenden Repositories zu nutzen.
Dieses Projekt wurde von Patrick Steinhardt geleitet.
Transaktionale symref-Updates
Der Befehl git-update-ref(1)
erlaubt es, Referenzen in einem Git-Repository zu schreiben. Es können auch mehrere Referenz-Updates atomar mit Transaktionen ausgeführt werden, indem der Befehl git update-ref --stdin
verwendet wird und die Informationen zum Referenz-Update an stdin übergeben werden. Nachfolgend findest du ein Beispiel dafür, wie dies geschieht.
$ git init .
$ git branch -m main
$ git commit --allow-empty -m "foo" && git commit --allow-empty -m "bar"
# Lese die Objekt ID für die zwei erstellten Commits.
$ git rev-parse main~ main
567aac2b3d1fbf0bd2433f669eb0b82a0348775e
3b13462a9a42e0a3130b9cbc472ab479d3ef0631
# Starte eine Transaktion mit mehreren Updates und committe diese.
$ git update-ref --stdin <<EOF
> start
> create refs/heads/new-ref 3b13462a9a42e0a3130b9cbc472ab479d3ef0631
> update refs/heads/main 567aac2b3d1fbf0bd2433f669eb0b82a0348775e
> commit
> EOF
$ git for-each-ref
567aac2b3d1fbf0bd2433f669eb0b82a0348775e commit refs/heads/main
3b13462a9a42e0a3130b9cbc472ab479d3ef0631 commit refs/heads/my-ref
In diesem Beispiel wird nach dem Commit der Transaktion ein neuer Branch erstellt, der auf den Commit „bar“ zeigt, und der Haupt-Branch wird aktualisiert, um auf den vorherigen Commit „foo“ zu zeigen. Durch das Committen der Transaktion werden die angegebenen Referenz-Updates atomar durchgeführt. Wenn ein einzelnes Referenz-Update fehlschlägt, wird die Transaktion abgebrochen und es werden keine Referenz-Updates durchgeführt.
Hier ist bemerkenswert, dass es keine Anweisungen zur Unterstützung von symref-Updates bei diesen Transaktionen gibt. Wenn ein(e) Benutzer(in) ein symref zusammen mit anderen Referenzen atomar in derselben Transaktion aktualisieren möchte, gibt es dazu kein Tool. Die in dieser Release eingeführten Anweisungen symref-create
, symref-update
, symref-delete
und symref-verify
bieten diese Funktion.
# Erstelle eine symbolische Referenz, die wir updaten können.
$ git symbolic-ref refs/heads/symref refs/heads/main
# Der --no-deref Parameter wird benötigt, damit die symbolische Referenz selbst geupdated wird.
$ git update-ref --stdin --no-deref <<EOF
> start
> symref-create refs/heads/new-symref refs/heads/main
> symref-update refs/heads/symref refs/heads/new-ref
> commit
> EOF
$ git symbolic-ref refs/heads/symref
refs/heads/new-ref
$ git symbolic-ref refs/heads/new-symref
refs/heads/main
Im obigen Beispiel wird eine neue symbolische Referenz erstellt und eine weitere in einer Transaktion aktualisiert. Diese neuen symref-Anweisungen können in Kombination mit den bereits bestehenden Anweisungen verwendet werden, um alle Arten von Referenz-Updates jetzt in einer einzigen Transaktion durchführen zu können. Weitere Informationen zu jeder dieser neuen Anweisungen findest du in der Dokumentation.
Dieses Projekt wurde von Karthik Nayak geleitet.
UX-Verbesserungen für git-config(1)
Mit dem Befehl git-config(1) können Repository-Optionen und globale Optionen angezeigt und konfiguriert werden. Die Modi, die für die Interaktion mit der Konfiguration verwendet werden, können explizit mit Hilfe von Flags ausgewählt oder implizit basierend auf der Anzahl der dem Befehl bereitgestellten Argumente bestimmt werden. Ein Beispiel:
$ git config --list
# Lese den Nutzernamen im expliziten Modus.
$ git config --get user.name
# Lese den Nutzernamen im impliziten Modus.
$ git config user.name
# Schreibe den Nutzernamen im expliziten Modus.
$ git config --set user.name "Sidney Jones"
# Schreibe den Nutzernamen im impliziten Modus.
$ git config user.name "Sidney Jones"
# Man kann auch ein optionales drittes Argument übergeben. Was denkst du, was der Effekt ist?
$ git config <name> [<value> [<value-pattern>]]
Im Allgemeinen entspricht die Benutzeroberfläche von git-config(1) nicht den anderen, moderneren Git-Befehlen, bei denen man normalerweise Unterbefehle verwendet. Ein Beispiel ist git remote list
. In diesem Release werden list
, get
, set
, unset
, rename-section
, remove-section
und edit
als Unterbefehle eingeführt, die mit dem config-Befehl verwendet werden können, während gleichzeitig die alte Syntax erhalten bleibt. Diese Änderung soll das Erlebnis der Benutzer(innen) verbessern, indem der config-Befehl den UI-Gewohnheiten entspricht und sich mehr an andere Befehle in Git angleicht. Zum Beispiel:
$ git config list
$ git config get user.name
$ git config set user.name "Sidney Jones"
Dieses Projekt wurde von Patrick Steinhardt geleitet.
Performance-Regression wurde behoben
Git-Operationen, die Attribute nutzen, lesen die .gitattributes
Dateien im Worktree des Repositorys. Dies ist für reine Git-Repositories problematisch, da diese per Definition keinen Worktree haben. Um dies zu umgehen, gibt es in Git die Konfiguration attr.tree
, mit der ein Quellbaum definiert werden kann, von dem Attribute ausgelesen werden.
In der Git Release 2.43.0 hat Git begonnen, bei “bare” Repositories standardmäßig den Baum von HEAD
als Quelle für Git-Attribute heranzuziehen. Leider führte diese zusätzliche Belastung durch die Suche nach Git-Attributdateien zu schweren Leistungseinbußen. Das liegt daran, dass bei jeder Attributsuche der komplette Quellbaum durchsucht wird, um nach einer zugeordneten .gitattributes
-Datei zu suchen, wenn attr.tree
festgelegt ist. Je größer und tiefer der Quellbaum des Repository ist, desto deutlicher wird die Performance-Regression. Benchmarks im linux.git-Repository zeigten beispielsweise, dass git-pack-objects(1) 1,68-mal länger braucht, um durchzulaufen. Dies kann zu Verzögerungen führen, wenn man beispielsweise git-clone(1) oder git-fetch(1) benutzt.
# attr.tree zeigt auf "HEAD", wie es in Git 2.43.0 Standard ist.
Benchmark 1: git -c attr.tree=HEAD pack-objects --all --stdout </dev/null >/dev/null
Time (mean ± σ): 133.807 s ± 4.866 s [User: 129.034 s, System: 6.671 s]
Range (min … max): 128.447 s … 137.945 s 3 runs
# attr.tree zeigt auf einen leeren Baum, wie es in Versionen vor Git 2.43.0 Standard war.
Benchmark 2: git -c attr.tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904 pack-objects --all --stdout </dev/null >/dev/null
Time (mean ± σ): 79.442 s ± 0.822 s [User: 77.500 s, System: 6.056 s]
Range (min … max): 78.583 s … 80.221 s 3 runs
Einige der wichtigsten Git-Befehle, die betroffen waren, sind clone
, pull
, fetch
und diff
, wenn diese wie erwähnt in Repositories mit großen oder tiefen Bäumen ausgeführt wurden. Um diese Performance-Regression zu beheben, wurde also die attr.tree
-Konfiguration teilweise zurückgesetzt, sodass sie nicht mehr standardmäßig auf HEAD
gesetzt ist. Weitere Informationen findest du in diesem Thread in der Mailingliste.
Unit-Test-Migration
In der Vergangenheit wurde das Testen im Git-Projekt über End-to-End-Tests durchgeführt, die als Shell-Skripte implementiert waren. Vor relativ kurzer Zeit hat das Git-Projekt ein Unit-Test-Framework, das in C geschrieben ist, eingeführt. Dieses neue Test-Framework bietet die Möglichkeit für detailliertere Tests von Implementierungsdetails auf niedrigerer Ebene und stellt dadurch eine Ergänzung der bestehenden End-to-End Tests dar. Es gibt einige bestehende End-to-End-Tests, die besser als Unit-Tests und daher gut für die Portierung geeignet sind.
In diesem Jahr unterstützt GitLab erneut Mitwirkende des Google Summer of Code (GSoC), die im Git-Projekt arbeiten. Dank dieser laufenden GSoC-Projekte und auch der großen Git-Community werden einige bestehende Tests überarbeitet und in das Unit-Test-Framework migriert. Während dieses letzten Release-Zyklus gab es mehrere Beiträge zur Verbesserung von Tests im Git-Projekt. Den Entwicklungsfortschritt dieser GSoC-Projekte kannst du auf den Blogs von Chandra und Ghanshyam verfolgen.
Bundle-URI-Fixes
Wenn ein Client etwas aus einem Remote-Repository abruft, werden normalerweise alle erforderlichen Objekte in einer vom Remote-Server zusammengestellten Packfile gesendet. Um einen Teil dieser Berechnungen einzusparen, können Server vorgefertigte „Bundles“ anbieten, die separat vom Remote-Server gespeichert werden und eine Reihe von Referenzen und Objekten enthalten, die der Client brauchen könnte. Der Client kann diese Pakete zuerst über einen Mechanismus namens bundle-uri abrufen.
Dank Xing Xin wurde ein Problem identifiziert und behoben, bei dem Git trotz des Herunterladens einiger Bundles immer noch alles vom Remote-Server heruntergeladen hat, als gäbe es keine Bundles. Das Problem war, dass Git nicht alle heruntergeladenen Bundles korrekt erkannte, was dazu führte, dass die aufeinanderfolgenden Bundles vom Remote-Server abgerufen werden mussten. Dank diesem Fix können Remote-Server, die den Mechanismus bundle-uri verwenden, diese redundante Arbeit vermeiden und die Leistung verbessern.
Weiterlesen
In diesem Artikel wurden nur einige der Beiträge von GitLab und der breiteren Git-Community für dieses neueste Release vorgestellt. Mehr darüber erfährst du in der offiziellen Release-Ankündigung des Git-Projekts. Sieh dir auch unsere letzten Blogbeiträge zu Git-Releases an, um weitere wichtige Beiträge von GitLab-Teammitgliedern zu entdecken.