Dauerhafte Git-Repositories auf einem Cloud-Mac warten

Dauerhafte Git-Repositories auf einem Cloud-Mac warten

Wenn ein Cloud-Mac über mehrere Wochen hinweg Builds ausgeführt hat, kann git fetch weiterhin schnell sein, während Branch-Wechsel, Merge-Base-Berechnungen und Objekttraversierungen allmählich langsamer werden. Häufig liegt das nicht am Netzwerk, sondern daran, dass sich in einem dauerhaft genutzten Repository zahlreiche lose Objekte, fragmentierte Pack-Dateien, veraltete Remote-Referenzen und Wartungssperren aus abgebrochenen Vorgängen angesammelt haben. Das Löschen von .git und ein anschließender neuer Klon sind zwar unkompliziert, beseitigen jedoch auch den vorhandenen Objekt-Cache und verschieben das Problem lediglich auf einen späteren Zeitpunkt.

Robuster ist es, die Repository-Wartung als eigenständigen Job zu behandeln: zuerst den Zustand messen, anschließend inkrementelle Aufgaben unter exklusiver Sperre ausführen und zum Schluss die Konnektivität der Objekte überprüfen. Der folgende Ablauf eignet sich sowohl für feste Arbeitsverzeichnisse als auch für Build-Knoten mit mehreren Worktrees.

Zunächst eine vergleichbare Repository-Baseline erfassen

Die Gesamtgröße des Verzeichnisses sollte nicht der erste Anhaltspunkt sein. Anzahl, Organisation und Erreichbarkeit der Git-Objekte geben wesentlich besser Aufschluss über den Zustand des Repositories. Wechseln Sie in das Repository und protokollieren Sie die folgenden Ausgaben:

git count-objects -vH
git rev-parse --git-dir
git rev-parse --git-common-dir
git for-each-ref --format='%(refname)' refs/remotes | wc -l
find "$(git rev-parse --git-common-dir)/objects/pack" -name '*.pack' | wc -l

Ein hoher Wert für count weist auf viele lose Objekte hin. Steigt packs kontinuierlich, erzeugen frühere Jobs fortlaufend kleine Pack-Dateien. Bei der Verwendung von Worktrees muss sich die Wartung auf das gemeinsame Verzeichnis beziehen, das git rev-parse --git-common-dir zurückgibt. Es genügt nicht, nur die .git-Zeigerdatei des aktuellen Worktrees zu prüfen.

Es empfiehlt sich, count, in-pack, packs und size-pack vor und nach jeder Wartung in einer einfachen Textdatei zu protokollieren. Ein geringerer Speicherverbrauch allein ist kein ausreichender Erfolgsnachweis: Eine aktualisierte Commit-Graph-Datei kann den Platzbedarf nahezu unverändert lassen und dennoch historische Traversierungen sowie Merge-Base-Abfragen beschleunigen.

Inkrementelle Aufgaben statt aggressiver Bereinigung wählen

Die Wartungsaufgaben von Git lassen sich getrennt ausführen. Auf dauerhaft betriebenen Build-Knoten sollten in der Regel zunächst Commit-Graph-Aktualisierung, Bereinigung loser Objekte und inkrementelles Repacking aktiviert werden:

git config maintenance.strategy incremental
git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

Der Commit-Graph beschleunigt Abfragen zur historischen Erreichbarkeit. Dank des Multi-Pack-Index muss Git nicht jede Pack-Datei einzeln durchsuchen. Das inkrementelle Repacking führt Objekte schrittweise zusammen und verhindert so, dass eine vollständige Bereinigung auf einmal zu viel Zeit und temporären Speicherplatz beansprucht.

Verwenden Sie git gc --prune=now nicht als regelmäßig geplante Routineaufgabe. Das sofortige Löschen beseitigt die Wiederherstellungsfrist und kann außerdem mit parallelen Prozessen kollidieren, die noch Referenzen auf ältere Objekte halten.

