Close

Der Umgang mit großen Repositorys in Git

Porträtfoto von Nicola Paolucci
Nicola Paolucci

Developer Advocate


Git ist eine hervorragende Lösung zur Nachverfolgung der Weiterentwicklung deiner Codebasis und zur effizienten Zusammenarbeit unter Kollegen. Aber was passiert, wenn das Repository, das du verfolgen möchtest, wirklich sehr groß ist?

In diesem Beitrag informiere ich dich über einige Techniken, um damit umzugehen.

Zwei Kategorien von großen Repositorys


Im Prinzip gibt es zwei Hauptgründe für aus allen Nähten platzende Repositorys:

  • Der Verlauf wächst auf eine enorme Länge an (das Projekt wird mit der Zeit immer größer und es sammelt sich Ballast an).
  • Sie enthalten riesige Binärressourcen, die verfolgt und Code zugeordnet werden müssen.

Es könnte aber auch beides sein.

Manchmal kommt beim zweiten Problemfall erschwerend hinzu, dass die alten, überholten binären Artefakte immer noch im Repository aufbewahrt werden. Dies lässt sich allerdings auf verhältnismäßig einfache – wenn auch lästige – Weise beheben (siehe unten).

Die Techniken und Problemumgehungen für jedes Szenario sind unterschiedlich, ergänzen sich aber auch manchmal. Also werde ich sie separat durchgehen.

Repositorys mit sehr langem Verlauf klonen


Der Schwellenwert, ab dem ein Repository als massiv groß gilt, ist zwar ziemlich hoch, das Klonen von Repositorys ist aber trotzdem mühsam. Und es lässt sich auch nicht immer vermeiden, dass die Verläufe sehr lang werden. Manche Repositorys müssen aus rechtlichen oder regulatorischen Gründen weiter gepflegt werden.

Einfache Lösung: Ein flacher Klon mit Git

Die erste Lösung für einen schnellen Klon, der dem Entwickler und System Zeit und Speicherplatz spart, besteht darin, nur aktuelle Überarbeitungen zu kopieren. Mit der Git-Option für einen flachen Klon kannst du nur die neuesten n-Commits aus dem Verlauf des Repositorys abrufen.

Wie funktioniert das nun? Mit der Option --depth, z. B.:

git clone --depth [depth] [remote-url]

Stell dir vor, du sammelst in deinem Repository den Projektverlauf von 10 oder mehr Jahren an. Wir haben zum Beispiel Jira (eine 11 Jahre alte Codebasis) zu Git migriert. Die Zeitersparnis für solche Repositorys summiert sich und kann sich deutlich bemerkbar machen.

Der vollständige Klon von Jira ist 677 MB groß, wobei das Arbeitsverzeichnis weitere 320 MB groß ist und aus mehr als 47.000 Commits besteht. Ein flacher Klon des Repositorys dauert 29,5 Sekunden, verglichen mit 4 Minuten und 24 Sekunden für einen vollständigen Klon mit dem gesamten Verlauf. Der Nutzen erhöht sich proportional dazu, wie viele Binärressourcen dein Projekt im Laufe der Zeit angehäuft hat.

Datenbanken
Zugehöriges Material

Verschieben eines vollständigen Git-Repositorys

Bitbucket-Logo
Lösung anzeigen

Git kennenlernen mit Bitbucket Cloud

Tipp: Build-Systeme, die mit deinem Git-Repository verbunden sind, profitieren ebenfalls von flachen Klonen.

Flache Klone wurden in Git recht stiefmütterlich behandelt und einige Vorgänge wurden kaum unterstützt. In den neueren Versionen jedoch (1.9 und höher) hat sich die Lage deutlich gebessert. Jetzt kannst du auch mit flachen Klonen richtige Pulls und Pushes von und zu Repositorys durchführen.

Chirurgische Lösung: Branch filtern mit Git

Wenn sehr große Repositorys viel überflüssigen Binärcode aus versehentlichen Commits oder auch alte Ressourcen beinhalten, die niemand mehr braucht, ist git filter-branch eine gute Lösung. Mit diesem Befehl kannst du nach vordefinierten Mustern den gesamten Projektverlauf durchsuchen und dabei filtern, Anpassungen vornehmen oder Dateien überspringen.

