Organisieren von Git-Repositorys mit häufig verschachtelten Untermodulen

50

Ich bin ein großer Fan von Git-Submodulen . Ich möchte in der Lage sein, eine Abhängigkeit zusammen mit ihrer Version zu verfolgen, so dass Sie ein Rollback auf eine frühere Version Ihres Projekts durchführen und die entsprechende Version der Abhängigkeit sicher und sauber erstellen können. Darüber hinaus ist es einfacher, unsere Bibliotheken als Open-Source-Projekte freizugeben, da die Historie für Bibliotheken von der der Anwendungen, die von ihnen abhängen (und die nicht aus Open-Source-Quellen stammen), getrennt ist.

Ich richte den Workflow für mehrere Projekte bei der Arbeit ein und fragte mich, wie es wohl wäre, wenn wir diesen Ansatz ein wenig extrem nehmen würden, anstatt nur ein einziges monolithisches Projekt zu haben. Ich stellte schnell fest, dass es eine potenzielle Dose Würmer gibt, wenn man wirklich Submodule verwendet.

Nimmt man ein Paar von Anwendungen: studiound player, und abhängigen Bibliotheken core, graphund network, wo Abhängigkeiten sind wie folgt:

  • core ist eigenständig
  • graphabhängig von core(Submodul bei ./libs/core)
  • networkabhängig von core( Submodul bei ./libs/core)
  • studioabhängig von graphund network(Untermodule bei ./libs/graphund ./libs/network)
  • playerabhängig von graphund network(Untermodule bei ./libs/graphund ./libs/network)

Angenommen, wir verwenden CMake und jedes dieser Projekte hat Unit-Tests und alle Arbeiten. Jedes Projekt (einschließlich studiound player) muss eigenständig kompiliert werden können, um Codemetriken, Komponententests usw. durchzuführen.

Die Sache ist, eine rekursive git submodule fetch, dann erhalten Sie die folgende Verzeichnisstruktur:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/graph/
studio/libs/graph/libs/         (sub-module depth: 2)
studio/libs/graph/libs/core/
studio/libs/network/
studio/libs/network/libs/       (sub-module depth: 2)
studio/libs/network/libs/core/

Beachten Sie, dass coreim studioProjekt zweimal geklont wird . Abgesehen von dieser Verschwendung von Speicherplatz habe ich ein Build-System-Problem, weil ich corezweimal baue und möglicherweise zwei verschiedene Versionen von bekomme core.

Frage

Wie organisiere ich Submodule, damit ich die versionierte Abhängigkeit und den Standalone-Build erhalte, ohne mehrere Kopien von häufig verschachtelten Submodulen zu erhalten?

Mögliche Lösung

Wenn die Bibliotheksabhängigkeit eher ein Vorschlag ist (dh "bekanntermaßen mit Version X arbeiten" oder "nur Version X wird offiziell unterstützt") und potenziell abhängige Anwendungen oder Bibliotheken für die Erstellung mit einer beliebigen Version verantwortlich sind, dann Ich könnte mir folgendes Szenario vorstellen:

  • Haben Sie das Build-System für graphund networkteilen Sie ihnen mit, wo sie zu finden sind core(z. B. über einen Compiler-Include-Pfad). Definieren Sie zwei Build-Ziele, "Standalone" und "Abhängigkeit", wobei "Standalone" auf "Abhängigkeit" basiert und den Include-Pfad hinzufügt, der auf das lokale coreUntermodul verweist.
  • Fügen Sie eine zusätzliche Abhängigkeit hinzu: studioon core. Dann studiobaut core, setzt den Include-Pfad auf seine eigene Kopie des coreSubmoduls, baut dann graphund networkim "Abhängigkeits" -Modus.

Die resultierende Ordnerstruktur sieht folgendermaßen aus:

studio/
studio/libs/                    (sub-module depth: 1)
studio/libs/core/
studio/libs/graph/
studio/libs/graph/libs/         (empty folder, sub-modules not fetched)
studio/libs/network/
studio/libs/network/libs/       (empty folder, sub-modules not fetched)

Dies erfordert jedoch etwas Magie für das Build-System (ich bin mir ziemlich sicher, dass dies mit CMake möglich ist) und ein wenig manuelle Arbeit seitens der Versionsaktualisierungen (Aktualisierungen grapherfordern möglicherweise auch Aktualisierungen coreund networkeine kompatible Version von coreallen Projekten). .

Irgendwelche Gedanken dazu?