Auch nach dem umfangreichen Löschen von Branches sollte zunächst die standardmäßige Schonfrist beibehalten werden. Eine weitergehende Bereinigung kommt erst infrage, wenn sicher feststeht, dass keine Builds, Checkouts, Rebases, Submodul-Aktualisierungen oder Archivierungsaufgaben ausgeführt werden.

Remote-Referenzen separat verwalten

Remote-Tracking-Branches, die über längere Zeit nicht bereinigt werden, vergrößern den Traversierungsumfang. Zeigen Sie die geplanten Änderungen zunächst in einer Vorschau an und führen Sie die Bereinigung anschließend aus:

git remote prune origin --dry-run
git remote prune origin

Damit werden ausschließlich Remote-Tracking-Referenzen verarbeitet. Der Befehl ersetzt weder die Objektbereinigung noch löscht er lokale Entwicklungs-Branches. Wenn Build-Skripte von Referenzen abhängen, die auf dem Remote bereits gelöscht wurden, müssen zuerst die Skripte korrigiert werden, anstatt veraltete Referenzen dauerhaft aufzubewahren.

Builds und Wartung mit einer atomaren Sperre isolieren

Wartungs- und Build-Aufgaben müssen dieselbe Repository-weite Sperre verwenden. Da mkdir innerhalb desselben Dateisystems atomar arbeitet, eignet sich der Befehl als einfacher Sperrmechanismus:

common_dir="$(git rev-parse --git-common-dir)"
lock_dir="$common_dir/maintenance.lock"

if ! mkdir "$lock_dir" 2>/dev/null; then
  printf '%s\n' "maintenance skipped: repository is busy"
  exit 0
fi

trap 'rmdir "$lock_dir"' EXIT INT TERM

git maintenance run \
  --task=commit-graph \
  --task=loose-objects \
  --task=incremental-repack

Auch der Einstiegspunkt des Build-Prozesses muss dieselbe Sperre prüfen oder selbst halten. Eine einseitige Sperre ausschließlich im Wartungsskript ist wirkungslos. Mehrere Worktrees dürfen nicht jeweils eigene Sperren in ihren Arbeitsverzeichnissen anlegen, da sie gemeinsam auf dieselbe Objektdatenbank zugreifen.

Zur manuellen Fehleranalyse können Prozess-ID und Startzeit im Sperrverzeichnis gespeichert werden. Eine Sperre darf jedoch nicht allein wegen eines alten Zeitstempels gelöscht werden. Prüfen Sie zunächst mit ps, ob der Prozess noch existiert, und kontrollieren Sie anschließend, ob git pack-objects, git index-pack oder git maintenance ausgeführt wird.

Indizes und Objektkonnektivität überprüfen

Ein Wartungsbefehl mit Exit-Code null bestätigt lediglich, dass der Befehl abgeschlossen wurde. Er garantiert nicht, dass das Repository bedenkenlos für weitere Builds verwendet werden kann. Mindestens drei Arten von Prüfungen sind erforderlich:

git commit-graph verify
git multi-pack-index verify
git fsck --connectivity-only

Die Prüfungen können wie folgt in die Pipeline aufgenommen werden:

| Prüfung | Zweck | Maßnahme bei einem Fehler | |---|---|---| | `commit-graph verify` | Ebenen und Referenzen des Commit-Graph überprüfen | Commit-Graph neu schreiben und erneut prüfen | | `multi-pack-index verify` | Verweise des Multi-Pack-Index überprüfen | Index neu erstellen, Pack-Dateien nicht manuell löschen | | `fsck --connectivity-only` | Vollständigkeit der erreichbaren Objekte prüfen | Wiederverwendung des Repositories stoppen und Objekte von einem vertrauenswürdigen Remote ergänzen | | `count-objects -vH` | Struktur vor und nach der Wartung vergleichen | Trend protokollieren, keine willkürlichen Grenzwerte festlegen |

