W Git, jak zrobić przechowywanie wersji dla kilkunastu bibliotek, wszystkie działały równolegle

11

Robimy projekty, ale ponownie wykorzystujemy dużo kodu między projektami i mamy wiele bibliotek, które zawierają nasz wspólny kod. Wdrażając nowe projekty, znajdujemy więcej sposobów na wyróżnienie wspólnego kodu i umieszczenie go w bibliotekach. Biblioteki zależą od siebie, a projekty zależą od bibliotek. Każdy projekt i wszystkie biblioteki używane w tym projekcie muszą używać tej samej wersji wszystkich bibliotek, do których się odnoszą. Jeśli wydamy oprogramowanie, będziemy musieli naprawiać błędy i być może dodawać nowe funkcje na wiele lat, czasem na dziesięciolecia. Mamy około tuzina bibliotek, zmiany często dotyczą więcej niż dwóch, a kilka zespołów pracuje nad kilkoma projektami równolegle, wprowadzając jednocześnie zmiany we wszystkich tych bibliotekach.

Niedawno przeszliśmy na git i skonfigurowaliśmy repozytoria dla każdej biblioteki i każdego projektu. Używamy skrytki jako wspólnego repozytorium, robimy nowe rzeczy na gałęziach funkcji, a następnie wysyłamy żądania ściągania i scalamy je dopiero po przejrzeniu.

Wiele problemów, z którymi mamy do czynienia w projektach, wymaga zmian w kilku bibliotekach i specyficznym kodzie projektu. Często obejmują one zmiany interfejsów bibliotek, z których niektóre są niezgodne. (Jeśli uważasz, że to brzmi podejrzanie: łączymy się ze sprzętem i ukrywamy konkretny sprzęt za ogólnymi interfejsami. Niemal za każdym razem, gdy integrujemy sprzęt innego dostawcy, spotykamy się z przypadkami, których nasze interfejsy nie przewidywały i dlatego musimy je udoskonalić.) przykład, wyobrazić sobie projekt P1z wykorzystaniem bibliotek L1, L2i L3. L1również używa L2i L3, i również L2używa L3. Wykres zależności wygląda następująco:

   <-------L1<--+
P1 <----+  ^    |
   <-+  |  |    |
     |  +--L2   |
     |     ^    |
     |     |    |
     +-----L3---+

Teraz wyobraź sobie, cechą tego projektu wymaga zmiany P1i L3który zmienia interfejs L3. Teraz dodaj projekty P2i P3do miksu, które również odnoszą się do tych bibliotek. Nie możemy sobie pozwolić na przełączenie ich wszystkich na nowy interfejs, przeprowadzenie wszystkich testów i wdrożenie nowego oprogramowania. Więc jaka jest alternatywa?

  1. zaimplementuj nowy interfejs w L3
  2. zgłoś prośbę L3i poczekaj na recenzję
  3. scal zmiany
  4. utwórz nową wersję L3
  5. rozpocznij pracę nad funkcją P1, odwołując się do L3nowej wersji, a następnie zaimplementuj funkcję w P1gałęzi funkcji
  6. zgłoś żądanie ściągnięcia, sprawdź je i połącz

(Właśnie zauważyłem, że zapomniałem przełączyć L1i L2do nowej wersji. A ja nawet nie wiem gdzie trzymać to w, bo to muszą być wykonane równolegle P1...)

Jest to żmudny, podatny na błędy i bardzo długi proces wdrażania tej funkcji, wymaga niezależnych recenzji (co znacznie utrudnia przeglądanie), w ogóle się nie skaluje i prawdopodobnie wyklucza nas z działalności, ponieważ ugrzęznąć w procesie, nigdy nic nie możemy zrobić.

Ale w jaki sposób wykorzystujemy rozgałęzianie i tagowanie, aby stworzyć proces, który pozwala nam wdrażać nowe funkcje w nowych projektach bez nadmiernego obciążenia?

