Praca nad projektem informatycznym odbywa się najczęściej w kilkuosobowym zespole. W takim środowisku jedną z najważniejszych rzeczy
do jakiej chcemy dążyć jest stworzenie i utrzymanie odpowiedniego porządku pracy. Dlatego też bardzo ważna jest właściwa organizacja zadań programistycznych.
Deweloper otrzymuje lub sam wybiera swoje zadanie do wykonania (
feature), a następnie pracuje nad nim przez pewien czas na swoim lokalnym komputerze.
W tym samym czasie inni deweloperzy również realizują swoje zadania.
Wszystkie te prace muszą być wykonywane przez jakiś czas niezależnie, by następnie
połączyć ich rezultat w jedną całość. W takich warunkach bardzo często dochodzi do żonglerki kodem. Aby była ona bezpieczna dla całego projektu,
wymagane jest użycie funkjonalności Git-a, jaką jest tworzenie nowych gałęzi kodu.
Każdy deweloper rozpoczynając pracę nad swoim zadaniem tworzy zatem nową gałąź (nowego
brancha),
który jest niczym innym jak jego kopią kodu pobraną w konkretnym momencie czasu ze stabilnego brancha głównego całego projektu (
mastera). Tym samym
jeśli kilku programistów stworzy swoje branche, wówczas będą oni pracowali na niezależnych kopiach kodu projektu.
Appa Notka.
Objerzyj ten materiał na filmie. Jeśli chcesz więcej filmów takich jak ten — subskrybuj i polub.
Takie działanie z Twojej strony pozwoli nam lepiej zrozumieć, czy ta seria filmów jest dla Ciebie wartościowa
i czy powinna być kontynuowana.
Tworzenie brancha - git branch
Tworzenie nowego brancha w Gicie jest bardzo proste. Wchodzimy do głównego katalogu projektu
(tam gdzie znajduje się katalog .git) i uruchamiamy komendę:
Przykładowo jeśli naszym zadaniem jest stworzenie szkieletu modułu zarządania użytkownikami wówczas stworzymy brancha o nazwie zgodnej z jego przeznaczeniem:
Oprócz wykreowanej w naszej głowie nazwy dodaliśmy jeszcze na początku prefix
feature co spowoduje, że Git
zapisze tego brancha w "katalogu" o nazwie feature. Dzięki temu przeglądając w przyszłości spis branchy będziemy od razu wiedzieć,
że dany branch dotyczy nowej funkcjonalności, a nie jest na przykład poprawą błędu (bugfix). Bugfixy powinny mieć tworzony osobny katalog o nazwie
bugfix. Taka kategoryzacja branchy jest zgodna z dobrymi praktykami.
Teraz zwróćmy uwagę na jeszcze jeden fakt. Samo stworzenie brancha nie oznacza, że będziemy mogli od razu rozpocząć pracę nad kodem.
Owszem, stworzyliśmy niezależną kopię (osobną gałąź) w stosunku do głównego brancha
master, ale ona jak narazie jest częścią naszego lokalnego repozytorium.
Jeśli chcemy przełączyć na tego brancha kod projektu znajdujący się w working directory, wówczas musimy wykonać komendę:
W ten sposób kod naszego projektu będzie zawierał dokładnie tą wersję kodu, która znajduje się na branchu w naszym lokalnym repozytorium.
W tym miejscu dobrze jest wspomnieć, że istnieje pewne uproszczenie, które pozwala wykonać operacje stworzenia brancha jak i przełączenia
się na niego w working directory za pomocą jednego kroku. Wystarczy że użyjemy komendy:
Appa Notka.
Objerzyj ten materiał na filmie. Jeśli chcesz więcej filmów takich jak ten — subskrybuj i polub.
Takie działanie z Twojej strony pozwoli nam lepiej zrozumieć, czy ta seria filmów jest dla Ciebie wartościowa
i czy powinna być kontynuowana.
Praca na branchu
Stworzyliśmy więc brancha
feature/Add-user-management-skeleton i jesteśmy przełączeni na niego w naszym projekcie.
Teraz chcemy wykonać pierwsze prace w ramach feature'a. Załóżmy, że podjeliśmy decyzje, iż na tym branchu chcemy wykonać jedynie zarys
nowej funkcjonalności (dlatego nazwaliśmy go szkieletem). Tworzymy wiec interfejs
UserService, a w nim cztery metody:
create,
read,
update,
delete czyli popularny CRUD. Na tym poprzestaniemy.
Nie będziemy tworzyć klasy implementującej. Chcemy jedynie aby nasi koledzy (albo koleżanki) z zespołu wiedzieli jaki mamy plan na moduł zarządzania użytkownikami.
Plan jak widać jest bardzo prosty i szczerze mówiąc zbyt prosty. Nie powinniśmy tworzyć branchy tylko po to by dodać do projektu jeden banalny interfejs.
Natomiast w tym przypadku uprościliśmy sprawę, aby skupić się na istocie pracy z Gitem. Dla tego przykładu nie ma znaczenia czy stworzymy jeden czy dziesięć plików.
Ważne jest co zrobimy dalej. W jaki sposób doprowadzimy do tego, że nasze zmiany w postaci nowego pliku zostaną dodane do wspólnego brancha
master.
Dopiero wtedy będą one mogły być zauważone i pobrane przez innych programistów.
Korzystając z tego co już wiemy z rozdziału
Git - Pierwsze kroki,
najpierw dodajemy plik do staging area, a następnie komitujemy do lokalnego repozytorium:
Pamiętamy przy tym, że wszystkie operacje (
add,
commit) wykonujemy na branchu
feature/Add-user-management-skeleton,
więc jeśli teraz przełączymy się na branch
master (za pomocą komendy
git checkout), wówczas w naszym kodzie projektu (working area)
nie będzie takiego pliku jak
UserService.java.
Skoro ten plik znajduje się tylko na naszym branchu, a chcemy by znalazł się na wspólnym branchu
master, to musimy wykonać jeszcze jedną operację...
Z brancha do brancha - git merge
No właśnie. Przyszedł czas na wysłanie naszych zmian na wspólnego brancha.
Pamiętajmy, że
możemy zmieniać tylko brancha, na którego jesteśmy aktualnie zchekoutowani.
Więc jeśli chcemy wgrać zmiany z naszego feature brancha do
mastera, to musimy się na niego najpierw przełączyć.
W tym celu wykorzystamy znaną nam komendę:
Teraz nie pozostało nam już nic innego jak tylko wywołać komendę, która pobierze zmiany z
jednego brancha i wstawi je do
drugiego brancha (bieżącego).
Operację taką nazywamy scaleniem co zresztą jest zgodne z nazwą komendy, której używamy:
W naszym przypadku wykonamy więc:
Fast-forward
W efekcie jeśli wszystko poszło zgodnie z planem i
merge wykonał poprawne scalenie naszego kodu,
otrzymamy informację podobną do poniższej:
To nie oznacza nic innego jak to, że operacja zakończyła się sukcesem. Zmiany zostały bezproblemowo zmergowane.
W powyższej odpowiedzi Git-a na szczególną uwagę zasługuje wpis
Fast-forward.
Oznacza on, że pomiędzy momentem, w którym pobraliśmy kopię brancha
master, a
czasem w którym obecnie wykonujemy merge nic nie zmieniło się na branchu
master.
Żaden inny programista nie wykonał operacji scalającej jego kod z tym branchem. Trzeba przyznać, że taka sytuacja
zdarza się dosyć rzadko, szczególnie w zespołach składających sie z wielu programistów.
Recursive merge
Znacznie częściej spotykamy się z sytuacją, w której branch, do którego chcemy mergować został już zmieniony od czasu kiedy pobieraliśmy jego kopię.
W takim przypadku po wykonaniu komendy
git merge otrzymamy informację zwrotną do naszego narzędzia edycyjnego obsługującego Gita:
Domyślnie Git używa edytora Vi, który nie jest specjalnie intuicyjny. Warto zapoznać się z jego podstawowymi komendami.
I tak, jeśli akceptujemy wstawioną przez Gita informację o komicie (
commit message), wówczas po prostu wychodzimy z edytora wpisując
:q!. Natomiast jeśli chcemy zmienić tą wiadomość, to musimy użyć komendy, która zapisze zmiany i dopiero wtedy
doprowadzi do wyjścia z edytora, czyli
:wq. Po wszystkim możemy skomitować zmiany do repozytorium tworząc w ten sposób
komit
mergujący. Git poinformuje nas o rezultacie tego co się właśnie wydarzyło.
W ten sposób wykonaliśmy merge'a rekursywnego (recursive merge).
Odnajdując w przyszłości (w historii komitów) wpis dotyczący takiego merge'a każdy deweloper będzie wiedział,
że nie był to zwykły komit typu fast-forward, tylko że dotyczył on scalenia zmian z dwóch branchy.
Fast-forward vs Recursive merge
Zobaczmy teraz na jednym obrazku jak dokładnie wyglądają scalenia typu Fast-forward i Recursive merge. Należy tutaj szczgólnie zwrócić uwagę
na dwie różnice. Pierwsza z nich to występowanie niezależnego (wykonanego przez innego dewelopera)
komita do brancha master, który pojawił się między stworzeniem przez nas brancha
feature/Add-user-management-skeleton
, a komitem mergującym (merge commit).
Druga różnica polega właśnie na tym, że w przypadku strategii Recursive merge jest wymagany
komit scalający, który zmerguje zmiany z komita wrzuconego
do brancha
master ze zmianami wykonanymi w tym czasie przez nas na branchu
feature/Add-user-management-skeleton.
Na koniec wspomnijmy jeszcze, że pomarańczowy komit znajdujący się na branchu
master mógł się tam znaleźć
nie tylko przez to, że inny programiasta napierw stworzył brancha, a potem zmergował zmiany (w trybie Fast-forward), ale też
to my sami mogliśmy stworzyć drugiego brancha i zmergować zmiany zanim wykonaliśmy merge'a brancha
feature/Add-user-management-skeleton.
W tym miejscu można postawić pytanie...dlaczego tworzyliśmy kolejnego brancha skoro już pracowaliśmy na innym? Odpowiedź jest dosyć prosta.
W trakcie naszej pracy zdarza się, że trzeba nagle zmienić priorytety i wykonać jeszcze jedno zadanie po drodze (np. poprawienie błędu)
i takie zadanie musi być wykonane w osobnym branchu z osobnym komitem, gdyż jest ono całkowicie niezwiązane z tym co robiliśmy do tej pory.
Rozdział postanowiliśmy zakończyć, ale temat będzie jeszcze przez nas rozwijany. Istnieją bowiem zagadnienia,
które potrafią nieco skomplikować mergowanie. Jednym z problemów, którym zajmiemy się w kolejnym rozdziale jest sytuacja,
w której dwie osoby wykonały zmiany dokładnie w tych samych liniach tego samego pliku. Wtedy mergowanie nie może być wykonane automatycznie.
Programista musi podjąć decyzje, które zmiany zachować lub jak obie te zmiany połączyć w jedną całość.
Linki
https://git-scm.com/book/pl/v1/Gałęzie-Gita-Podstawy-rozgałęziania-i-scalania