Gitflow, czyli jak korzystać z gita i nie zwariować
Czym jest git chyba nie muszę nikomu tłumaczyć. Stworzył go Linus Torvalds jak narzędzie wspomagające rozwój jądra Linuxa. Tempo prac jakie towarzyszyły powstaniu gita robi wrażenie. Prace nad Gitem rozpoczęły się 3 kwietnia 2005 roku, projekt został ogłoszony 6 kwietnia a 7 kwietnia Git obsługiwał kontrolę wersji swojego kodu. 18 kwietnia pierwszy raz wykonano marge’a a 16 czerwca 2005 jądro Linuxa było już hostowane przez Gita.
Naprawdę szacunek!
Warto zauważyć, że w trakcie prac nad gitem niezwykle szybko zaczęto korzystanie z narzędzia we wczesnej fazie do prac nad samym projektem. Nad wkładem w Linusa w rozwój oprogramowania, można by wiele napisać, ale nie o tym, na być ten wpis.
W tym wpisie chciałbym opisać, jak wykorzystując gita możesz pozbyć się wielu problemów jakie możesz napotkać w trakcie prac nad jakimkolwiek projektem. W jaki sposób oddzielić kod, który jest już na produkcji od kodu, który dopiero jest rozwijane? Jak w prosty sposób nie narobić bałaganu pracując równolegle nad kilkoma funkcjonalnościami? Itd. Itp.
Prostym rozwiązaniem na te i inne problemy jest model branchowania opracowany przez Vincenta Driessen’a. Ponieważ jeden obrazek może zastąpić więcej niż tysiąc słów oto on.
Tak wygląd dany model. Piękny prawda?
Dlaczego?
Zacznijmy od prostego przykładu, mamy kod developerski i po pewnym czasie wchodzimy na „produkcję”. Wiem, każdy by chciał mieć jeszcze chwilę być coś dopracować, ale niestety się tak nie da. Ponieważ chcielibyśmy by wszystko w naszym kodzie działało, jak należy, ale możemy spodziewać się zgłaszanych błędów, warto by sobie zaznaczyć w którym miejscu jest kod developerski a w którym już produkcyjny.
I w ten oto prosty sposób pracujemy sobie na branch’u develop a na masterze mamy produkcję.
Działa? Działa.
Po co nam wiele branch’y? A po to by zgodnie z konwencją w branchu master znajdował się wyłącznie kod w stanie gotowym dla produkcji. Wszystko co rozwijamy, twórzmy po za tym branch’em. Otwiera to pole chociażby do zautomatyzowania procesu budowania i wdrażania kodu produkcyjnego, mamy nowy commit na masterze, kompilujemy i wrażamy.
Swoją drogą czy to nie przypomina zasady pojedynczej odpowiedzialność znanej z zasad solid?
Z pozoru można odpowiedzieć twierdząco na powyższe pytanie, ale co na przykład z rozwijaniem różnych funkcjonalności przez różne zespoły, co z raportowanie postępów prac nad takimi funkcjonalnościami? Co z procesem zarządzania wydaniami? I co najważniejsze, gdzie poprawiać błędy wykryte w kodzie produkcyjnym?
Dlatego mamy jeszcze różne rodzaje branch’y, które może możemy wykorzystać do takich celów, a są to:
- Feature branches
- Release branches
- Hotfix branches
Każdy z tych rodzajów branch’y ma swoje specyficzne przeznaczenie i powinniśmy ściśle stosować się do zasad ich tworzenie i późniejszej obsługi.
Feature branches
Zajmijmy się teraz nowymi funkcjonalnościami w naszym projekcie, do obsługi których będziemy wykorzystywać feature branch’e. Zanim radośnie usiądziemy do kodowania warto byśmy sobie odpowiedzieli na pytanie, kiedy to wejdzie na produkcję? Nie zawsze taka informacja jest znane, dlatego feature branch’a musimy utworzyć sobie z branch’a develop.
Tworzenie feature branch’a będąc w branchu develop wykonujemy poleceniem
$ git checkout -b feature\myfeature develop
Kończąc pracę nad daną funkcjonalnością możemy go zmargować do brancha develop następującymi poleceniami
$ git checkout develop
$ git merge –no-ff feature\myfeature
$ git branch -d feature\myfeature
$ git push origin develop
I tu powstaje pytanie pod co używać flagi –no-ff margując branch’e? Zwykły marge wcieli nam wszystkie zmiany do brancha develop jednak w takim przypadku utracimy informacje o istnieniu branch’a feature.
Release branches
Release branches służą do przygotowywania nowych wydań produkcyjnych. Wszelkie zabiegi jakie wykonuje się na kodzie (zmiana numeru wersji czy numeru builda) powinny odbywać się właśnie na takich branchach, dzięki temu nie musimy commitować do brancha develop informacji, że podbiliśmy numer wersji. W branchu release ponadto możemy wykonywać ostatnie poprawki błędów.
Tworzenie release branch’y następuje z brancha develop. Dla przykładu jeżeli nasza aktualna wersja produkcyjna ma numer 1.5.999 a my wprowadziliśmy rewolucyjne zmiany w naszej aplikacji godne nadania wersji 2.0 to podbicie wersji następuje właśnie w tym miejscu.
$ git checkout -b release-2.0 develop
Wszelkie operacje związane z podbiciem wersji do 2.0
$ git commit -a -m „Podbicie wersji do 2.0”
Po utworzeniu nowego brancha powinniśmy w naszym kodzie podbić wersję na przykład zmieniając informacje w assembly info itd. Itp. Możemy to zrobić ręcznie, możemy zrobić sobie jakiś skrypt, który to za nas to zrobi, pełna dowolność.
Istotą życia branchy release jest przygotowanie kodu do wydania, i znów się kłania zasada pojedynczej odpowiedzialności, dlatego też jedyne zmiany jakich powinniśmy dokonywać w branchu release to poprawki błędów, żadnych nowych funkcjonalności, żadnych refaktoryzacji, nic tylko błędy. I gdy już dopełni się istota życia nowego wydania pora na wdrożenie.
$ git checkout master
$ git merge –no-ff release-2.0
$ git tag -a 2.0
No i dokonało się. Nowe wydanie poszło na produkcję wszyscy zadowoleni i w ogóle 🙂 to jednak nie wszystko pora na wyrównanie kodu w branchu develop.
$ git checkout develop
$ git merge –no-ff release-2.0
Dzięki temu wszelkie zmiany jakie zostały wprowadzone w branch’u release-2.0 (powtarzam -poprawki błędów) mamy znów w branchu develop i znów możemy przystąpić do pracy nad nowymi funkcjonalnościami.
Hotfix branches
Zrobiliśmy wdrożenie, wielkie święto trwa, development nowych funkcjonalności idzie w najlepsze. Żyć, nie umierać. Wszystko do czasu, aż rozlegnie się cierpki głos syreny alarmowej, oznajmiającej błąd na produkcji. Zdarza się.
Zgodnie ze starym przysłowiem znajdź mi błąd a brancha dla niego też się znajdzie.
Obsługa hotfix branches jest bardzo podobna do obsługi release branches, z tą różnicą, że tworzymy je już z wersji produkcyjnej.
Na przykład jeżeli nasza wersja produkcyjna ma wersję 1.2 i błąd, który został zgłoszony jest pierwszym błędem tworzymy sobie branch hotfix-1.2.1
$ git checkout -b hotfix-1.2.1 master
Operacje z podbiciem wersji na 1.2.1
$ git commit -a -m „Podbicie wersji do 1.2.1”
Tu także pojawia się konieczność podbicia wersji naszego oprogramowania, więc aż się prosi o automatyzację.
Jak już poprawimy co mieliśmy poprawić i dzięki temu świat już nie płonie, albo płonie, ale już znacznie mniej, pora na zakończenie pracy z hotfix’em.
$ git checkout develop
$ git merge –no-ff hotfix-1.2.1
W ten oto sposób, można rozłożyć sobie pracę nad projektem w różnych miejscach w repozytorium.
Mnie osobiście uderza uniwersalność zasad solid, a zwłaszcza zasady pojedynczej odpowiedzialności i tak jak klasa powinna mieć jeden i tylko jeden powód do zmiany tak każda faza projektu znajdzie sobie swojego branch’a.
Oczywiście przedstawione tutaj podejście jest w pewien sposób tylko szablonem, który można dowolnie modyfikować, mimo jednak warto przyjąć sobie jakąś konwencję.
A Ty korzystasz u siebie z feature branch’y?