Organizowanie repozytoriów Git za pomocą wspólnych zagnieżdżonych podmodułów

50

Jestem wielkim fanem podmodułów Git . Lubię być w stanie śledzić zależność wraz z jej wersją, abyś mógł przywrócić poprzednią wersję swojego projektu i mieć odpowiednią wersję zależności, aby zbudować bezpiecznie i czysto. Co więcej, łatwiej jest wydać nasze biblioteki jako projekty open source, ponieważ historia bibliotek jest odrębna od historii aplikacji, które od nich zależą (i które nie będą otwarte).

Konfiguruję przepływ pracy dla wielu projektów w pracy i zastanawiałem się, jak by to było, gdybyśmy przyjęli to podejście trochę ekstremalnie zamiast jednego projektu monolitycznego. Szybko zdałem sobie sprawę, że istnieje potencjalna puszka robaków, które naprawdę wykorzystują podmoduły.

Przypuśćmy parę wniosków: studioa player, i bibliotek zależnych core, graphoraz network, gdzie zależności są w następujący sposób:

  • core jest samodzielny
  • graphzależy od core(podmoduł w ./libs/core)
  • networkzależy od core(podmoduł w ./libs/core)
  • studiozależy od graphi network(podmoduły ./libs/graphi ./libs/network)
  • playerzależy od graphi network(podmoduły ./libs/graphi ./libs/network)

Załóżmy, że korzystamy z CMake i że każdy z tych projektów ma testy jednostkowe i wszystkie prace. Każdy projekt (w tym studioi player) musi mieć możliwość samodzielnej kompilacji w celu wykonywania pomiarów kodu, testowania jednostkowego itp.

Chodzi o to, że rekurencyjne git submodule fetch, a następnie masz następującą strukturę katalogów:

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/

Zauważ, że coreklonowany jest dwukrotnie w studioprojekcie. Oprócz marnowania miejsca na dysku mam problem z kompilacją systemu, ponieważ buduję coredwa razy i potencjalnie otrzymuję dwie różne wersje core.

Pytanie

Jak zorganizować podmoduły, aby uzyskać zależną od wersji zależność i samodzielną kompilację bez uzyskiwania wielu kopii wspólnych zagnieżdżonych podmodułów?

Możliwe rozwiązanie

Jeśli zależność od biblioteki jest w pewnym sensie sugestią (tj. Jest „znana z pracy z wersją X” lub „tylko oficjalnie obsługiwana jest wersja X”), a potencjalnie zależne aplikacje lub biblioteki są odpowiedzialne za tworzenie z dowolną wersją, którą lubią, to Mogę sobie wyobrazić następujący scenariusz:

  • Poproś system kompilacji graphi networkpowiedz, gdzie mają znaleźć core(np. Za pomocą kompilatora zawierają ścieżkę). Zdefiniuj dwa cele kompilacji, „autonomiczny” i „zależność”, gdzie „samodzielny” jest oparty na „zależności” i dodaje ścieżkę dołączania, aby wskazywać na lokalny corepodmoduł.
  • Wprowadź dodatkową zależność: studiowłączone core. Następnie studiobuduje core, ustawia zawierać ścieżkę do własnej kopii corepodmodułem, a następnie buduje graphi networkw trybie „zależność”.

Wynikowa struktura folderów wygląda następująco:

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)

Wymaga to jednak pewnej magii systemu kompilacji (jestem pewien, że można to zrobić za pomocą CMake) i trochę ręcznej pracy przy aktualizacjach wersji (aktualizacja graphmoże również wymagać aktualizacji corei networkuzyskania kompatybilnej wersji corewe wszystkich projektach) .

Masz jakieś przemyślenia na ten temat?

André Caron
źródło
Zauważ, że ten problem nie jest specyficzny dla cmake: istnieje dla każdego systemu kompilacji, w tym żadnego systemu! (tj. gdy zamierzone jest, aby superprojekt po prostu dodał źródła bibliotek; w tym biblioteki zawierające tylko nagłówki)
MM

Odpowiedzi:

5

Jestem bardzo spóźniony na tę imprezę, ale twoje pytanie wciąż nie wydaje się pełnej odpowiedzi i jest to dość znaczący hit Google.

Mam dokładnie taki sam problem z C ++ / CMake / Git / Submodules i mam podobny problem z MATLAB / Git / Submodules, co powoduje dodatkową dziwność, ponieważ MATLAB nie jest skompilowany. Ostatnio natknąłem się na ten film , który wydaje się proponować „rozwiązanie”. Nie podoba mi się to rozwiązanie, ponieważ zasadniczo oznacza wyrzucanie submodułów, ale eliminuje problem. Jest tak, jak zaleca @errordeveloper. Każdy projekt nie ma podmodułów. Aby zbudować projekt, utwórz superprojekt, aby go zbudować, i dołącz go jako element równorzędny do jego zależności.

Twój projekt do opracowania graphmoże wyglądać następująco:

buildgraph/graph
buildgraph/core

a następnie twoim projektem dla studia może być:

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

Super-projekty to tylko podstawa CMakeLists.txti kilka podmodułów. Ale żaden z projektów nie ma samych podmodułów.

Jedyne koszty, jakie widzę w tym podejściu, to rozpowszechnianie błahych „superprojektów”, które są po prostu poświęcone budowaniu twoich prawdziwych projektów. A jeśli ktoś przejmie jeden z twoich projektów, nie można łatwo stwierdzić bez znalezienia super-projektu, jakie są jego zależności. Może to na przykład sprawić, że będzie on bardzo brzydki na Github.

chadsgilbert
źródło
1

