Utrzymanie dwóch oddzielnych wersji oprogramowania z tej samej bazy kodów w Kontroli wersji

45

Powiedzmy, że piszę dwie różne wersje tego samego oprogramowania / programu / aplikacji / skryptu i przechowuję je pod kontrolą wersji. Pierwsza wersja jest bezpłatną wersją „podstawową”, podczas gdy druga jest płatną wersją „premium”, która pobiera bazę kodu wersji bezpłatnej i rozwija ją o kilka dodatkowych funkcji o wartości dodanej. Wszelkie nowe łatki, poprawki lub funkcje muszą znaleźć drogę do obu wersji.

Obecnie rozważam użycie masteri developgałęzi dla głównej bazy kodu (wersja bezpłatna) obok master-premiumi develop-premiumgałęzi dla wersji płatnej. Gdy wprowadzona zostanie zmiana w wersji darmowej i połączona z mastergałęzią (po dokładnych testach developoczywiście), zostanie ona skopiowana do develop-premiumgałęzi za pomocą cherry-pickpolecenia do dalszych testów, a następnie scalona master-premium.

Czy to najlepszy przepływ pracy w tej sytuacji? Czy są jakieś potencjalne problemy, zastrzeżenia lub pułapki, o których należy pamiętać? Czy istnieje lepsza strategia rozgałęziania niż ta, którą już wymyśliłem?

Twoja opinia jest mile widziana!

PS Dotyczy skryptu PHP przechowywanego w Git, ale odpowiedzi powinny dotyczyć dowolnego języka lub VCS.

Joseph Leedy
źródło

Odpowiedzi:

83

Zamiast mieć dwie wersje kodu ze wspólną bazą, powinieneś zaprojektować aplikację w taki sposób, aby te funkcje premium były kompatybilne z wtyczkami i sterowane konfiguracją, a nie innymi bazami kodu.

Jeśli boisz się wysłać te funkcje premium (wyłączone przez konfigurację) z wersją podstawową, nadal możesz usunąć ten kod w ostatnim etapie kompilacji / pakowania i mieć tylko dwa profile kompilacji.

Mając ten projekt, możesz również wysyłać 5 różnych smaków i uzyskać dużą elastyczność, być może nawet pozwalając stronom trzecim wnieść swój wkład.

OliverS
źródło
2
Tak, o tym zacząłem myśleć ostatniej nocy, zanim poszedłem spać. Dzięki!
Joseph Leedy
3
nowoczesny system Windows został zaprojektowany w ten sposób, wszystkie wersje mają ten sam kod i mają odblokowane funkcje w zależności od używanego klucza licencyjnego.
Mooing Duck
39

Gorąco polecam nie przy użyciu oddziałów do tego celu. Ogólnie rzecz biorąc, powinieneś rozważyć gałęzie dla rzeczy, które zostaną (lub mogą być) scalone ponownie później (lub dla gałęzi wydania, w których ostatecznie zatrzymasz rozwój jednej z gałęzi). W twoim przypadku nigdy nie połączysz swoich wersji „podstawowej” i „premium” i oba będą utrzymywane przez czas nieokreślony, więc gałęzie nie będą odpowiednie.