André Caron
quelle
Beachten Sie, dass dieses Problem nicht spezifisch für cmake ist: Es existiert für jedes Build-System, auch für kein System! (dh, wenn das Superprojekt nur die Bibliotheksquellen hinzufügen soll; dies schließt nur Bibliotheken mit Überschriften ein)
MM

Antworten:

5

Ich komme sehr spät zu dieser Party, aber Ihre Frage scheint immer noch keine vollständige Antwort zu haben, und es ist ein ziemlich prominenter Hit von Google.

Ich habe genau das gleiche Problem mit C ++ / CMake / Git / Submodules und ich habe ein ähnliches Problem mit MATLAB / Git / Submodules, das eine zusätzliche Verrücktheit bekommt, weil MATLAB nicht kompiliert wird. Ich bin vor kurzem auf dieses Video gestoßen, das eine "Lösung" vorzuschlagen scheint. Ich mag die Lösung nicht, weil es im Wesentlichen bedeutet, Submodule wegzuwerfen, aber es beseitigt das Problem. Es ist genau so, wie @errordeveloper empfiehlt. Jedes Projekt hat keine Submodule. Um ein Projekt zu erstellen, erstellen Sie ein Superprojekt, um es zu erstellen, und fügen Sie es den Abhängigkeiten gleich.

Ihr Entwicklungsprojekt graphkönnte also so aussehen:

buildgraph/graph
buildgraph/core

und dann könnte dein Projekt für Studio sein:

buildstudio/studio
buildstudio/graph
buildstudio/network
buildstudio/core

Die Superprojekte sind nur ein Haupt- CMakeLists.txtund ein Bündel von Submodulen. Keines der Projekte verfügt jedoch über eigene Submodule.

Die einzigen Kosten, die ich für diesen Ansatz sehe, sind die vielen kleinen "Super-Projekte", die nur dem Aufbau Ihrer echten Projekte gewidmet sind. Und wenn jemand eines Ihrer Projekte in den Griff bekommt, gibt es keine einfache Möglichkeit, die Abhängigkeiten des Superprojekts zu ermitteln. Das könnte es zum Beispiel hässlich machen, auf Github zu sitzen.

chadsgilbert
quelle
1

Ich nehme an, wenn Sie sowohl Module graphals auch networkSubmodule integrieren studio, müssen Sie corein der Geschichte von immer dieselbe Version von zu einem bestimmten Zeitpunkt haben studio. Ich würde das studio/libs/coreSubmodul in simlinken studio/libs/{graph,network}/libs.

Aktualisieren:

Ich habe mehrere Repositorys mit den von Ihnen angegebenen Abhängigkeiten erstellt:

./core      <--- (v2)
./graph
./graph/libs
./graph/libs/core  <--- (v2)
./graph/.gitmodules
./network
./network/libs
./network/libs/core  <--- (v1)
./network/.gitmodules
./studio
./studio/libs
./studio/libs/graph
./studio/libs/graph/libs
./studio/libs/graph/libs/core <--- (v1)
./studio/libs/graph/.gitmodules
./studio/libs/network
./studio/libs/network/libs
./studio/libs/network/libs/core  <--- (v1)
./studio/libs/network/.gitmodules
./studio/studio
./studio/.gitmodules

v1und v2sind zwei verschiedene Versionen von core. graphbehandelt Version 2, während networkeinige Arbeit benötigt und bei Version 1 steckt. In studioden lokalen eingebetteten Versionen von corebeiden wird darauf v1hingewiesen, um ein funktionierendes Programm zu haben. Abgesehen von der Build-Perspektive funktioniert jetzt alles gut mit Submodulen.

Ich kann jetzt das folgende Verzeichnis entfernen:

./studio/libs/network/libs/core

Und ersetze es durch einen symbolischen Link:

./studio/libs/network/libs/core@ -> ../../graph/libs/core/

Ich setze diese Änderung lokal um und verliere die Fähigkeit, zwei separate Versionen von coreinside zu haben studio, aber ich baue nur coreeinmal. Wenn ich zum Upgrade bereit v2bin, kann ich Folgendes tun:

 git submodule update # (--rebase ?)

... in studio / libs / network.