Es ist ein sehr mächtiges Tool, wenn du einmal festgestellt hast, wo dein Repository besonders umfangreich ist. Es gibt Hilfsskripte, mit denen du große Objekte identifizieren und diese Aufgabe somit einfach erledigen kannst.

Die Syntax lautet wie folgt:

git filter-branch --tree-filter 'rm -rf [/path/to/spurious/asset/folder]'

git filter-branch hat allerdings einen kleinen Nachteil: Sobald du _filter-branch_ einsetzt, überschreibst du quasi den gesamten Verlauf deines Projekts und damit alle Commit-IDs. Dann ist jeder Entwickler gezwungen, das aktualisierte Repository erneut zu klonen.

Falls du also vorhast, eine Bereinigung mit git filter-branch durchzuführen, solltest du deinem Team vorher Bescheid geben und während der Bereinigung einen kurzen Freeze einplanen. Anschließend solltest du alle dazu auffordern, das Repository erneut zu klonen.

Tipp: In diesem Beitrag zum Aufteilen deines Git-Repositorys erfährst du mehr über git filter-branch.

Alternative zu flachen Klonen mit Git: Nur einen Branch klonen

Seit dem Release von Git 1.7.10 kannst du den Umfang eines Verlaufs beim Klonen begrenzen, indem du einzelne Branches klonst. Das kann so aussehen:

git clone [remote url] --branch [branch_name] --single-branch [folder]

Dieser spezielle Hack wäre bei langlebigen und voneinander abweichenden Branches nützlich, oder wenn du eine sehr große Anzahl Branches hättest, aber nur mit einigen von ihnen arbeiten müsstest. Wenn du nur über wenige Branches ohne große Unterschiede verfügst, wirst du hiervon wahrscheinlich kaum profitieren.

Umgang mit Repositorys mit riesigen binären Assets


Die zweite Art von großen Repositorys sind solche mit riesigen Binärressourcen. Damit haben es viele verschiedene Arten von Softwareteams (und Nicht-Softwareteams!) zu tun. Gaming-Teams müssen mit riesigen 3D-Modellen jonglieren, Webentwicklungsteams müssen möglicherweise Rohbild-Assets verfolgen, CAD-Teams müssen eventuell den Status von binären Ergebnissen bearbeiten und verfolgen.

Die Verarbeitung von Binärressourcen ist mit Git im Grunde kein Problem, doch manchmal tut sich Git etwas schwer. Standardmäßig komprimiert und speichert Git alle folgenden Vollversionen von Binärressourcen, was bei einer großen Menge solcher Daten nicht gerade ideal ist.

Es gibt ein paar einfache Kniffe, die dir die Arbeit erleichtern, etwa die Speicherbereinigung mit (‘git gc’) oder eine optimierte Nutzung der Delta-Commits für einige Binärtypen in .gitattributes.

Es ist jedoch wichtig, über die Art der Binärressourcen deines Projekts nachzudenken, denn davon hängt der richtige Ansatz ab. Folgende Punkte solltest du beispielsweise beachten:

  • Bei erheblichen Änderungen in Binärdateien, die mehr als nur die Metadaten-Kopfzeile betreffen, ist die Delta-Komprimierung wahrscheinlich nutzlos. Nutze für diese Dateien daher ‘delta off’, um überflüssige Delta-Komprimierungsarbeiten beim Neuverpacken zu vermeiden.
  • Die Dateien im obigen Szenario lassen sich mit zlib wahrscheinlich nur schlecht komprimieren. Du kannst die Komprimierung mit ‘core.compression 0’ oder 'core.loosecompression 0' deaktivieren. Diese globale Einstellung wirkt sich allerdings negativ auf alle nicht binären Dateien aus, die sich eigentlich gut komprimieren ließen. Wenn du die Binärressourcen in einem separaten Repository unterbringst, ist das sinnvoll.
  • Es ist wichtig, daran zu denken, dass 'git gc' die "duplizierten" losen Objekte in eine Single-Pack-Datei umwandelt. Sollten sich die Dateien jedoch nicht auf irgendeine Weise komprimieren lassen, wird sich das wahrscheinlich kaum auf die erzeugte Paketdatei auswirken.
  • Sieh dir an, was mit 'core.bigFileThreshold' möglich ist. Eine Delta-Kodierung würde bei über 512 MB sowieso nicht durchgeführt – ganz unabhängig davon, was du für .gitattributes festgelegt hast. Vielleicht ist diese Feineinstellung nützlich für dich.

