Marcin Lewandowski
Marcin Lewandowski
Programista PHP ( Symfony ), blogger, trener oraz miłośnik kawy. Na co dzień pracuję z Symfony, RabbitMQ, ElasticSearch, Node.js, Redis, Docker, MySQL.

Jak zarządzać gałęziami wykorzystując Git Flow

Jak zarządzać gałęziami wykorzystując Git Flow

Git jest genialnym systemem kontroli wersji, którego siłą są gałęzie oraz ogromna swoboda w pracy. Jednak to co jest dobrodziejstwem może stać się przekleństwem, bowiem swoboda w tworzeniu gałęzi ich scalaniu oraz rozproszony model nie narzuca żadnego modelu pracy. W związku z chęcią optymalizacji pracy oraz usystematyzowania sposobu zarządzania projektem pojawiła się idea Git Flow.

Semantyczne Wersjonowanie

Zanim przejdę do samego Git Flow chciałbym wspomnieć o wersjonowaniu projektów. Jest to związane z faktem wydawania wersji naszego oprogramowania pracując w Git Flow. Jeśli pracowaliście przy większych projektach zapewne spotkaliście się z oznaczeniami np. v1.3.0. W zależności od firmy, projektu numeracja taka może wyglądać różnie, jednak gdzieś tam zawsze się pojawia.

Stosując Git FLow w projekt także będziemy wydawać wersję naszego oprogramowania, a skoro chcemy to robić to róbmy to w jakiś usystematyzowany sposób. I tu pojawia się na horyzoncie coś co zostało nazwane Semantycznym Wersjonowaniem 2.0.0. Jest to dokument opisujący nam jak powinniśmy nadawać numerację kolejnym wydaniom oprogramowania. Nie martwcie się prawdopodobnie podobną koncepcję już stosujecie w waszej aplikacji, a jeśli nie to macie okazję poznać sprawdzoną technikę od Toma Prestona-Wernera współzałożyciela GitHub-a.

Numeracja jest trzycyfrowa oddzielona kropkami X.Y.Z przy każdym kolejnym wydaniu dokonujemy inkrementacji. Każdą z wartości zwiększamy w innym momencie i tak:

X – gdy dokonujemy zmian niekompatybilnych z API np. takim momentem może być aktualizacja framework-a w naszym projekcie,

Y – gdy dodajemy nową funkcjonalność zachowując kompatybilność z poprzednimi wersjami, w ramach zmiany tego numeru możliwe jest wprowadzanie także poprawek nie łamiących kompatybilności,

Z – gdy dokonujemy naprawy błędu i nie łamie to kompatybilności z poprzednimi wersjami, upraszczając zmieniamy wartość gdy wydanie jest jedynie poprawkowe i nie dodaje żadnych nowych funkcjonalności,

Dodatkowo możliwe są wydania przedpremierowe, które oznaczamy poprzez dodanie po numerze wersji myślnika oraz znaków ASCII np. 1.3.0-beta1

Powyższy opis jest bardzo uproszczony i nie oddaje wszystkich niuansów dokumentu źródłowego. Jeśli chcecie poznać dokładne zasady to zachęcam do zapoznania z dokumentem: https://semver.org/lang/pl

Koncepcja Git Flow

Pracując z Git-em nieodłączne jest tworzenie gałęzi w ramach których rozwijamy nowe funkcjonalności, naprawiamy błędy, przygotowujemy wydania, przeprowadzamy refaktoring i wiele więcej. We wszystkich tych przypadkach schemat działania jest ten sam:

  • przechodzimy do gałęzi głównej,
  • tworzymy nową gałąź,
  • wprowadzamy modyfikacje,
  • wracamy do gałęzi głównej,
  • scalamy modyfikacje,
  • usuwamy zbędną gałąź

I tak w koło Macieju ;) Już na tym etapie przydała by się jakaś metoda pozwalająca nieco zautomatyzować te kroki, odpowiedzią na to jest Git Flow. Na tym etapie pracujemy zawsze z gałęzią master i gałęzią w której dokonujemy modyfikacji nazywaną często feature.

Powyższy schemat obrazuje bieżący model pracy, jednak przy odpowiednio długiej pracy z Git-em w projektach zapewne zauważycie potrzebę dodatkowej gałęzi. To z jej poziomu tworzymy rozgałęzienia i do niej scalamy poprawki oraz nowe funkcjonalności. Dopiero po testach gałąź tę scalimy z master-em tworząc nową wersję naszej aplikacji. W większości wypadków nazywamy ją develop i dzięki niej nasz sposób pracy zmienia się.