sbi
źródło
1
Zmiana oprzyrządowania nie powinna zbytnio wpływać na procesy, które wprowadziłeś. Jak sobie poradziłeś z tym problemem przed przejściem na git?
Bart van Ingen Schenau,
Czy można po prostu dodać nową metodę do interfejsu bez zrywania istniejącej, gdy zależy od niej zbyt wiele bibliotek? Zwykle nie jest to najlepszy pomysł, ale przynajmniej pozwoli ci „wdrożyć” się w implementację nowej funkcji i możesz właściwie przestać używać starej metody, kiedy tylko jest wolna chwila. Czy te interfejsy są zbyt stanowe, aby działały „interfejsy równoległe”?
Ixrec
1
@Ixrec: Powiedziano mi, że „to nie jest gitowy sposób” robienia rzeczy. Każdy korzysta z indywidualnych repozytoriów do indywidualnych projektów, więc postanowiliśmy, że my również to zrobimy.
sbi
2
Twierdziłbym, że nie są to odrębne projekty, jeśli często trzeba je zmieniać w tandemie. Granice między „projektami” powinny zawsze mieć jakąś długoterminową gwarancję kompatybilności wstecznej imo.
Ixrec
1
@Ixrec: Zintegrowanie większej ilości sprzętu jest podobne do przenoszenia kodu na większą liczbę platform: im więcej to zrobisz, tym mniej musisz zmienić na inny sprzęt / platformę. Na dłuższą metę kod się ustabilizuje. Jednak teraz musimy znaleźć proces, który pozwoli nam pozostać na rynku wystarczająco długo, aby się tam dostać.
sbi

Odpowiedzi:

5

To coś w rodzaju ujawnienia tego, co oczywiste, ale może warto o tym wspomnieć.

Zwykle repozytorium git jest dostosowywane do lib / projektu, ponieważ zazwyczaj są one niezależne. Aktualizujesz swój projekt i nie przejmujesz się resztą. Inne projekty w zależności od tego po prostu aktualizują swoją bibliotekę, kiedy tylko uznają to za stosowne.

Wydaje się jednak, że Twój przypadek jest wysoce zależny od skorelowanych komponentów, więc jedna cecha zwykle wpływa na wiele z nich. A całość musi być zapakowana jako pakiet. Ponieważ implementacja funkcji / zmiany / błędu często wymaga jednoczesnego dostosowania wielu różnych bibliotek / projektów, być może warto umieścić je wszystkie w tym samym repozytorium.

Są to silne zalety / wady.

Zalety:

  • Identyfikowalność: gałąź pokazuje wszystko, co zmieniło się w każdym projekcie / bibliotece związanej z tą funkcją / błędem.
  • Wiązanie: po prostu wybierz tag, a uzyskasz właściwe źródła.

Wady:

  • Scalanie: ... czasami jest to trudne przy jednym projekcie. Ponieważ różne zespoły pracują nad wspólnymi oddziałami, przygotuj się na wpływ.
  • Niebezpieczny czynnik „ups”: jeśli jeden pracownik popełni błąd w repozytorium, popełniając błąd, może to wpłynąć na wszystkie projekty i zespoły.

Od Ciebie zależy, czy cena jest warta korzyści.

EDYTOWAĆ:

Działa to tak:

  • Funkcja X musi zostać zaimplementowana
  • Utwórz oddział feature_x
  • Wszyscy programiści zaangażowali się w pracę nad tą gałęzią i pracowali nad nią równolegle, prawdopodobnie w dedykowanych katalogach związanych z ich projektem / lib
  • Po zakończeniu sprawdź, przetestuj, zapakuj, cokolwiek
  • Scal go z powrotem w mistrzu ... a to może być trudna część, ponieważ w międzyczasie feature_yi feature_zmoże również zostało dodane. Staje się połączeniem „cross-team”. Dlatego jest to poważna wada.

tylko dla przypomnienia: myślę, że w większości przypadków jest to zły pomysł i należy to robić ostrożnie, ponieważ wada scalania jest zwykle wyższa niż ta, którą uzyskujesz dzięki zarządzaniu zależnościami / właściwemu śledzeniu funkcji.

Dagnele
źródło
Dzięki, naprawdę obecnie na to patrzymy. W świetle tego nie rozumiem, w jaki sposób moglibyśmy rozgałęzić się za pomocą git. W SVN rozgałęzienia oznaczają kopiowanie poddrzewa (w repozytorium) do innego miejsca (w repozytorium). Dzięki temu łatwo jest tworzyć oddziały dowolnego poddrzewa w repozytorium, więc jeśli masz wiele projektów, możesz rozgałęzić każdy z nich osobno. Czy jest coś takiego w git, czy zawsze będziemy w stanie rozgałęzić całe repo?
sbi,
@sbi: rozdzielisz całe repozytorium. Nie możesz pracować z poddrzewami w różnych gałęziach, co w pewnym sensie pokonałoby punkt w twoim przypadku. Git niczego nie „skopiuje”, po prostu śledzi zmiany w gałęzi, nad którą pracujesz.
Dagnelies,
Wymaga to więc od osoby utworzenia gałęzi funkcji dla jednej biblioteki, aby również scaliła wszystkie pozostałe podczas scalania lub zmiany bazy. To prawdziwa wada. (BTW, SVN również zawsze robi tylko leniwe kopie.)
sbi
@sbi: patrz edycja
dagnelies
1
Cóż, obecnie większość z nas nie czuje się komfortowo. :-/Co więcej, nawet ci, którzy są (i którzy nalegali na przejście na git), nie wiedzą, jak dostosować nasz proces rozwoju do git. Westchnienie. Obawiam się, że minie kilka trudnych miesięcy, aż sprawy staną się gładsze. W każdym razie dzięki, twoja jest najbardziej / jedyną pomocną odpowiedzią do tej pory.
sbi
4