Zamiast tego utrzymuj jedną wspólną wersję kodu źródłowego i używaj kompilacji warunkowej (np. #ifdefW C / C ++, nie jestem pewien, jaki jest odpowiednik PHP), aby uwzględnić lub wykluczyć sekcje kodu, które różnią się między „podstawową” i „premium”.

Wygląda na to, że PHP może nie mieć wbudowanej takiej funkcji kompilacji warunkowej, więc możesz użyć preprocesora C ( cppprawdopodobnie już go masz), aby wstępnie przetworzyć wspólny kod źródłowy, a następnie stworzyć „podstawową” i „premium” wersja bez dyrektyw preprocesora. Oczywiście, jeśli zdecydujesz się to zrobić, powinieneś użyć makelub czegoś podobnego, aby zautomatyzować proces uruchamiania preprocesora.

Greg Hewgill
źródło
To, co mówisz o oddziałach, ma sens! Być może zamiast tego mógłbym utworzyć osobne repozytorium zawierające tylko kod Premium i użyć jakiegoś skryptu wydania lub podmodułu, aby połączyć go z kodem podstawowym? Może to jednak utrudnić TDD ...
Joseph Leedy
14
Utworzenie kolejnego repozytorium jest jeszcze gorsze niż tworzenie oddziałów! Zdecydowanie chcesz wybrać rozwiązanie, które wymaga najmniejszego powielania wersjonowanego kodu.
Greg Hewgill
2
Celem drugiego repozytorium jest przechowywanie tylko dodatkowego kodu, a nie kolejnej kopii całej aplikacji.
Joseph Leedy
1
Ach, rozumiem, to byłoby bardziej jak model „wtyczki”, w którym twój podstawowy kod ma zdolność ładowania i uruchamiania wtyczek (jeśli istnieją). Kod wtyczki jest osobny i zapewnia funkcje premium.
Greg Hewgill
4
@Joseph: użycie dwóch repozytoriów jest odpowiednie tylko wtedy, gdy wersja dwóch baz kodu jest prawie niezależna od siebie. Jeśli tak nie jest, zdecydowanie zaleciłbym zrobienie tego, co napisał Greg i zachowanie wszystkiego w jednym repozytorium. Jedyne, co chciałbym przemyśleć, to użycie „preprocesora C”. Myślę, że mały skrypt napisany w wybranym przez ciebie języku (sam PHP jest w porządku, Perl lub Python nawet lepiej), który tworzy kopię twojego kodu bez (jakoś zaznaczonych) funkcji premium, by załatwić sprawę.
Doc Brown
8

Korzystamy z 2 oddzielnych projektów, podstawowego i premium, które zależą od projektu podstawowego. Nie używaj pamięci podręcznych, są one zwykle używane do funkcji.

Silviu Burcea
źródło
To mi się podoba, ponieważ możesz użyć skryptu kompilacji do zautomatyzowania tworzenia programów zarówno podstawowych, jak i premium.
neontapir
1
w ogólnym przypadku potrzebujesz 3 projektów: części wspólnej, często zorganizowanej jako biblioteka, oraz części niestandardowych dla dwóch różnych wersji.
Andriy Tylychko,
3

Podczas gdy większość aktualnych odpowiedzi przemawia za kompilacją warunkową zamiast gałęzi, istnieje jeden scenariusz, w którym korzystanie z gałęzi daje wyraźną korzyść: jeśli (teraz lub później) zdecydujesz się udostępnić kod źródłowy wersji podstawowej, w tym wszystkie historii wersji, ale z wyłączeniem wszystkich funkcji premium, możesz to zrobić z podejściem gałęzi, ale nie z jedną gałęzią i kompilacją warunkową.

Odradzam wybieranie czereśni i zamiast tego łączę wszystkie zmiany z wersji podstawowej w wersję premium. W podstawowej wersji nie powinno być żadnych funkcji ani poprawek błędów, ale brakowało ich w wersji premium. Aby wszystko było jak najbardziej bezbolesne, należy upewnić się, że oddział premium modyfikuje wspólne pliki w jak najmniejszym stopniu. Dlatego gałąź premium powinna w większości zawierać dodatkowe pliki i być może niewielkie modyfikacje instrukcji budowania. W ten sposób zmiany z wersji podstawowej zostaną scalone automatycznie bez powodowania konfliktów.

Odpowiedź Grega sugeruje, że „rozważasz gałęzie dla rzeczy, które zostaną (lub mogą być) scalone ponownie później”. Z podejściem Właśnie ten opisany jest przypadek, chyba że ostateczna oddział dla wszystkich zatwierdzeń będzie master-premiumnie master(co jest w rzeczywistości master-basic).

Podmoduły byłyby oczywiście również opcją. To zależy od procesu kompilacji, ale jeśli możesz przekształcić wersję premium w projekt, który wykorzystuje wersję podstawową jako moduł, byłoby dobrze. Możesz jednak mieć trudniejszy czas, jeśli w pewnym momencie zdecydujesz się na wybranie funkcji z gałęzi premium do gałęzi podstawowej. W przypadku podmodułów taka zmiana byłaby reprezentowana jako dwa odrębne zatwierdzenia, podczas gdy w przypadku gałęzi byłby to pojedynczy zatwierdzenie do wersji podstawowej, a następne scalenie do wersji premium wiedziałoby, że zmiany te są już uwzględnione i nie mają do ponownego połączenia.

MvG
źródło
0

W „sprzęcie” robi się to często, są to systemy sprzedawane w celu kontrolowania bałaganu, przepraszam, nie pamiętam, jak się nazywają.

Po dostarczeniu pralki „średniego zasięgu” jej kod nie zmienia się inaczej niż w przypadku bardzo ważnej poprawki błędu, nawet gdy ten sam kod zostanie zmieniony w pralce „niskiej klasy”, która jest wysyłana kilka miesięcy później.

Klienci nie spodziewają się aktualizacji do pralki, którą już przynieśli, nowy model nie jest również wysyłany co kilka miesięcy.

Większość z nas nie mieszka w tym świecie, więc rób to, co mówi Greg, chyba że piszesz oprogramowanie do pralek.

Łan
źródło