Wybierając pomiędzy pojedynczym lub wieloma projektami w repozytorium git?

223

W gitśrodowisku, w którym zmodularyzowaliśmy większość projektów, mamy do czynienia z jednym projektem na repozytorium lub wieloma projektami na problem z projektem repozytorium . Rozważmy projekt modułowy:

myProject/
   +-- gui
   +-- core
   +-- api
   +-- implA
   +-- implB

Dzisiaj mamy jeden projekt na repozytorium . Daje swobodę

  • release Poszczególne komponenty
  • tag Poszczególne komponenty

Ale jest to również kłopotliwe dla branchkomponentów, ponieważ często rozgałęzienie apiwymaga równoważnych gałęzi corei być może innych komponentów.

Biorąc pod uwagę, że chcemy releaseposzczególne komponenty, możemy nadal uzyskać podobną elastyczność, wykorzystując wiele projektów na projekt repozytorium .

Jakie są doświadczenia i jak / dlaczego rozwiązałeś te problemy?

Johan Sjöberg
źródło
1
Mam teraz bardzo podobny problem. Muszę wydać różne wersje projektu, więc będą musiały znajdować się w różnych repozytoriach. To jednak koszmar do opanowania. Byłoby wspaniale, gdyby istniał sposób na rozgałęzienie tylko podkatalogów.
Andrew T Finnell,
1
Każdy moduł musi mieć osobne numery wersji. I używamy git-describe.
linquize
Jestem zaskoczony, że Bit ( bitsrc.io ) i Lerna ( github.com/lerna/lerna ) nie zostały wymienione! Możesz dowiedzieć się więcej tutaj: hackernoon.com/…
Yoni,

Odpowiedzi:

199

Istnieją trzy główne wady one project per repository, takie jak opisałeś to powyżej. Są to mniej prawdziwe, jeśli są naprawdę odrębnymi projektami, ale od dźwięków tego zmiany do jednego często wymagają zmian w innym, co może naprawdę wyolbrzymić te problemy:

  1. Trudniej jest odkryć, kiedy wprowadzono błędy. Narzędzia takie jak git bisectstają się znacznie trudniejsze w użyciu, gdy rozbijesz swoje repozytorium na sub-repozytoria. Jest to możliwe, po prostu nie jest tak łatwe, co oznacza, że ​​polowanie na błędy w czasach kryzysu jest o wiele trudniejsze.
  2. Śledzenie całej historii funkcji jest znacznie trudniejsze. Polecenia przerzucania historii, takie jak git logpo prostu, nie wyświetlają tak istotnie historii ze złamanymi strukturami repozytorium. Możesz uzyskać użyteczne dane wyjściowe za pomocą podmodułów lub poddrzewa lub za pomocą innych metod skryptowych, ale to nie to samo, co pisanie tig --grep=<caseID>lub git log --grep=<caseID>skanowanie wszystkich zatwierdzeń, na których Ci zależy. Twoja historia staje się trudniejsza do zrozumienia, co czyni ją mniej użyteczną, gdy naprawdę jej potrzebujesz.
  3. Nowi programiści poświęcają więcej czasu na naukę struktury Kontroli wersji, zanim będą mogli rozpocząć kodowanie. Każde nowe zadanie wymaga procedur pobierania, ale złamanie repozytorium projektu oznacza, że ​​muszą pobrać strukturę VC oprócz architektury kodu. Z mojego doświadczenia wynika, że ​​jest to szczególnie trudne dla nowych deweloperów, którzy pochodzą z bardziej tradycyjnych, scentralizowanych sklepów korzystających z jednego repozytorium.

Ostatecznie jest to kalkulacja kosztów alternatywnych. W jednym z byłych pracodawców nasza podstawowa aplikacja została podzielona na 35 różnych sub-repozytoriów. Do tego użyliśmy skomplikowanego zestawu skryptów do przeszukiwania historii, upewnienia się, że stan (tj. Gałęzie produkcyjne vs. programistyczne) był taki sam we wszystkich i wdrażaliśmy je pojedynczo lub masowo.

To było po prostu za dużo; przynajmniej za dużo dla nas. Narzuty związane z zarządzaniem sprawiły, że nasze funkcje stały się mniej zwinne, znacznie trudniejsze we wdrożeniach, uczyły nauczanie nowych deweloperów zbyt wiele czasu, a na koniec ledwo pamiętaliśmy, dlaczego przede wszystkim rozbiliśmy repozytorium. Pewnego pięknego wiosennego dnia wydałem 10 USD na popołudnie czasu obliczeniowego klastra w EC2. Przetarłem repo z kilkoma tuzinami git filter-branchpołączeń. Nigdy nie oglądaliśmy się za siebie.