Rozwiązaniem, którego szukasz, jest narzędzie do zarządzania zależnościami w koordynacji z submodułami git

Narzędzia takie jak:

  • Maven
  • Mrówka
  • Kompozytor

Możesz użyć tych narzędzi do zdefiniowania zależności projektu.

Możesz wymagać, aby submoduł był co najmniej w wersji > 2.xx lub oznaczał zakres wersji, które są kompatybilne = 2.2. * Lub mniej niż konkretna wersja <2.2.3

Ilekroć wypuszczasz nową wersję jednego z pakietów, możesz oznaczyć go numerem wersji, w ten sposób możesz pobrać tę konkretną wersję kodu do wszystkich innych projektów

Patrick
źródło
Ale zarządzanie zależnościami nie jest naszym problemem, zostało to rozwiązane. Obecnie regularnie wprowadzamy zmiany w wielu bibliotekach i musimy zminimalizować koszty związane z tworzeniem nowych wersji przy jednoczesnym utrzymaniu stabilnych wydań projektów. Twoja odpowiedź nie wydaje się stanowić rozwiązania tego problemu.
sbi
@sbi To zarządza kosztami tworzenia nowych wersji i utrzymywania stabilnych wydań projektów. Ponieważ możesz dyktować, że projekt x opiera się na projekcie y w wersji 2.1.1, możesz tworzyć nowe wersje projektu y, które nie miałyby wpływu na projekt x.
Patrick
Znów deklarowanie zależności nie jest naszym problemem. Możemy to już zrobić. Problem polega na tym, jak skutecznie zarządzać zmianami w kilku projektach / bibliotekach. Twoja odpowiedź nie wyjaśnia tego.
sbi
@sbi: więc jaki jest dokładnie twój problem? Dokonujesz zmian, zmieniasz wersję, aktualizujesz zależności tam, gdzie jest to wymagane, i voila. To, co opisałeś w swoim pierwszym poście, to typowe dla maven & co. rzeczy. Każda dystrybucja oparta jest na jasno zdefiniowanych wersjonowanych bibliotekach. Jak to może być bardziej jasne?
Dagnelies
@arnaud: Czasy realizacji takiego procesu dla (obecnie dość powszechnych) zmian przecinających trzy lub więcej warstw zabiłyby nas. Myślałem, że moje pytanie to opisuje.
sbi
0

Submoduły

Powinieneś spróbować zdobyć moduły podrzędne , jak sugerowano w jednym komentarzu.

Kiedy projekt P1odnosi się do trzech submodułów L1, L2i L3, faktycznie przechowuje odniesienie do poszczególnych zatwierdzeń we wszystkich trzech repozytoriów: są to robocze wersje poszczególnych bibliotek dla tego projektu .

Tak więc wiele projektów może współpracować z wieloma podmodułami: P1może odnosić się do starej wersji biblioteki, L1podczas gdy projekt P2korzysta z nowej wersji.

Co się stanie, gdy dostarczysz nową wersję L3?

  • wdrożyć nowy interfejs w L3
  • zatwierdzaj, testuj , wysyłaj żądanie ściągania, przeglądaj, scalaj, ... (nie możesz tego uniknąć)
  • zapewnia L2współpracę z L3, zatwierdzanie, ...
  • zapewnić pracę L1z nowymi L2, ...
  • upewnij się, że P1współpracuje z nowymi wersjami wszystkich bibliotek:
    • wewnątrz P1jest lokalna kopia robocza L1, L2i L3, fetche zmiany jesteś zainteresowany.
    • zatwierdzić zmiany, git add L1 L2 L3aby zatwierdzić nowe odniesienie do modułów
    • żądanie wyciągnięcia P1, test, przegląd, żądanie wyciągnięcia, scalenie ...

Metodologia