Kolejny problem z jakim możemy się spotkać przy pracy w większych zespołach to moment wydawania naszej aplikacji. Bowiem scalanie gałęzi rozwojowej z gałęzią produkcyjną czasem wymaga sporo czasu. Może to być związane z weryfikacją wszystkich poprawek oraz nowych funkcjonalności przez dewelopera, czy też czas trwania testów automatycznych jest dość długi. W tym czasie ktoś z zespołu może już do gałęzi develop wrzucić kolejną poprawkę, która nie powinna pojawić się w bieżącym wydaniu. Rozwiązaniem jest oczywiście kolejna gałąź release.

Korzystamy z niej w momencie wydawania naszej aplikacji, tworzymy rozgałęzienie z develop co gwarantuje nam, że nikt nie doda nam żadnych modyfikacji w trakcje pracy na niej. W gałęzi tej możemy wprowadzać jedynie poprawki i zminę numeru wersji, nic poza tymi dwiema rzeczami. Po zakończeniu pracy z gałęzią scalamy ją z master-em oraz develop, scalanie z gałęzią develop jest o tyle istotne, że gałąź ta musi być spójna z gałęzią master. Pominięcie scalania gałęzi release z develop spowodowało by nie posiadanie modyfikacji, a tym samym brak spójności i problemy w kolejnych wydaniach.

Jednak to nie wszystko co będzie nam niezbędne do pracy, bowiem jesteśmy jedynie ludźmi i popełniamy błędy. Mimo testów automatycznych oraz osób odpowiedzialnych za przetestowanie nowych funkcjonalności / poprawek, na produkcję mogą trafić błędy. Rozwiązujemy je poprzez gałąź hotfix tworzoną z poziomu gałęzi master.

Po sprawdzeniu i akceptacji scalamy taką gałąź z wersją produkcyjną oraz develop, która jak pamiętamy ma być w 100% spójna z gałęzią master.

Tak w uproszczeniu powinien wyglądać nasz schemat pracy z Git-em, może on się wam wydawać nieco abstrakcyjny i stwierdzicie, że wam wystarcza jedynie wersja master / develop. Jeśli się sprawdza to spoko, nie będę namawiał. Warto jednak wiedzieć jak działać, gdy projekt się rozrośnie i będzie obejmował wielu programistów. Zaś proces wydawniczy kolejnych wersji to nie jest commit do mastera ;)

Powyższe schematy są uproszczone na potrzeby przekazania idei zaś bardziej skomplikowana wersja Git Flow może wyglądać w poniższy sposób.

Instalacja

Instalacja w systemach Unix-owych jest banalna i sprowadza się do jednej komendy.

Mac OS X

Przechodzimy do terminala i uruchamiamy komendę:

brew install git-flow

Jeśli nie macie brew czyli menadżera pakietów Homebrew to instalujecie go poleceniem:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Ubuntu

W systemach pochodnych Debian-owi uruchamiamy terminal i uruchamiamy polecenie:

sudo apt-get install git-flow

Windows

Instalacja w systemach z rodziny Windows jest nieco bardziej skomplikowana i wymaga nieco więcej zachodu jednak warto. Zaczynamy od instalacji Git-a ze strony git-scm.com. Następnie przechodzimy na GitHub-a do projektu nvie/gitflow, gdzie w Wiki mamy opisaną instalację.

Praca w projekcie

Jeśli mamy zainstalowany Git Flow to możemy zaczynać pracę z projektem. Po wpisaniu w konsoli polecenia:

git flow

Otrzymamy listę dostępnych poleceń, jest ona stosunkowo krótka co ułatwia ich zapamiętanie. I tak mamy:

init – inicjalizacja projektu do pracy z Git Flow, feature – zarządzanie gałęziami z nowymi fukcjami / poprawkami, release – zarządzanie gałęziami z wydaniami aplikacji, hotfix – zarządzanie gałęziami z krytycznymi poprawkami wprowadzanymi bezpośrednio na produkcję, support – zarządzanie gałęziami wsparcia starszych wersji, tym nie będziemy się zajmowali, gdyż jak do tej pory nie znalazłem dla tego zastosowania, version – wyświetla numer wersji,

Polecenia te mają swoje rozwinięcia jednak są one tak proste, że sprowadzają się do jednego schematu:

Każde polecenie zarządzające gałęziami ma rozpoczynające prace nad daną rzeczą start, kończące prace finish. Dodatkowo możemy opublikować publish, czyli wysłać do repozytorium zdalnego naszą gałąź. Oraz możemy pobrać pull czy to zmiany w gałęzi czy też samą gałąź.

Inicjalizacja pracy z git flow w projekcie

Kiedy chcemy rozpocząć pracę w projekcie wykorzystując dobrodziejstwa Git Flow to należy przejść do katalogu projektu i wywołać polecenie:

git flow init

Zachowanie polecenia w początkowej fazie może nieco się różnić w zależności czy zostanie wykonane na nowym projekcie nie posiadającym jeszcze repozytorium Git-a, czy też w już istniejącym. Jeśli nie posiadamy jeszcze repozytorium to inicjalizacja będzie wyglądała następująco:

Jeśli zaś posiadamy już repozytorium zobaczymy coś podobnego do poniższej listy:

W obu przypadkach zostaniemy zapytani o nazewnictwo poszczególnych rodzajów gałęzi. Jeśli ono nam odpowiada to nie musimy niczego zmieniać jedynie potwierdzać domyślne ustawienia klawiszem Enter. Po zakończeniu powinniśmy znajdować się na gałęzi develop co możemy sprawdzić poleceniem git branch

Jest to dla nas główna gałąź i możemy niejako traktować ją jak master bowiem, to do niej zawsze będziemy wracać po wywołaniu poleceń. Nie ważne czy będziemy robić poprawkę, nowe wydanie czy usuwać błąd krytyczny. Po wydaniu polecenia git flow finish wrócimy do gałęzi develop.

Rozwiązywanie zadań

Zaczynając pracę nad kolejnym zadaniem nie zależnie od jego charakteru wykonujemy polecenie:

git flow feature start artykul-1

Gdzie artykul-1 jest naszą nazwą np. z systemu zadań. W tym momencie została utworzona gałąź na podstawie gałęzi develop i zostaliśmy na nią przełączeni. O tym wszystkim informuje nas git flow w opisach pod wydanymi poleceniami.

Teraz możemy pracować na nowej gałęzi wedle własnego uznania, a po zakończeniu prac wykonujemy polecenie:

git flow feature finish artykul-1

Spowoduje to zakończenie prac nad zadaniem i połączeniem go z gałęzią develop i zniszczeniu gałęzi na której pracowaliśmy. Także tutaj zostaniemy o wszystkim poinformowani.

Poprawki krytyczne

Metodologia jest identyczna jak w przypadku pracy ze zwykłym zadaniem, z tą różnicą że odwołujemy się do menadżera gałęzi hotfix poleceniem:

git flow hotfix start issue-45

Po wywołaniu polecenia na postawie gałęzi master jest tworzona nowa gałąź o podanej nazwie, w tym przypadku issue-45. Po czym zostajemy na nią przełączeni.

Następnie po zakończeniu prac wykonujemy polecenie:

git flow hotfix finish issue-45

Po wywołaniu polecenia zostaniemy poproszeni o wprowadzenie opisu dwukrotnie, raz dla gałęzi master, drugi dla gałęzi develop. Dodatkowo konieczne będzie podanie opisu dla tag-a przypisywanego do commit-a w gałęzi master. Po czym gałąź ta zostanie scalona z gałęzią master oraz develop. Jak widać poniżej z logów dostarczanych przez git flow.

Wypuszczanie nowej wersji aplikacji

Ostatnią rzeczą jaką powinniśmy wiedzieć jak wykonać to wydanie nowej wersji naszej aplikacji. I podobnie jak wcześniej odwołujemy się do odpowiedniego menadżera gałęzi, w tym przypadku release poleceniem:

git flow release start v0.1.0

Następnie testujemy aplikację, poprawiamy numer wersji i kończymy pracę poleceniem:

git flow release finish v0.1.0

Po wydaniu polecenia zostanie otwarte okno na opis commit-a, który zostanie utworzony. Po dodaniu opisu zamykamy edytor co spowoduje otwarcie nowego edytora tekstu w którym wpisujemy nazwę tagu, tag ten zostanie przypisany do commit-a. Najczęściej stosuje się numer wersji, jak widać to poniżej.

Po wprowadzeniu wszystkich informacji podobnie jak to ma miejsce z poprawkami krytycznymi następuje scalenie gałęzi z gałęzią master oraz develop. Dodatkowo commit zostaje otagowany numerem wersji, następnie gałąź jest niszczona, a my zostajemy przełączeni na gałąź develop. Dokładna lista wykonanych operacji poniżej:

Podsumowanie

Praca z Git Flow jest bardzo przyjemna i umożliwia usystematyzowanie procesu wydawania aplikacji. Wprowadza także pewien porządek w gałęziach oraz nadaje im określone przeznaczenie. Jest to o tyle istotne, że w przypadku pojawienia się nowego programisty czy to w firmie czy w zespole bardzo łatwo jest się wdrożyć w nasz system pracy.

A może używacie Git Flow lub jakiegoś innego systemu organizującego pracę z Git-em w firmie czy też zespole ??