Git und Projektabhängigkeiten
NICOLA PAOLUCCI
Developer Advocate
Denke über die folgenden Fragen nach:
Wie gehst du in Git
mit Projektabhängigkeiten um?
Unser Projekt besteht aus mehreren voneinander abhängigen Repositorys. Derzeit verwalten wir diese mit svn:externals
. Wie gehen wir am besten vor, wenn wir dafür Git
verwenden möchten?
Wie teilt man ein großes Repository mithilfe von Git
in kleinere Komponenten auf?
Das sind einige unserer am häufigsten gestellten Fragen.
Dieses Thema bereitet vielen Softwareteams bei der Einführung von Git
Kopfzerbrechen. Dieser Artikel soll Licht ins Dunkel bringen.
Projekt-Abhängigkeiten und Build-Infrastruktur greifen offenbar ineinander, was auch intern bei Atlassian eine Diskussion über die "Zukunft von Builds" entfacht hat.
Separate Repositorys statt einem einzigen können so manches erschweren. Trotzdem ist dies aus mindestens zwei wichtigen Gründen ein relativ natürlicher und manchmal auch zwingend notwendiger Schritt während der Weiterentwicklung eines Softwareprojekts: steigende Build-Zeiten und gemeinsame Abhängigkeiten zwischen Projekten.
Ein Überblick in groben Zügen: Richtlinien und suboptimale Lösungen
Zurück zur Frage: Wie kannst du Projektabhängigkeiten mit Git
verfolgen und verwalten?
Am besten gar nicht!
Spaß beiseite. Ich werde diese Frage zuerst mal allgemein beantworten und später ins Detail gehen. Allerdings gibt es für all die Schwierigkeiten mit Projektabhängigkeiten keine Patentlösung, weder in Git
noch auf andere Weise.
Sobald ein Projekt eine gewisse Größe erreicht hat, ist es sinnvoll, es logisch zu unterteilen. Warte aber nicht, bis du in einem Repository schon Millionen von Zeilen an Code verfasst hast. Die folgenden Hinweise sollen dir lediglich helfen, deinen eigenen Ansatz zu finden.
Zugehöriges Material
Git installieren
Lösung anzeigen
Git kennenlernen mit Bitbucket Cloud
Erste Möglichkeit: Verwendung eines geeigneten Build-/Abhängigkeitstools statt Git
Ein Abhängigkeitsmanagementtool ist derzeit die von mir empfohlene Methode zum Umgang mit den wachsenden Problemen und den Build-Zeiten größerer Projekte.
Halte deine Module in individuellen Repositorys separat und verwalte ihre Interdependenzen mit einem Tool, das hierfür konzipiert wurde. Es ist für (nahezu) jeden Technologie-Stack eines auf dem Markt erhältlich. Einige Beispiele:
- Maven (oder Gradle), wenn du Java verwendest
- Npm für Node-Apps
- Bower, Component.io etc., wenn du JavaScript verwendest (neu!)
- Pip und requirements.txt, falls du mit Python arbeitest
- RubyGems und Bundler, falls du mit Ruby arbeitest
- NuGet für .NET
- Ivy (oder eigene CMake-Aktionen) für C++ (neu!)
- CocoaPods für Cocoa-iOS-Apps
- Composer oder Phing für PHP (neu!)
- Die Build-/Abhängigkeits-Infrastruktur von Go ist ein Stück weit in die Programmiersprache integriert (wobei eine umfassendere Lösung entwickelt wird, siehe godep). Für unseren Git-Server (Bitbucket) nutzen wir sowohl Maven als auch Bower. Wenn alles zum Builden bereit ist, zieht sich das ausgewählte Tool die richtigen Versionen über die Abhängigkeiten, sodass der Build für dein Main-Projekt laufen kann. Einige dieser Tools haben Einschränkungen und gehen nicht von optimalen Annahmen aus. Sie haben sich aber bewährt und sind für diesen Zweck gut geeignet.
Die Probleme durch das Aufteilen eines Projekts
Zur Vereinfachung fasst man zu Beginn eines Projekts alles in einem Build zusammen. Mit zunehmender Größe des Projekts wird dein Build jedoch zu langsam und du musst "cachen". Jetzt kommt das Abhängigkeits-Management ins Spiel. Daher eignen sich Untermodule (im Folgenden mehr dazu) z. B. sehr gut für dynamische Programmiersprachen. Die meisten müssen sich irgendwann über Build-Zeiten Gedanken machen, daher denke ich, dass ein Tool zum Management von Abhängigkeiten sinnvoll ist.
Das Aufteilen von Komponenten in separate Repositorys bringt einige ernsthafte Schwierigkeiten mit sich. In unbestimmter Reihenfolge:
- Zum Ändern einer Komponente ist ein Release erforderlich.
- Es ist zeitaufwendig und kann aus den unerfindlichsten Gründen schiefgehen.
- Man kommt sich bei kleinen Änderungen dumm vor.
- Neue Builds müssen für jede Komponente manuell erstellt werden.
- Das Finden von Repositorys wird erschwert.
- Wenn nicht die gesamte Quelle in einem einzigen Repository zur Verfügung steht, wird refaktoriert.
- In einigen Umgebungen (wie in unserer) ist für die Aktualisierung von APIs ein Meilenstein-Release des Produkts und dann des Plug-ins und anschließend wieder des Produkts erforderlich. Ich habe bestimmt einige Punkte vergessen, aber ich denke, du hast jetzt eine gute Vorstellung davon, dass eine perfekte Lösung für das Problem sicherlich anders aussieht.
Zweite Möglichkeit: git submodule
Wenn du kein Tool zum Abhängigkeits-Management nutzen willst oder kannst, bietet Git
dir einen einfachen Weg, mit Untermodulen
zu arbeiten. Untermodule können vor allem bei dynamischen Programmiersprachen praktisch sein. Langsame Builds ersparen sie dir allerdings nicht unbedingt. Ich habe ein paar Anhaltspunkte und Tipps dazu notiert und auch Alternativen entdeckt. Im Internet findet man jedoch größtenteils Argumente, die dagegensprechen.
1-zu1-Entsprechung von svn:external in Git
ACHTUNG! Wenn du auf der Suche nach einer gleichwertigen Lösung für svn:externals
in Git
bist, kannst du submodules
verwenden. Mit submodules
werden nur Release Branches und keine zufälligen Commits nachverfolgt.
Dritte Möglichkeit: Andere Tools für Build- und Stack-übergreifende Abhängigkeiten
Nicht immer hast du das Glück, an einem völlig homogenen Projekt zu arbeiten, das mit einem einzigen Tool fertiggestellt und zusammengesetzt werden kann. Zum Beispiel müssen bei einigen mobilen Projekten Abhängigkeiten von Java und C++ in Einklang gebracht oder anbieterspezifische Tools zur Erstellung von Ressourcen eingesetzt werden. In komplexeren Fällen kannst du Git
auch noch erweitern. Ein tolles Beispiel hierfür ist das Repository von Android.
Andere Build-Tools, die einen näheren Blick wert sind:
Fazit und weiterführende Artikel
Weitere lesenswerte Inhalte zum Thema Build-Infrastruktur (und Maven) von Charles O'Farrell:
- Auf der Suche nach dem ultimativen Build-Tool
- und der nachfolgende Blogpost über Mavens grundlegende Schwächen
- Rekursives Maven als schädlich bezeichnet
Zum Schluss noch ein großartiges Zitat aus dem letzten der oben angegebenen Artikel. Es bezieht sich zwar auf Maven, gilt aber ebenso für andere Build- und Abhängigkeitstools:
"Ein Cache macht nichts, außer Dinge zu beschleunigen. Man könnte einen Cache komplett entfernen und das umgebende System würde genauso funktionieren, nur langsamer. Ein Cache hat auch keine Nebenwirkungen. Egal, was man in der Vergangenheit mit einem Cache gemacht hat, eine bestimmte Abfrage an den Cache wird bei derselben Abfrage in der Zukunft denselben Wert zurückgeben.
Das Erlebnis mit Maven unterscheidet sich stark von dem, was ich beschreibe! Maven-Repositorys werden wie Caches verwendet, haben aber nicht die Eigenschaften von Caches. Wenn man etwas aus einem Maven-Repository abfragst, ist es sehr wichtig, was man in der Vergangenheit getan hat. Es gibt das Letzte zurück, was man hineingelegt hat. Wenn man etwas abfragt, bevor man es eingibt, wird das möglicherweise scheitern."
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.