Jest to żmudny, podatny na błędy i bardzo długi proces wdrażania tej funkcji, wymaga niezależnych recenzji (co znacznie utrudnia przeglądanie), w ogóle się nie skaluje i prawdopodobnie wyklucza nas z działalności, ponieważ ugrzęznąć w procesie, nigdy nic nie możemy zrobić.

Tak, wymaga niezależnych recenzji, ponieważ zmieniasz:

  • Biblioteka
  • biblioteki od niego zależne
  • projekty zależne od wielu bibliotek

Czy zostałbyś wykluczony z działalności, ponieważ dostarczasz bzdury? (Właściwie może nie). Jeśli tak, musisz wykonać testy i przejrzeć zmiany.

Dzięki odpowiednim narzędziom git (nawet gitk) możesz łatwo zobaczyć, z których wersji bibliotek korzysta każdy projekt, i możesz je aktualizować niezależnie w zależności od potrzeb. Submoduły są idealne do twojej sytuacji i nie spowolnią twojego procesu.

Może znajdziesz sposób na zautomatyzowanie części tego procesu, ale większość powyższych kroków wymaga ludzkiego mózgu. Najskuteczniejszym sposobem na skrócenie czasu jest zapewnienie łatwej ewolucji bibliotek i projektów . Jeśli twoja podstawa kodu potrafi z łatwością sprostać nowym wymaganiom, recenzje kodu będą prostsze i nie zajmą ci dużo czasu.

(Edytuj) kolejną rzeczą, która może ci pomóc, jest grupowanie powiązanych recenzji kodów. Zatwierdzasz wszystkie zmiany i czekasz, aż rozpowszechnisz je we wszystkich bibliotekach i projektach, które ich używają, zanim zasygnalizujesz żądania ściągnięcia (lub zanim się nimi zajmiesz). W końcu robisz większą recenzję dla całego łańcucha zależności. Może to pomoże ci zaoszczędzić czas, jeśli każda lokalna zmiana jest niewielka.

rdzeń rdzeniowy
źródło
Opisujesz, jak rozwiązać problem zależności (który, jak już powiedziałem, rozwiązaliśmy) i negujesz sam problem, który mamy. Dlaczego w ogóle przeszkadzasz? (FWIW, piszemy oprogramowanie, które napędza elektrownie. Czysty, bezpieczny i dokładnie sprawdzony kod jest podstawową funkcją.)
sbi
@sbi Co to są podmoduły, jeśli nie specjalny przypadek rozgałęzienia i tagowania? Ci myśleć Submoduły są o zarządzanie zależnościami, ponieważ one również śledzić zależności. Ale oczywiście, jeśli chcesz, wymyśl na nowo submoduły z tagami, nie mam nic przeciwko. Nie rozumiem twojego problemu: jeśli sprawdzony kod jest podstawową funkcją, musisz przeznaczyć trochę czasu na recenzje. Nie jesteś ugrzęznięty, idziesz tak szybko, jak to możliwe, z nałożonymi na ciebie ograniczeniami.
coredump,
Recenzje są dla nas bardzo ważne. Jest to jeden z powodów, dla których niepokoi nas to, że problem (i jego recenzje) został podzielony na kilka recenzji z powodu kilku zmian w kilku repozytoriach. Co więcej, nie chcemy dać się wciągnąć w administrowanie nami na śmierć w związku z jednym problemem: wolimy spędzać czas na pisaniu i recenzowaniu kodu. Re submoduły: jak dotąd jedyne, co o nich słyszałem, to „nie przejmuj się, to nie jest git”. Cóż, biorąc pod uwagę, że nasze wymagania wydają się tak wyjątkowe, może powinniśmy ponownie rozważyć tę decyzję ...
sbi
0

Rozumiem więc, że dla P1 chcesz zmienić interfejs L3, ale chcesz, aby inne P2 i P3, które zależą od interfejsu L3, zmieniły się od razu. Jest to typowy przypadek kompatybilności wstecznej. Jest ładny artykuł na temat zachowania kompatybilności wstecznej

Istnieje kilka sposobów rozwiązania tego problemu:

  • Za każdym razem musisz tworzyć nowe interfejsy, które mogą rozszerzać stare interfejsy.

LUB

  • Jeśli chcesz wycofać stary interfejs po pewnym czasie, możesz mieć kilka wersji interfejsów, a po przeniesieniu wszystkich zależnych projektów usuniesz starsze interfejsy.