Przypuszczam, że kiedy integrujesz oba moduły graphi networkpodmoduły studio, zawsze musisz mieć tę samą wersję corew danym momencie w historii studio. Chciałbym podlinkować studio/libs/corepodmoduł do studio/libs/{graph,network}/libs.

Aktualizacja:

Stworzyłem wiele repozytoriów z podanymi przez ciebie zależnościami:

./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

v1i v2są dwiema różnymi wersjami core. graphobsługuje wersję 2, podczas gdy networkwymaga trochę pracy i utknął w wersji 1. W studio, lokalne wersje coreobu punktów do v1, aby mieć działający program. Teraz, oprócz perspektywy kompilacji, wszystko działa dobrze z submodułami.

Teraz mogę usunąć następujący katalog:

./studio/libs/network/libs/core

I zamień go na symboliczny link:

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

Lokalnie zatwierdzam tę zmianę i tracę możliwość posiadania dwóch oddzielnych wersji corewewnątrz studio, ale buduję tylko coreraz. Kiedy będę gotowy na uaktualnienie v2, mogę:

 git submodule update # (--rebase ?)

... w studio / libs / network.

rdzeń rdzeniowy
źródło
Pomysł na symboliczny link przyszedł mi do głowy, ale to nie jest rozwiązanie. Jeśli łączysz z graph/libs/corezewnątrz, nie używasz submodułu. Jeśli łączysz z studio/libs/corejedną z bibliotek własnych podmodułu, to którą wybierasz, graphczy network? Co ponadto dzieje się, gdy ma trzy lub więcej warstw głębokości? Wreszcie, co jeśli coremoże być szereg zmian. To oczywiste, że nie chcesz się połączyć do obu wersji core, że graphi networkużywasz.
André Caron,
"który wybierasz ?" : corebyłby submodułem pobranym z oryginalnej corebiblioteki, zaktualizowanym do wersji kompatybilnej z obydwoma graphi network(musisz zdecydować, która z nich jest dobra). Dowiązania symboliczne zostałyby dodane w lokalnych graphi networkpodmodułach (nieruchome).
coredump
1
Dowiązania symboliczne, które proponujesz dodać graphi networkktóre wskazywałyby poza własne repozytorium (np. Gdzie indziej w studioprojekcie). Skąd wiedzą, kiedy użyć własnego podmodułu, a kiedy użyć dowiązania symbolicznego? Być może powinieneś dodać przykład, aby zademonstrować swój tok myślenia.
André Caron,
0

Spłaszczyłbym go, aby głębokość podmodułu wynosiła tylko jeden, i mieć repozytorium, które pomieściłoby wszystkie moduły jako podmoduły i nic poza README i skryptami kompilacji. Dla każdego pakietu istniałby osobny skrypt kompilacji łączący jego zależności. W przeciwnym razie możesz mieć osobne repozytorium dla pakietu.

programista błędów
źródło
1
Nie jestem pewien, czy było to jasne w moim poście, ale mam wiele aplikacji zależnych od tych samych bibliotek i nie chcę powielać skryptów kompilacji dla bibliotek w różnych aplikacjach.
André Caron
3
Powinieneś rozwinąć swoją odpowiedź, aby wykazać, w jaki sposób rozwiązuje ona różne problemy. Nie jest dla mnie jasne, w jaki sposób łączysz zależności, biorąc pod uwagę, że w zależności od kontekstu zależne biblioteki nie znajdują się w tej samej lokalizacji.
André Caron
0

Nie użyłbym submodułów.

To kuszące, tak jak kiedyś w przypadku svn-externals. Czy możesz być jednak pewien, że wszystkie projekty, które łączysz, są nadal w tym samym miejscu w ciągu roku? A co za pięć?

Dlatego po prostu kopiuję wszystkie wymagane zależności do mojego projektu. Oznacza to, że dopóki moje repozytorium jest ważne, mogę sprawdzić dokładny stan.

Zasadniczo mam następującą strukturę folderów:

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


e.g. ext/boost, ext/cppunit

Chociaż nie jest to bardzo miłe z punktu widzenia miejsca na dysku, cenię sobie gwarancję, że mogę sprawdzić każdy zarejestrowany stan, o ile repo jest dostępne znacznie wyżej.

Ponadto istnieje wiele problemów z submodułami opisanymi tutaj

Wilbert
źródło
Jestem pewien, że znajdują się we właściwej lokalizacji, ponieważ utrzymuję je wszystkie :-) Również uważaj na kopiowanie projektów ze względu na warunki redystrybucji.
André Caron
OK, to zmniejsza problem. I licencjonowanie: Tak, musisz być ostrożny, ale to zupełnie inny problem.
Wilbert
0

W obliczu dokładnie tego samego problemu. Jednym z rozwiązań mogłoby być jakieś repo libs, które trzymać core, network, graphjak submodules i zaledwie CMakeLists że powie każdy z bibliotekami, gdzie znaleźć jego zależności. Każda aplikacja będzie miała teraz libspodmoduł i będzie używać tylko niezbędnych bibliotek.

Testowanie każdej biblioteki można skonfigurować na 2 sposoby:

  • Mają core_testing, graph_testing, network_testing jako osobne aplikacje
  • Wdróż przetestowane biblioteki lib na serwerach testowych i znajdź je podczas uruchamiania testów przy użyciu cmake
Max
źródło
Czy to nie udostępnia wszystkich bibliotek dla wszystkich innych bibliotek?
André Caron,
Domyślnie tak. Ale można to ustalić w cmakelistach na poziomie libs. Jeśli graphnie musisz wiedzieć o network- nie przekazuj networkrzeczy związanych z graphpodkatalogiem
Max