Schlägt eine Prüfung fehl, darf nicht als Erstes eine aggressivere Bereinigung ausgeführt werden. Sichern Sie stattdessen den aktuellen Zustand einschließlich Befehlsausgaben, Git-Version, Pfad des gemeinsamen Objektverzeichnisses und Commit-ID des letzten erfolgreichen Builds. Fehlende Objekte können erneut von einem vertrauenswürdigen Remote abgerufen werden. Ist eine Wiederherstellung weiterhin nicht möglich, isolieren Sie das alte Verzeichnis und erstellen Sie einen neuen Klon, anstatt im ursprünglichen Verzeichnis wiederholt Dateien probeweise zu löschen.

Wartung in den Lebenszyklus des Knotens integrieren

Die Wartungshäufigkeit sollte sich nach dem Änderungsvolumen des Repositories richten, nicht nach einem starren Minutenintervall für alle Projekte. Bei einem Monorepository mit hoher Commit-Frequenz kann die inkrementelle Wartung täglich nach Abschluss des letzten Jobs ausgeführt werden. Bei Projekten mit geringer Aktivität sollte sie anhand der Entwicklung der Anzahl loser Objekte und Pack-Dateien ausgelöst werden. Vor der Ausführung muss zudem ausreichend freier Speicherplatz verfügbar sein, da alte und neue Pack-Dateien während des Repackings vorübergehend gleichzeitig existieren.

Auf einem Cloud-Mac von VMOak sollte das Wartungsskript außerhalb des Repositories in einem schreibgeschützten Betriebsverzeichnis abgelegt werden. Der Scheduler übergibt ihm den Pfad des Ziel-Repositories. Das Skript darf keine gleichnamigen Dateien aus Projekt-Branches einlesen, damit gewöhnliche Codeänderungen die Wartungslogik nicht überschreiben können. Wenn ein Knoten mehrere Repositories hostet, müssen Sperrung und Prüfung für jedes Repository einzeln erfolgen. Ein Fehler in einem Repository darf keine Bereinigung der übrigen Repositories auslösen.

Bei der abschließenden Abnahme zählt nicht nur, ob der Speicherverbrauch gesunken ist. Es muss auch sichergestellt werden, dass sich die alltäglichen Abläufe nicht verschlechtert haben: Führen Sie eine Remote-Aktualisierung, einen Checkout des Ziel-Commits, eine Merge-Base-Abfrage und eine schreibgeschützte Build-Vorbereitung aus und vergleichen Sie anschließend die Protokolle vor und nach der Wartung. So wird die Git-Wartung zu einem nachvollziehbaren Betriebsvorgang auf dem Knoten und nicht zu einem Löschbefehl, der erst bei akutem Speicherplatzmangel ausgeführt wird.

Häufig gestellte Fragen

Darf die Git-Wartung während eines Builds laufen?

Sie sollte nicht parallel zu Checkout, Rebase oder Paketierung laufen. Verwenden Sie eine atomare Sperre pro Repository und warten Sie auf ein freies Arbeitsverzeichnis.

Warum nicht regelmäßig git gc --prune=now ausführen?

Sofortiges Löschen beseitigt die Wiederherstellungsfrist und gefährdet Objekte, die ein paralleler Prozess noch verwendet. Inkrementelles Repacking ist für dauerhafte Runner sicherer.

Was muss nach der Wartung geprüft werden?

Prüfen Sie Commit-Graph, Multi-Pack-Index und Objektverbindungen. Erfassen Sie außerdem Pack-Anzahl, lose Objekte und belegten Speicherplatz.

Exklusiver physischer Knoten

Wählen Sie einen Cloud-Mac für Builds, Automatisierung oder Remote-Creative-Workflows

Vergleichen Sie M4 mit 16 GB und M4 Pro mit 64 GB, prüfen Sie Mietdauer und verfügbare Standorte und geben Sie anschließend Ihre Bestellung auf.

Cloud-Mac-Konfiguration wählen