Uday Shankar
źródło
1
Nie, zgodność wsteczna jest zapewniona przez gałęzie wydań i nie jest naszym problemem. Problem polega na tym, że siedzimy na szybko zmieniającej się bazie kodu, która chce teraz rozdzielić się na biblioteki, mimo że interfejsy wciąż znajdują się w fazie, w której często się zmieniają. Wiem, jak zarządzać takimi bestiami w SVN, ale nie wiem, jak to zrobić w git bez utopienia się w administracji.
sbi
0

Jeśli dobrze rozwiązuję problem:

  • masz 4 powiązane ze sobą moduły, P1 i L1 do L3
  • musisz zmienić P1, co ostatecznie wpłynie na L1 na L3
  • Liczy się jako błąd procesu, jeśli musisz zmienić wszystkie 4 razem
  • liczy się to jako błąd procesu, jeśli trzeba je wszystkie zmienić 1 na 1.
  • Liczy się jako błąd procesu, jeśli musisz wcześniej zidentyfikować fragmenty, w których należy wprowadzić zmiany.

Zatem celem jest, abyś mógł wykonać P1 i L1 za jednym razem, a następnie miesiąc później zrobić L2 i L3 za jednym razem.

W świecie Java jest to trywialny i być może domyślny sposób pracy:

  • wszystko idzie w jednym repozytorium bez odpowiedniego rozgałęzienia
  • moduły są kompilowane + połączone razem przez maven na podstawie numerów wersji, a nie faktu, że wszystkie znajdują się w tym samym drzewie katalogów.

Możesz więc mieć kod na dysku lokalnym dla L3, który nie skompilowałby się, gdyby był kompilowany z kopią P1 w innym katalogu na dysku; na szczęście tak nie jest. Java może to zrobić bezpośrednio, ponieważ kompilowanie / łączenie opowieści jest oparte na skompilowanych plikach jar, a nie na kodzie źródłowym.

Nie znam wcześniej powszechnie używanego rozwiązania tego problemu dla świata C / C ++ i wyobrażam sobie, że prawie nie chcesz zmieniać języków. Ale można łatwo zhakować coś razem, tworząc pliki, które zrobiły to samo:

  • zainstalowane biblioteki + nagłówki do znanych katalogów z osadzonymi numerami wersji
  • zmieniono ścieżki kompilatora na moduł do katalogu dla odpowiednich numerów wersji

Możesz nawet użyć obsługi C / C ++ w maven , chociaż większość programistów C patrzyłaby na ciebie dziwnie, gdybyś ...

soru
źródło
„liczy się to jako błąd procesu, jeśli musisz zmienić wszystkie 4 razem” . Właściwie to nie byłoby. W rzeczywistości to właśnie zrobiliśmy za pomocą SVN.
sbi
W takim razie myślę, że nie ma problemu z umieszczeniem wszystkich projektów w repozytorium.
soru
Obecnie oceniamy umieszczenie bibliotek w tylko dwóch repozytoriach. To wciąż więcej niż jeden, ale znacznie mniej niż „jeden na każdy projekt”, a biblioteki można bardzo dobrze podzielić na dwie grupy. Dzięki za wkład!
sbi
PS: „Wyobrażam sobie, że prawie nie chcesz zmieniać języków”. To jest osadzone rzeczy. :)
sbi
-1

Istnieje proste rozwiązanie: wycinanie gałęzi wydania w całym repozytorium, łączenie wszystkich poprawek do wszystkich aktywnie wysyłanych wersji (jest to łatwe w jasnym przypadku, powinno być możliwe w git).

Wszystkie alternatywy spowodują z czasem okropny bałagan i rozwój projektu.

zzz777
źródło
Czy możesz prosić o opracowanie? Nie jestem pewien, co sugerujesz.
sbi
W jasnym przypadku punkt rozgałęzienia definiujesz jako gałąź podstawowa i znacznik czasu. Masz następującą hierarchię: gałąź podstawowa -> gałąź wydania-rozwoju -> gałąź rozwoju prywatnego. Cały rozwój odbywa się w prywatnych oddziałach, a następnie w hierarchii. Oddziały wydania klienta są zerwane z działem wydania i rozwoju. Nie znam się tak dobrze na git, ale wydaje się, że najbliższą rzeczą jest wyjaśnienie sprawy wśród darmowych systemów kontroli źródła.
zzz777
Uważne przeczytanie mojego pytania powinno pokazać, że mamy problemy z narzutem związanym z propagowaniem zmian między repozytoriami. To nie ma nic wspólnego z twoją odpowiedzią.
sbi
@sbi Przepraszam, że źle zrozumiałem twoje pytanie. Obawiam się, że prędzej czy później staniesz w obliczu strasznego bałaganu.
zzz777