Krzysztof
źródło
7
Poza tym, jako menedżer repozytorium jest kilka przyjemniejszych rzeczy niż kupowanie czasu w systemie, który może zrobić w dwie godziny to, czego twój laptop nie mógłby zrobić w 20, za mniej niż cena lunchu. Czasami naprawdę kocham internet.
Christopher
2
Jak wydałbyś te pojedyncze projekty jako osobne wydania? Czy nigdy nie musisz tego robić? Taki mam problem. Z, jeśli chcesz utworzyć V1 Projektu A i V2 Projektu B.
Andrew T Finnell,
5
Aby przejść między „jednym projektem na repozytorium” a „wieloma repozytoriami”, rozważ git-poddrzewo (dobre wyjaśnienie na stackoverflow.com/a/17864475/15585 )
deterb
1
Napisałem skrypt, aby zautomatyzować to dla typowych przypadków użycia: github.com/Oakleon/git-join-repos
chrishiestand
Co to jest „struktura VC?”
Robert Harvey
60

Christopher bardzo dobrze wyliczył wady modelu jednego projektu na repozytorium. Chciałbym omówić niektóre z powodów, dla których warto rozważyć podejście oparte na wielu repozytoriach. W wielu środowiskach, w których pracowałem, podejście oparte na wielu repozytoriach było rozsądnym rozwiązaniem, ale decyzja o tym, ile repozytoriów mieć i gdzie dokonać cięć, nie zawsze była łatwa.

Na moim obecnym stanowisku migrowałem gigantyczne repozytorium CVS z pojedynczym repozytorium i ponad dziesięcioletnią historią do wielu repozytoriów git. Od tej pierwszej decyzji liczba repozytoriów wzrosła (dzięki działaniom innych zespołów), do tego stopnia, że ​​podejrzewam, że mamy więcej niż byłoby optymalne. Niektórzy nowi zatrudnieni sugerowali połączenie repozytoriów, ale ja się temu sprzeciwiałem. Projekt Wayland ma podobne doświadczenie. W rozmowie, którą ostatnio widziałem, mieli w pewnym momencie ponad 200 repozytoriów git, za co przeprosił szef. Patrząc na ich stronę internetową , widzę, że mają teraz 5 lat, co wydaje się rozsądne. Ważne jest, aby pamiętać, że łączenie i dzielenie repozytoriów jest wykonalnym zadaniem i można eksperymentować (z uzasadnieniem).

Kiedy więc chcesz mieć wiele repozytoriów?

  1. Pojedyncze repozytorium byłoby zbyt duże, aby było wydajne.
  2. Twoje repozytoria są luźno powiązane lub oddzielone.
  3. Deweloper zazwyczaj potrzebuje tylko jednego lub niewielkiego podzbioru twoich repozytoriów.
  4. Zazwyczaj chcesz samodzielnie tworzyć repozytoria i tylko od czasu do czasu je synchronizować.
  5. Chcesz zachęcić do większej modułowości.
  6. Różne zespoły pracują w różnych repozytoriach.

Punkty 2 i 3 są znaczące tylko wtedy, gdy punkt 1 jest ważny. Dzieląc nasze repozytoria, znacznie zmniejszyłem opóźnienia naszych współpracowników z zewnątrz, zmniejszyłem zużycie dysku i poprawiłem ruch w sieci.

4 i 5 są bardziej subtelne. Po podzieleniu repozytoriów powiedzmy klienta i serwera, to bardziej kosztowne jest koordynowanie zmian między kodem klienta i serwera. Może to być pozytywne, ponieważ zachęca do oddzielenia interfejsu między nimi.

Nawet pomimo wad projektów obejmujących wiele repozytoriów, w ten sposób wykonuje się wiele poważnych prac - na myśl przychodzą sposoby i ulepszenia. Nie wydaje mi się, aby doszło do konsensusu w sprawie najlepszych praktyk i konieczna jest pewna ocena. Narzędzia do pracy z wieloma repozytoriami (git-poddrzewo, git-submodule i inne) są wciąż rozwijane i eksperymentowane. Radzę eksperymentować i być pragmatycznym.

Spacemoose
źródło
7
Ta odpowiedź byłaby jeszcze bardziej pomocna w odniesieniu do twierdzenia: „dołączanie i dzielenie repozytoriów jest wykonalnym zadaniem”.
Wildcard
3
Wiele repozytoriów może również działać wbrew modułowości, ponieważ utrudniają zmianę współdzielonego kodu. Zależności między repozytoriami utrudniają integrację, mogą łatwiej łamać kod (nawet jeśli masz dobre narzędzia, aby to sprawdzić), a groźba zerwania kodu out-of repo zniechęca do refaktoryzacji interfejsów, które są jednym z najpotężniejszych narzędzi do robienia rzeczy bardziej modułowy.
Curt J. Sampson,
Tutaj wszystko o MicroServices i projektowaniu DDD. Powinieneś zminimalizować wspólny kod.
Arwin
49

Gdy korzystamy z GitHub, faktycznie mamy wiele projektów w jednym repozytorium, ale upewniamy się, że te projekty / moduły są odpowiednio zmodularyzowane (używamy konwencji -api i -core + Maven + sprawdzanie statyczne i uruchomieniowe, a nawet pewnego dnia możemy uruchomić system OSGi) .

