Ein bestehendes Repository nachträglich umstellen
Ein Repository, das über Jahre gewachsen ist, enthält meist etliche große Binärdateien, die von Anfang an besser in Git LFS gelegen hätten. Mit git lfs migrate import lässt sich das nachträglich korrigieren, allerdings nur, wenn Historie-Umschreibung, Team-Koordination und Force-Push sauber geplant werden.
Inhaltsverzeichnis
- 1. Wann sich eine nachträgliche LFS-Migration lohnt
- 2. Große Dateien in der bestehenden Historie identifizieren
- 3. .gitattributes und Tracking-Regeln definieren
- 4. git lfs migrate import: die Historie umschreiben
- 5. Migration nur des aktuellen Branches vs. --everything
- 6. Force-Push und Team-Koordination
- 7. Verifikation nach der Migration
- 8. Speicher- und Bandbreiten-Kontingente im Blick behalten
- 9. Rollback-Strategie, falls etwas schiefgeht
- 10. Zusammenfassung
- 11. FAQ
1. Wann sich eine nachträgliche LFS-Migration lohnt
Git wurde für Textdateien und deren zeilenweise Versionierung entworfen, nicht für große Binärdateien wie Bilder, Videos, PDF-Dateien oder kompilierte Artefakte. Jede Änderung an einer solchen Datei erzeugt eine vollständige neue Kopie im Objekt-Speicher, weil Git binäre Inhalte nicht sinnvoll delta-komprimieren kann, wodurch das Repository über Jahre spürbar anwächst, selbst wenn die aktuellen Dateien selbst gar nicht mehr so groß sind.
Ein deutliches Signal für Handlungsbedarf ist ein Clone, der spürbar länger dauert, als es der aktuelle Dateibestand rechtfertigen würde, oder ein .git-Verzeichnis, das ein Vielfaches des Arbeitsverzeichnisses ausmacht. In diesem Fall lohnt sich eine Migration, bei der große Binärdateien rückwirkend aus der Git-Objekt-Datenbank entfernt und stattdessen über Git LFS referenziert werden, was die eigentliche Historie schlank hält und nur noch kleine Zeiger-Dateien im regulären Repository belässt.
2. Große Dateien in der bestehenden Historie identifizieren
Bevor eine Migration beginnt, muss klar sein, welche Dateitypen und Pfade tatsächlich für die Größe des Repositories verantwortlich sind. Ein einfacher, aber wirksamer Ansatz durchsucht alle Objekte im Repository nach ihrer komprimierten Größe und listet die größten Einträge zusammen mit dem Pfad, unter dem sie zuletzt bekannt waren.
Diese Analyse zeigt häufig, dass wenige Dateitypen den Großteil der Repository-Größe verursachen, etwa PNG-Screenshots in einem Dokumentationsordner oder kompilierte DLL-Dateien, die versehentlich eingecheckt wurden. Diese Erkenntnis bestimmt direkt, welche Muster später in der .gitattributes Datei für Git LFS registriert werden.
# Größte Objekte in der Historie nach Größse sortiert auflisten
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
awk '/^blob/ {print substr($0,6)}' |
sort -k2 -n -r |
head -n 20
3. .gitattributes und Tracking-Regeln definieren
Git LFS entscheidet anhand der .gitattributes Datei, welche Dateien über den LFS-Mechanismus verwaltet werden. Der Befehl git lfs track ergänzt diese Datei automatisch um passende Einträge, üblicherweise nach Dateiendung, kann aber auch auf konkrete Verzeichnisse eingeschränkt werden, falls nur ein Teil der Dateien eines bestimmten Typs betroffen sein soll.
Wichtig ist, die Regeln vor der eigentlichen Migration final abzustimmen, weil git lfs migrate import genau diese Muster verwendet, um zu entscheiden, welche historischen Dateien umgeschrieben werden. Eine nachträglich ergänzte Regel greift nicht automatisch rückwirkend auf bereits migrierte Commits.
git lfs install
git lfs track "*.psd" "*.zip" "assets/videos/**"
git add .gitattributes
git commit -m "chore: LFS Tracking-Regeln definieren"
4. git lfs migrate import: die Historie umschreiben
Der eigentliche Migrationsschritt erfolgt mit git lfs migrate import, das jeden Commit in der ausgewählten Historie durchläuft, passende Dateien durch LFS-Zeiger ersetzt und die tatsächlichen Inhalte in den LFS-Speicher verschiebt. Da dabei jeder betroffene Commit neu geschrieben wird, ändern sich zwangsläufig alle Commit-Hashes ab dem ersten betroffenen Commit, ganz ähnlich wie bei einem umfassenden interaktiven Rebase.
Der Parameter --include steuert, welche Muster berücksichtigt werden, wenn keine .gitattributes vorab existiert, andernfalls übernimmt der Befehl automatisch die dort definierten Muster. Ein Testlauf in einem frisch geklonten, isolierten Verzeichnis vor der eigentlichen Migration ist unbedingt empfehlenswert, um das Ergebnis zu prüfen, bevor die Änderung auf das gemeinsame Repository angewendet wird.
# Test in isolierter Kopie
git clone --no-local /pfad/zum/original testklon
cd testklon
# Migration auf Basis der bereits definierten .gitattributes
git lfs migrate import --include-ref=refs/heads/main
# Ergebnis pruefen
git lfs ls-files
du -sh .git
5. Migration nur des aktuellen Branches vs. --everything
Standardmäßig bearbeitet git lfs migrate import nur den aktuell ausgecheckten Branch, was für eine erste Validierung sinnvoll ist, aber ein Repository mit vielen aktiven Feature-Branches oder Tags nicht vollständig bereinigt. Die Option --everything bezieht alle Referenzen ein, also alle lokalen Branches, Tags und, sofern gewünscht, auch Remote-Tracking-Branches, und stellt sicher, dass keine alten, unbereinigten Objekte über einen vergessenen Branch weiterhin im Repository verbleiben.
Der Nachteil einer vollständigen Migration ist der höhere Aufwand, weil jeder betroffene Branch und Tag neu geschrieben wird und damit potenziell jeder offene Pull Request auf Basis der alten Historie ungültig wird. In der Praxis empfiehlt es sich, die Migration in einem Wartungsfenster durchzuführen, in dem keine offenen Feature-Branches gegen die betroffene Historie laufen.
git lfs migrate import --everything
6. Force-Push und Team-Koordination
Da die Migration die Historie umschreibt, muss das Ergebnis anschließend mit einem Force-Push auf den Remote-Server übertragen werden, wofür sich git push --force-with-lease gegenüber einem einfachen --force empfiehlt, weil es verhindert, dass zwischenzeitlich von anderen gepushte Commits versehentlich überschrieben werden. Jeder Entwickler mit einem bestehenden lokalen Klon muss danach seinen eigenen Stand verwerfen und neu klonen oder gezielt auf die neue Historie umsteigen.
Eine klare Kommunikation vor der Migration ist entscheidend: Alle offenen Branches sollten vorher gemergt oder als Patch gesichert werden, und ein fester Zeitpunkt sollte kommuniziert werden, ab dem niemand mehr in das alte Repository pusht. Ohne diese Koordination entstehen nach der Migration unweigerlich Konflikte zwischen der alten und der neuen Historie.
git push --force-with-lease origin --all
git push --force-with-lease origin --tags
# Jeder Entwickler nach der Migration
git fetch origin
git reset --hard origin/main
7. Verifikation nach der Migration
Nach der Migration sollte geprüft werden, ob tatsächlich alle erwarteten Dateien über LFS verwaltet werden und ob die Repository-Größe sich wie erwartet reduziert hat. Der Befehl git lfs ls-files listet alle aktuell über LFS getrackten Dateien im Arbeitsverzeichnis, während ein Vergleich der .git-Verzeichnisgröße vor und nach der Migration den quantitativen Effekt belegt.
Ebenso wichtig ist eine stichprobenartige Prüfung, ob historische Commits weiterhin korrekt auschecken, etwa durch einen Checkout eines älteren Tags und eine Kontrolle, ob die dort referenzierten Binärdateien korrekt über LFS aufgelöst werden. Ein fehlerhafter LFS-Zeiger würde sich sonst erst bemerkbar machen, wenn jemand tatsächlich auf den betroffenen Commit zugreift.
8. Speicher- und Bandbreiten-Kontingente im Blick behalten
Git LFS Speicher und Bandbreite sind bei den meisten Hosting-Anbietern kontingentiert und separat von normalem Git-Speicherplatz abgerechnet, was bei einer großflächigen nachträglichen Migration zu einer plötzlichen Kontingent-Überschreitung führen kann, wenn vorher niemand die tatsächliche Datenmenge kalkuliert hat. Vor der Migration lohnt sich deshalb eine Abschätzung der Gesamtgröße aller zu migrierenden Dateien und ein Abgleich mit dem aktuellen Kontingent des Hosting-Plans.
Da jeder Klon und jeder CI-Checkout künftig LFS-Bandbreite verbraucht, sollte auch die CI-Konfiguration überprüft werden, insbesondere ob Jobs, die die betroffenen Binärdateien gar nicht benötigen, mit GIT_LFS_SKIP_SMUDGE=1 konfiguriert werden können, um unnötige Downloads zu vermeiden.
GIT_LFS_SKIP_SMUDGE=1 git clone https://example.com/repo.git
9. Rollback-Strategie, falls etwas schiefgeht
Vor jeder Migration sollte ein vollständiges Backup des Repositories angelegt werden, idealerweise ein einfacher git clone --mirror in ein separates Verzeichnis oder einen zusätzlichen Remote, der unabhängig vom eigentlichen Migrationsvorgang existiert. Sollte sich nach der Migration herausstellen, dass wichtige Referenzen fehlen oder die LFS-Konfiguration fehlerhaft ist, lässt sich der alte Zustand aus diesem Mirror vollständig wiederherstellen.
Ein Rollback nach einem bereits erfolgten Force-Push ist deutlich aufwendiger, weil jeder Entwickler, der bereits die neue Historie gezogen hat, wieder auf den alten Stand zurückgesetzt werden muss. Deshalb lohnt sich die zusätzliche Zeit für einen sorgfältigen Testlauf in einem isolierten Klon vor der eigentlichen Migration fast immer, weil sie das Risiko eines aufwendigen Rollbacks im gemeinsamen Repository deutlich reduziert.
# Vollständiges Backup vor der Migration
git clone --mirror /pfad/zum/original backup-vor-lfs-migration.git
| Schritt | Werkzeug | Historie betroffen | Empfohlener Zeitpunkt |
|---|---|---|---|
| Große Dateien identifizieren | git rev-list + cat-file | Nein | Vor der Planung |
| Tracking-Regeln definieren | .gitattributes + git lfs track | Nein | Vor der Migration |
| Historie umschreiben | git lfs migrate import | Ja, ab erstem Treffer | Wartungsfenster |
| Ergebnis veröffentlichen | git push --force-with-lease | Ja, remote | Direkt nach Test |
| Backup sichern | git clone --mirror | Nein, nur Kopie | Vor jedem Schritt |
Mironsoft
Git-Workflows, Branching-Strategien und CI-Hooks
Chaotische Git-Historie und unklare Branching-Regeln im Team?
Wir richten saubere Git-Workflows ein, klären Branching-Strategien fürs Team und automatisieren Qualitätschecks über Git-Hooks und CI-Pipelines, damit die Historie nachvollziehbar bleibt.
Workflow-Audit
Bestehende Branching-Strategie und Merge-Praxis auf Schwachstellen prüfen.
Hook-Automatisierung
Pre-Commit- und Pre-Push-Hooks für Linting, Tests und Commit-Konventionen einrichten.
Team-Schulung
Rebase, Cherry-Pick und Konfliktauflösung im Team praxisnah vermitteln.
10. Zusammenfassung
LFS-Migration
Kernbefehl
git lfs migrate import ersetzt Dateien durch LFS-Zeiger
Reichweite
--everything erfasst alle Branches, Tags und Referenzen
Veröffentlichung
git push --force-with-lease nach erfolgreichem Testlauf
Absicherung
git clone --mirror als vollständiges Backup vor Beginn