Lösung für große Ordnerbäume: sparse-checkout mit Git

Eine kleine Hilfe beim Problem mit Binärressourcen ist die Option sparse checkout von Git (ab Git 1.7.0). So kannst du auch dein Arbeitsverzeichnis sauber halten, indem du genau angibst, welche Ordner befüllt werden sollen. Leider macht sich das nicht bei der Größe des lokalen Gesamt-Repositorys bemerkbar, aber die Methode ist nützlich bei umfassenden Ordnerstrukturen.

Welche Befehle gehören dazu? Hier ist ein Beispiel:

  • Klone des gesamte Repository einmal: ‘git clone’
  • Aktiviere die Funktion: ‘git config core.sparsecheckout true’
  • Hinzufügen von Ordnern, die explizit benötigt werden, und Ignorieren von Asset-Ordnern:
    • echo src/ › .git/info/sparse-checkout
  • Lies den Baum wie angegeben:
    • git read-tree -m -u HEAD

Danach kannst du wieder deine üblichen Git-Befehle verwenden. Dein Arbeitsverzeichnis enthält jedoch nur die oben von dir angegebenen Ordner.

Kontrolllösung beim Aktualisieren großer Dateien: Untermodule

[UPDATE] … oder du gehst einen anderen Weg und nutzt Git LFS


Wenn du regelmäßig mit großen Dateien arbeitest, ist es möglicherweise die beste Lösung, den Support für große Dateien (LFS) zu nutzen, den Atlassian 2015 gemeinsam mit GitHub entwickelt hat. (Ja, du hast richtig gelesen. Wir haben uns mit GitHub zusammengetan, um einen Open-Source-Beitrag zum Git-Projekt zu leisten.)

Git LFS ist eine Erweiterung, die dich dabei unterstützt, Pointer (was sonst!) zu speichern, die auf große Dateien in deinem Repository zeigen, anstatt die Dateien selbst dort ablegen zu müssen. Die eigentlichen Dateien werden auf einem Remote-Server gespeichert. Es überrascht nicht, dass das Klonen deines Repositorys dadurch erheblich schneller abläuft.

Git LFS-Diagramm

Wie auch GitHub unterstützt Bitbucket Git LFS. Du kannst also vielleicht schon auf diese Technologie zugreifen. Besonders hilfreich ist sie für Teams aus Designern, Videofilmern, Musikern und CAD-Benutzern.

Fazit


Ein sehr großer Repository-Verlauf oder riesige Dateien sind längst kein Grund, die tollen Funktionen von Git ungenutzt zu lassen. Für beide Probleme gibt es gangbare Lösungen.

In den anderen Artikeln, die ich oben verlinkt habe, findest du weitere Informationen zu Submodulen, Projektabhängigkeiten und Git LFS. Auffrischungsinhalte zu Befehlen und Workflows findest du auf unserer Git-Microsite mit zahlreichen Tutorials. Viel Spaß beim Programmieren!

Nicola Paolucci

Nicola is an all-round hacker who loves exploring and teaching bleeding edge technologies. He writes and talks about Git, development workflows, code collaboration and more recently about Docker. Prior to his current role as Developer Instigator at Atlassian he led software teams, built crowd sourcing applications for geo-spacial data, worked on huge e-commerce deployments. Little known facts about Nicola: he gesticulates a lot while speaking (being Italian), lives in Amsterdam and rides a Ducati.


Diesen Artikel teilen
Nächstes Thema

Lesenswert

Füge diese Ressourcen deinen Lesezeichen hinzu, um mehr über DevOps-Teams und fortlaufende Updates zu DevOps bei Atlassian zu erfahren.

Mitarbeiter arbeiten mit unzähligen Tools zusammen

Bitbucket-Blog

Abbildung: DevOps

DevOps-Lernpfad

Demo Den: Feature-Demos mit Atlassian-Experten

So funktioniert Bitbucket Cloud mit Atlassian Open DevOps

Melde dich für unseren DevOps-Newsletter an

Thank you for signing up