Core-Dump
quelle
Die Idee der symbolischen Verknüpfung kam mir in den Sinn, ist aber keine Lösung. Wenn Sie von graph/libs/coreaußen verlinken , verwenden Sie das Submodul nicht. Wenn Sie eine Verknüpfung studio/libs/corezu einer der Bibliotheken des Submoduls herstellen, welche wählen Sie dann aus, graphoder network? Was passiert außerdem, wenn es drei oder mehr Schichten tief ist? Was ist, wenn corees eine Reihe von Revisionen geben kann? Es ist nicht offensichtlich , dass Sie zu jeder Version verknüpfen möchten , coredass graphund networkverwenden.
André Caron
"welches wählst du?" : corewäre ein Submodul, das aus der Originalbibliothek coreabgerufen und auf eine Version aktualisiert wird, die mit beiden graphund kompatibel ist network(Sie müssen entscheiden, welches gut ist). Die symbolischen Verknüpfungen werden im lokalen graphund im networkSubmodul hinzugefügt (nicht abgerufen).
Coredump
1
Die symbolischen Links schlagen Sie in hinzufügen graphund networkaußerhalb ihres eigenen Repository (zB irgendwo anders im Punkt würde studioProjekt). Woher wissen sie, wann sie ihr eigenes Untermodul verwenden müssen und wann sie den symbolischen Link verwenden müssen? Vielleicht sollten Sie ein Beispiel hinzufügen, um Ihre Denkweise zu demonstrieren.
André Caron
0

Ich würde es auf eine Submodultiefe von nur einem reduzieren und ein Repository haben, das alle Module als Submodule und nichts anderes außer README und den Build-Skripten enthält. Für jedes der Pakete, die seine Abhängigkeiten verknüpfen, gibt es ein separates Erstellungsskript. Andernfalls können Sie für ein Paket ein separates Repo haben.

Fehlerentwickler
quelle
1
Ich bin nicht sicher, ob dies in meinem Beitrag klar war, aber ich habe mehrere Anwendungen, die von denselben Bibliotheken abhängen, und ich möchte die Erstellungsskripten für Bibliotheken nicht anwendungsübergreifend duplizieren.
André Caron
3
Sie sollten Ihre Antwort ausarbeiten, um zu demonstrieren, wie sie die verschiedenen Probleme angeht. Mir ist nicht klar, wie Sie Abhängigkeiten verknüpfen, da sich die abhängigen Bibliotheken je nach Kontext nicht am selben Ort befinden.
André Caron
0

Ich würde keine Submodule verwenden.

Es ist verlockend, so wie es früher bei svn-externals der Fall war. Können Sie jedoch sicher sein, dass sich alle Projekte, die Sie verknüpfen, in einem Jahr noch am selben Ort befinden? Was ist mit in fünf?

Daher kopiere ich einfach alle erforderlichen Abhängigkeiten in mein Projekt. Dies bedeutet, dass ich, solange mein Repo gültig ist, den genauen Status überprüfen kann.

Grundsätzlich habe ich eine Ordnerstruktur wie folgt:

myproject/... [sources etc]
ext/ [third-party dependencies]


e.g. ext/boost, ext/cppunit

Obwohl dies aus Sicht des Festplattenspeichers nicht sehr schön ist, schätze ich die Garantie, dass ich jeden aufgezeichneten Status überprüfen kann, solange das Repo viel höher verfügbar ist.

Darüber hinaus gibt es eine Reihe von Problemen mit Submodulen, wie hier beschrieben

Wilbert
quelle
Ich bin mir sicher, dass sie sich am richtigen Ort befinden, da ich sie alle behalte :-) Seien Sie auch vorsichtig beim Kopieren von Projekten, da Umverteilungsbedingungen vorliegen.
André Caron
OK, das reduziert das Problem. Und Lizenzierung: Ja, Sie müssen vorsichtig sein, aber das ist ein ganz anderes Problem.
Wilbert
0

Genau das gleiche Problem hier konfrontiert. Eine der Lösungen könnte sein , einige Repo zu haben , libsdie halten würde core, network, graphals Submodule und nur CMakeLists , dass jeder der Libs sagen würde , wo ihre Abhängigkeiten zu finden. Jede Anwendung würde jetzt libsals Submodul und nur die notwendigen Bibliotheken verwenden.

Das Testen jeder Bibliothek kann auf zwei Arten erfolgen:

  • Verwenden Sie core_testing, graph_testing und network_testing als separate Anwendungen
  • Stellen Sie getestete Bibliotheken auf Testservern bereit und suchen Sie sie, während Sie Tests mit cmake ausführen
Max
quelle
Stehen damit nicht alle Bibliotheken allen anderen Bibliotheken zur Verfügung?
André Caron
Standardmäßig ja. Aber das könnte in Bibliothekslisten entschieden werden. Wenn Sie graphnichts darüber wissen müssen network- geben Sie keine relevanten Informationen networkan graphsubdir weiter
Max