Na czym to oszczędza? Nie musimy wydawać wielu żądań ściągania, jeśli zmieniamy coś małego w wielu projektach. Problemy i Wiki są scentralizowane itp.

Nadal traktujemy każdy moduł / projekt jako właściwy niezależny projekt i budujemy i integrujemy je osobno w naszym serwerze CI itp.

Martijn Verburg
źródło
1
Bardzo interesujące. Podejrzewam, że to popularny model na githubie. Jeśli masz do czynienia z wydaniami poszczególnych komponentów, czy stosujesz coś podobnego, submodulesczy wypuszczasz / tagujesz całe repozytorium?
Johan Sjöberg,
podmoduły, jeśli musimy, ale na razie wykonujemy wersję od rodzica w dół.
Martijn Verburg,
U mojego obecnego pracodawcy stosujemy podobną strategię i pakujemy metadane dotyczące ostatniego zatwierdzenia w projekcie do różnych plików manifestów artefaktów (tj. Wyników git log -1 -- <project_dir>). To naprawdę świetne. Ta odpowiedź zasługuje na więcej pochwał.
Christopher
22

Dla mnie główną różnicą w korzystaniu z jednego lub więcej niż jednego repozytorium są odpowiedzi na następujące pytania:

  • Czy wiele części opracowanych przez ten sam zespół ma ten sam cykl wydawania, ten sam klient? Zatem jest mniej powodów, aby podzielić jedno repozytorium.
  • Czy wiele części jest od siebie wysoce zależnych? Tak więc podział modelu, kontrolera i interfejsu użytkownika (nawet gdy są to różne części) nie jest zbyt rozsądny ze względu na dużą zależność od siebie. Ale jeśli 2 części mają tylko niewielką zależność, która jest implementowana przez stabilny interfejs, który jest zmieniany co kilka lat, więc rozsądnie byłoby podzielić 2 części na 2 repozytoria.

Na przykład mam małą aplikację (tylko klient), która sprawdza „jakość” repozytorium Subversion. Istnieje podstawowa implementacja, którą można uruchomić z wiersza poleceń i działa dobrze z Javą 6. Ale zacząłem implementować interfejs użytkownika, który używa JavaFX jako części Java 8. Więc podzieliłem 2 i utworzyłem drugie repozytorium (z drugim procesem kompilacji), z innym harmonogramem, ...

Lubię powyższe odpowiedzi (głosowałem za nimi), ale myślę, że to nie jest cała prawdziwa historia. Chciałem więc dodać również argumenty za podzieleniem repozytoriów. Tak więc prawdziwa odpowiedź (kiedy podzielić) może być gdzieś pośrodku ...

mliebelt
źródło
0

Z twojego przykładu, repozytoria powinny być skonfigurowane pod względem ich współzależności. Obowiązuje tutaj całe rozumowanie dotyczące projektowania MicroServices i Domain Driven Design: w niektórych przypadkach zduplikowany kod jest akceptowalny, praca z interfejsami, nie naruszaj kompatybilności, chyba że naprawdę musisz itp.

Teraz moim zdaniem interfejs powinien być niezależny od backendu. Repozytorium projektu interfejsu użytkownika zwykle powinno zawierać kod interfejsu użytkownika i kontroler klienta. Kontroler klienta połączy się z kontrolerami usług w sposób abstrakcyjny. Będą korzystać z abstrakcji klienta usługi / interfejsu API, która jest wersjonowana oddzielnie od usługi, dzięki czemu usługa może być aktualizowana bez przerywania klienta (ów) (może być kilku różnych klientów).

Tak więc sama usługa powinna być własnym repozytorium. Moim zdaniem usługa ta stanowi jedynie oparcie dla logiki biznesowej opartej na jednym punkcie prawdy. Logika biznesowa powinna więc zazwyczaj być oddzielna od technologii usługowej, która ją hostuje. Z drugiej strony implementacja repozytorium jest zazwyczaj tak ściśle związana z logiką biznesową, że można ją zintegrować z tym samym repozytorium. Ale nawet tam przebieg może się różnić.

Oczywiście proste projekty, które raczej nie zmienią się znacznie pod względem technologicznym lub obsługują wiele stosów, w których cały interfejs użytkownika może być hostowany z tego samego źródła co zaplecza, a usługi zaplecza są zwykle używane tylko przez tego samego klienta, mogą korzystać z większej liczby ściśle zintegrowane repozytoria.

W takim przypadku prawdopodobnie dobrze byłoby mieć pełny pion w jednym repozytorium i skupić się na upewnieniu się, że domeny funkcjonalne są odpowiednio autonomiczne w swoim własnym repozytorium. Dzięki temu nadal masz większość zalet mniejszych repozytoriów, a poza tym niewielki narzut.

Arwin
źródło