Git LFS Migration: bestehendes Repository nachträglich umstellen
AI generated
git
HEAD
Git · Git LFS
Git LFS Migration
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.

10 Min. Lesezeit Git Git LFS Repository-Migration

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

11. FAQ: LFS-Migration

1Verändert git lfs migrate import die Commit-Hashes?
Ja, jeder Commit, der eine migrierte Datei enthält, wird neu geschrieben, wodurch sich sein Hash und der aller nachfolgenden Commits ändert. Das ist unvermeidlich, weil die Datei-Inhalte selbst Teil des Commit-Objekts sind und ausgetauscht werden.
2Muss ich Git LFS beim Hosting-Anbieter separat aktivieren?
Bei GitHub, GitLab und Bitbucket ist Git LFS Server-seitig standardmäßig verfügbar, oft aber mit einem begrenzten kostenlosen Kontingent für Speicher und Bandbreite. Ein selbst gehosteter Server benötigt zusätzlich eine LFS-fähige Server-Komponente.
3Kann ich die Migration auf bestimmte Verzeichnisse beschränken?
Ja, über die Option --include lassen sich Glob-Muster angeben, die auf bestimmte Pfade oder Dateiendungen eingeschränkt sind, ergänzt um --exclude für gezielte Ausnahmen innerhalb dieser Muster.
4Was passiert mit bereits offenen Pull Requests während der Migration?
Da sich die Commit-Hashes des Zielbranches ändern, werden offene Pull Requests gegen die alte Historie ungültig oder zeigen massive, unerwartete Diffs. Sie sollten vor der Migration entweder gemergt oder auf die neue Historie rebased werden.
5Wie lange dauert eine Migration bei einem sehr großen Repository?
Das hängt stark von der Anzahl der betroffenen Commits und der Gesamtgröße der zu migrierenden Dateien ab, kann bei Repositories mit mehreren Zehntausend Commits aber durchaus mehrere Stunden dauern. Ein Testlauf gibt vorab eine realistische Zeitschätzung.
6Kann git lfs migrate import auch wieder rückgängig gemacht werden, also von LFS zurück in normale Blobs?
Ja, mit git lfs migrate export existiert der umgekehrte Befehl, der LFS-Zeiger wieder durch die tatsächlichen Dateiinhalte ersetzt. Auch dieser Vorgang schreibt die Historie erneut um und erfordert dieselbe Sorgfalt wie der Import.
7Erkennt git lfs migrate import automatisch alle großen Dateien?
Nein, der Befehl richtet sich ausschließlich nach den in .gitattributes definierten Mustern oder den explizit über --include angegebenen Mustern. Eine vorherige Analyse der tatsächlichen Dateigrößen ist deshalb notwendig, um die richtigen Muster zu definieren.
8Muss ich CI-Pipelines nach der Migration anpassen?
In der Regel ja, zumindest um zu prüfen, ob git-lfs auf den Build-Agenten installiert ist und ob Jobs, die keine Binärdateien benötigen, mit GIT_LFS_SKIP_SMUDGE unnötigen LFS-Traffic vermeiden.
9Was ist der Unterschied zwischen git lfs migrate und BFG Repo-Cleaner?
BFG Repo-Cleaner entfernt Dateien und Historie-Objekte vollständig, ohne sie durch LFS-Zeiger zu ersetzen, während git lfs migrate import die Dateien gezielt durch LFS-Referenzen austauscht und weiterhin abrufbar hält. Für eine echte LFS-Migration ist git lfs migrate das dafür vorgesehene Werkzeug.
10Sollte ich vor der Migration ein Wartungsfenster ankündigen?
Bei einem aktiv genutzten Team-Repository unbedingt, weil jeder Push während der Migration zu Konflikten mit der umgeschriebenen Historie führt. Ein klar kommuniziertes Zeitfenster, in dem niemand pusht, reduziert das Risiko von Nacharbeiten erheblich.