Seriál Online kurz Git – Paralelné svety a Git branch – 4. diel

V niekoľkých predchádzajúcich dieloch (V Hlbinách Súborového Systému – 3. diel a Lokálna Práca so Súbormi – 2. diel) tohto seriálu sme si ukázali základy práce s Gitom. Git je neuveriteľne výkonný nástroj pre správu verzií, ktorý umožňuje vývojárom pracovať na rôznych častiach projektu súčasne prostredníctvom vetiev (branches). Tento prístup umožňuje vytváranie paralelných svetov, kde sa môžu súbežne vyvíjať nové funkcie, opravovať chyby alebo testovať experimenty. V tomto článku sa pozrieme na to, čo sú vetvy v Gite, ako ich používať a aké sú najlepšie postupy pre efektívnu správu vetiev. Ako použiť Git ako stroj času, ktorý ťa vie presunúť v histórii zmien zdrojových kódov do ľubovoľného bodu v minulosti. To je nepochybne hlavná úloha verzionovacieho systému, ale nie je to jeho jediný hlavný prínos. Ďalší veľmi mocný nástroj, ktorý Git (ale aj iné lepšie verzionovacie systémy) ponúka, je vetvenie kódu – teda mergovanie a branchovanie. Ako to funguje a prečo je dobré techniku branchovania čo najviac ovládnuť, sa pozrieme v tomto diely.

Rýchle preopakovanie alebo kde sme to skončili

V poslednom diely sme si ukázali, ako vlastne Git uchováva históriu súborov. Obsah súboru sa uloží do blobu, informácie o súboroch a priečinkoch do tree objektov a stav working copy sa ako snapshot uloží spolu s commitom. Všetky tie objekty majú svoj hash, cez ktorý sa na seba odkazujú. To všetko sú princípy, ktoré treba mať na pamäti, keď sa púšťame do diskusií o branchovaní a mergovaní. Než sa ponoríme do toho, ako branchovanie funguje v Gite, je dobré si povedať, čo to vlastne je a na čo to je dobré a to zjednodušeným pohľadom bez detailov Gitu.

Čo sú vetvy (Branches) v Gite?

Vetva je ukazovateľ na konkrétny commit v histórii projektu. Umožňuje izolovať prácu na nových funkciách, opravách chýb alebo iných zmenách bez ovplyvnenia hlavnej verzie kódu.

Hlavné príkazy pre prácu s vetvami:

  • git branch – zobrazenie, vytváranie a mazanie vetiev
  • git checkout – prepínanie medzi vetvami
  • git merge – zlúčenie vetiev
  • git rebase – presunutie alebo kombinovanie commitov z jednej vetvy na druhú

git branch # Zobrazenie zoznamu vetiev
git branch nová-vetva # Vytvorenie novej vetvy
git checkout nová-vetva # Prepnutie na novú vetvu
git checkout -b nová-vetva # Vytvorenie a prepnutie na novú vetvu
git branch -d stará-vetva # Odstránenie starej vetvy

Vytváranie a prepínanie medzi vetvami

Vytvorenie novej vetvy:

  • Novú vetvu môžete vytvoriť pomocou príkazu git branch <názov-vetvy>.
  • Po vytvorení novej vetvy na ňu môžete prepnúť pomocou príkazu git checkout <názov-vetvy>.

Príklady:

git branch feature-login # Vytvorenie vetvy pre novú funkciu
git checkout feature-login # Prepnutie na vetvu feature-login

Kombinovaný príkaz pre vytvorenie a prepnutie:

git checkout -b feature-login # Vytvorenie a prepnutie na vetvu feature-login

O čo vlastne ide a ako na životný cyklus projektu

Dobrý nástroj pre vývojára softvéru poznáš podľa toho, že sa vie prispôsobiť potrebám projektu. Tie totiž vyplývajú zo životného cyklu projektu a ten môže napríklad pri komerčných projektoch vyplývať z obchodného modelu, ktorý je pre úspech kľúčový. Ak máš nástroj, ktorý vám nedovoľuje realizovať životný cyklus, aký chcete, tak v reťazovej reakcii môže škrtiť hoci aj to obchodné oddelenie.

Príkladom takých požiadaviek na životný cyklus môže byť aj projekt, na ktorom práve pracujem. Máme tu niekoľko požiadaviek (vyplývajúcich zo spôsobu, ako sa produkt predáva a distribuuje k zákazníkom):

  1. chceme nepretržitý vývoj novej funkcionality
  2. chceme každý týždeň stabilný release pre zákazníkov a tiež musíme byť schopní vydať stabilný release kedykoľvek s kritickou opravou
  3. release má byť selektívny – nie všetci zákazníci dostávajú všetky verzie
  4. chceme mať mierne upravenú verziu pre každého zákazníka
  5. chceme vyvíjať aj funkcionalitu, ktorej vývoj trvá aj dlhšie ako týždeň, ale tak, aby nenarúšala alebo nebrzdila vývoj a release menších vecí

Ak sa pozrieš na tieto požiadavky tak to, čo uvidíš sú vlastne paralelné svety, v ktorých žije zdrojový kód. V jednom svete do neho denne prichádzajú nové funkcionality, v druhom sa už len opravujú chyby a stabilizuje sa. Potom je tam skupina paralelných svetov, v ktorých je zdrojový kód vo verzii určenej pre daného zákazníka a s jeho úpravami (jeden zákazník = jeden svet). A na koniec je tam skupina svetov, kde v každom prebieha vývoj nejakej dlhotrvajúcej funkcionality.

Git branchovanie

No, vyzerá to komplikovane, ale našťastie je tu Git. Ako pomocník, ktorému sa netreba prispôsobovať, ale ktorý sa prispôsobil projektu. Git umožňuje branchovanie kódu, na čo sa dá pozerať ako na vytvorenie paralelného sveta pre tvoj zdrojový kód, kde si môže žiť nezávisle od zvyšku. A mergovanie je potom presun zmien z jedného sveta do druhého. Poďme sa pozrieť, ako vyzerajú také paralelné svety nášho projektu na obrázku:

Git paralelné svety branche

Master je branch, v ktorom prebieha hlavný vývoj – teda do neho sa commitujú najnovšie veci. V momente uzavretia verzie pre daný týždeň sa zmeny zamergujú do stable, kde sa testuje a opravujú chyby (chyby sa potom mergujú aj späť do mastera). Potom je tam branch pre každého zákazníka zvlášť, v ktorom môže mať svoje customizácie (ale tých by nemalo byť veľa) a ktorý obsahuje aktuálnu verziu zdrojových kódov, ktorá bola zákazníkovi nasadená. A nakoniec sú tam dočasné branche pre vývoj dlhotrvajúcej funkcionality. Tie vznikajú na začiatku jej vývoja a zanikajú na konci.

Na tomto príklade je pekné, že demonštruje hneď niekoľko spôsobov, ako sa dá branchovanie využiť. Pritom ale hlavná myšlienka branchovania je jediná: umožňuje ti mať zdrojový kód v niekoľkých kópiách, s ktorými vieš nezávisle pracovať, ale zároveň medzi nimi vieš jednotlivé zmeny presúvať.

Referencie

Predtým, než začneme plniť príkazový riadok, tak si musíme povedať ešte o referenciách v Gite. Branchovanie a mergovanie je hlavne o práci s commitmi, lebo commit je jednotka zmeny a hlavný účel branchu je schopnosť mať niekoľko verzií kódov s rôznymi zmenami, prípadne tieto zmeny medzi nimi presúvať. Povedali sme si, že commit má svoj hash, ktorým sa vieme na neho odkázať. Hashe sú perfektný nástroj pre počítače, ale zlý pre ľudí, lebo sú neintuitívne a dlhé na pamätanie. Preto Git používa takzvané referencie.

Referencie sú vlastne pomenované odkazy na commity. Inak povedané, referencia je meno, ktoré má povedané, že v danom momente súvisí s nejakým commitom. Ak pracujeme s referenciou (použijeme jej meno v príkazovom riadku), tak to Git vyhodnotí tak, že chceme pracovať s commitom, na ktorý sa referencia odkazuje. Vzťah referencie a iných interných objektov vieme znázorniť takto:

 

Git BLOB tree commit ref

O referenciách sa dá povedať ešte niekoľko faktov:

  • referencie musia mať jedinečný názov v rámci jedného repozitára
  • na jeden commit sa môže odkazovať ľubovoľný počet referencií
  • existujú symbolické referencie – referencie, ktoré sa odkazujú na iné referencie
  • referencie sú v .git priečinku uložené v podpriečinku refs

Ideme na to – prvý branch

A je to praktická časť. Pred spustením príkazu pre branchovanie si skúsime zobraziť, ako to vyzerá v našom repozitári:

Git pred branchom

Vidíme teda sériu commitov, kde každý (okrem prvého) sa odkazuje na svojho predchodcu. Tiež tam vidíme referenciu master. Tá sa odkazuje na posledný commit, ktorý sme urobili a pri každom ďalšom commite sa automaticky posúva. A taktiež vidíme symbolickú referenciu Head. Tá hovorí, že práve pracujeme (vo working copy) s masterom. Je na čase vytvoriť nový branch:

> git branch second

Ak Git nič nevypíše, znamená to, že sme boli úspešní. Príkaz branch sa dá použiť aj na kontrolu aktuálneho stavu branchov:

>git branch –list

* master

second

Vo výpise vidíme dva branche, pričom pri branchy master je hviezdička, čo znamená, že aktuálne pracujeme s týmto branchom. A takto to vyzerá graficky znázornené:

Git po branchy

Čo sa vlastne zmenilo? Veľa nie. Pribudla akurát nová referencia. Branche totiž na úrovni Gitu nie je nič iné len referencia, ktorá sa odkazuje na commit. Ešte viac jasnejšie to začne byť o chvíľu. Náš nový paralelný svet je zatiaľ totožný s tým pôvodným. Poďme ho zmeniť. Najprv sa musíme do neho prepnúť a to príkazom checkout:

> git checkout second

Switched to a new branch ‚second‘

> git branch –list

master

* second

Teraz, keď sme sa ocitli v našom novom paralelnom svete, urobíme v ňom zmenu. Napríklad v súbore test, ktorý nám ostal z predchádzajúcich pokusov, doplníme na koniec výkričník. Zmenu pridáme do staged changes a commitneme.

> git add test

> git commit -m „Zmena v second“

[second 532f7b4] Zmena v second

1 file changed, 1 insertion(+), 1 deletion(-)

Na zobrazenie aktuálnej histórie použijeme trochu komplikovanejšiu verziu príkazu log:

> git log –graph –oneline –decorate –all

* 532f7b4 (HEAD -> second) Zmena v second

* e83a556 (master) Pridany testovaci subor

* ce3944e Presun suboru

* 74622d0 Odstranenie testovacieho suboru

Čo sa z toho dá vyčítať? Úplne hore je commit, ktorý sme spravili v branchy second. Ten je teraz ako keby jeden commit vpred, pred masterom. Referencia master zostala ukazovať na predchádzajúci commit. Zatiaľ v podstate nedošlo k paralelnej práci. To sa teraz zmení. Prepneme sa  do branchu master, urobíme zmenu v súbore index.html a commitneme ju:

> git checkout master

Switched to branch ‚master‘

(zmena v súbore index.html)

> git add subdir\index.html

> git commit -m „Zmena v master“

[master e9cce4c] Zmena v master

1 file changed, 1 insertion(+), 1 deletion(-)

Hotovo. Ako to vyzerajú naše paralelné svety teraz?

> git log –graph –oneline –decorate –all

* e9cce4c (HEAD -> master) Zmena v master

| * 532f7b4 (second) Zmena v second

|/

* e83a556 Pridany testovaci subor

* ce3944e Presun suboru

No tak vetvenie reality je tam už pekne vidieť. Commit „Pridany testovaci subor“ teraz predstavuje rodičovský commit pre dva rôzne iné commity. Tie sú vykonané každý v inom branchy. V skutočnosti sme sa ale nedozvedeli až tak veľa nového. Skúsim to zhrnúť:

* každá zmena je do repozitára uložená cez commit

* každý commit má svoj predchádzajúci (rodičovský) commit

* môžem mať ľubovoľné množstvo referencií, kde každá sa odkazuje práve na jeden commit

* commit môže byť rodičovský commit až pre dva iné commity – vtedy dochádza k vetveniu

Naozaj nový fakt je len ten posledný. Všetko ostatné sme vedeli, než sme sa pustili do branchovania. Branche teda v skutočnosti nie sú nič komplikované, ani magické. Branch je séria commitov (zmien), ktorá má ale začiatok niekde v commite spoločnom s ostatnými branchami. A aby sa s tým všetkým dobre pracovalo, sú tu referencie, aby sme sa na túto sériu commitov vedeli nejako pekne odkazovať. Výsledok je teda: commity + referencia = branch.

A teraz späť – mergujeme

Takže sme si urobili výlet do paralelného sveta (branch second), kde sme zmenili súbor test. Za tú dobu sme v našom hlavnom svete (master) zmenili súbor index.html. V prípade, že si stále prepnutý v branchy master, môžeš si pozrieť, že súbor test je v pôvodnom stave. Teraz chcem zmeny zo second dostať do master tak, aby sme mali všetko po kope. Ideme mergovať.

Merge sa vždy vykonáva v branchy, do ktorého zmeny prichádzajú. Teda ak ideme teraz mergovať zmeny zo second do master, musím byť prepnutý v master. Následne použijeme príkaz merge.

> git merge second

Merge made by the ‚recursive‘ strategy.

test | 2 +-

1 file changed, 1 insertion(+), 1 deletion(-)

Z výpisu je jasné, že sa v podstate aktualizoval súbor test (ten bol zmenený v branchy second). Ako teraz vyzerá výpis logu?

> git log –graph –oneline –decorate –all

*   63f0320 (HEAD -> master) Merge branch ‚second‘

|\

| * 532f7b4 (second) Zmena v second

* | e9cce4c Zmena v master

|/

* e83a556 Pridany testovaci subor

* ce3944e Presun suboru

Na grafe vidno, ako sa zmeny v branchy second pripojili späť do branchu master. V skutočnosti sa vytvoril špeciálny merge commit, ktorý má až dva rodičovské commity. Ak si teraz pozrieš súbor test, zistíš, že v ňom máš zmeny z branchu second. Grafické znázornenie predchádzajúceho logu vyzerá takto:

Po Git mergy

Branch second považujeme za dočasný branch, ktorý slúžil len na určitý čas, preto je ho dobré odstrániť. Ale ešte predtým zistíme, či všetky zmeny z neho sú zamergované:

> git branch –merged

* master

second

Príkaz branch v takomto tvare zobrazuje aktuálny branch (ten s hviezdičkou) a zoznam všetkých ostatných, ktorých commity boli zamergované do neho. Takto sa dá skontrolovať, že v branchoch neostali nejaké zmeny. Teraz second zmažeme:

> git branch -d second

Deleted branch second (was 532f7b4).

Zlučovanie vetiev

Zlučovanie vetiev umožňuje skombinovať zmeny z jednej vetvy s inou. Najčastejšie sa používa na zlúčenie funkčných vetiev s hlavnou (master alebo main) vetvou po dokončení práce na funkcii alebo oprave chyby.

Príklady:

git checkout main # Prepnutie na hlavnú vetvu
git merge feature-login # Zlúčenie vetvy feature-login s hlavnou vetvou

Riešenie konfliktov pri zlučovaní

Pri zlučovaní vetiev môžu nastať konflikty, ak sa zmeny v oboch vetvách týkajú rovnakých častí kódu. Git upozorní na tieto konflikty a označí ich v súboroch.

Kroky pri riešení konfliktov:

  1. Otvorte konfliktný súbor v textovom editore
  2. Manuálne upravte konfliktné časti podľa potreby
  3. Odstráňte značky konfliktov (<<<<<<, ======, >>>>>>)
  4. Potvrďte vyriešenie konfliktov príkazmi git add a git commit

Príklady:

git add konfliktný_súbor
git commit -m „Riešenie konfliktu pri zlúčení“

Použitie rebasingu

Rebase je alternatívny spôsob, ako skombinovať zmeny z jednej vetvy do druhej. Namiesto vytvorenia merge commit-u presunie alebo „prehrá“ commity z jednej vetvy na druhú, čím udržuje históriu projektu lineárnu.

Príklady:

git checkout feature-login # Prepnutie na vetvu feature-login
git rebase main # Presunutie commitov z main vetvy na začiatok feature-login
Rebase je užitočný na udržiavanie čistej histórie projektu, ale môže byť zložitejší na správu v prípade konfliktov.

Tagy pre dôležité verzie

Tagy sú užitočné na označenie dôležitých bodov v histórii projektu, ako sú verzie vydaní, míľniky a ďalšie významné udalosti.

Vytváranie tagov:

git tag v1.0 # Vytvorenie ľahkého tagu
git tag -a v1.0 -m „Prvá stabilná verzia“ # Vytvorenie anotovaného tagu

Push tagov do vzdialeného repozitára:

git push origin v1.0 # Push konkrétneho tagu
git push origin –tags # Push všetkých tagov

Strach z paralelných svetov

Pracoval som na niekoľkých projektoch, ktoré používali nejaký verzionovací systém, ktorý dokázal pracovať s branchami a pritom to vôbec nepoužívali. Časom sa ukázalo, že technický vedúci (a v dôsledku toho aj vývojári) mali z toho nástroja strach. Strach z toho, že keď začneš tvoriť paralelné svety, vymkne sa ti to z pod kontroly. Fakt je, že pri branchovaní musíš vedieť, čo robíš.

Môj príklad vyzerá jednoducho, lebo naozaj jednoduchý je. To, s čím sa stretneš v praxi je často komplikovanejšie (viac branchov, viac zmien v nich, viacnásobné merge atď.). Naozaj sa v tom dá stratiť, ak človek nevie, čo robí. Na druhej strane pochopiť základné princípy nie je nič strašné. Len si treba tiež uvedomiť, že branchovanie je zručnosť ako každá iná – opakovaním sa v nej zlepšuješ. Dôležité je začať v malom.

Ideálne začať pracovať s branchami určenými pre úlohy. To sú tie, ktoré existujú len počas implementácie. Sú to branche, ktoré žijú len krátko a väčšinou sa mergujú len raz – smerom späť do mastera. Po niekoľkých takýchto branchoch a mergoch ti tá technika začne určite pripadať jednoduchá. A až sa budeš cítiť isto v tomto, môžeš začať budovať dlhodobejšie branche s viacnásobnými mergami. Chce to trpezlivosť a sústredenosť. Nič viac.

Záver a sumarizácia

Na začiatku som napísal, že dobrý nástroj pre vývojárov sa vždy prispôsobí ich práci. Pre Git a branchovanie to jednoznačne platí. Akýkoľvek proces práce so zmenami v kóde, ktorý si vymyslíš, je veľmi pravdepodobné, že Git ho bude schopný pokryť. Správa vetiev v Gite je nevyhnutná pre efektívnu spoluprácu a správu kódu. Použitie vetiev vám umožní pracovať na rôznych častiach projektu paralelne, zatiaľ čo tagy vám pomôžu označiť dôležité verzie a míľniky. Správne používanie príkazov pre vytváranie, zlučovanie a rebasing vetiev vám pomôže udržať váš projekt organizovaný a bez konfliktov. Práca s branchami nemusí byť vždy jednoduchá, život je život a softvérový projekt so sebou prináša aj komplikovanejšie situácie ako len vytvorenie branchu, niekoľko zmien v ňom a spätný merge. Aj preto má Git vo výbave omnoho viac príkazov, ktoré súvisia s mergovaním ako sme si teraz ukázali. Nabudúce sa pozrieme na niektoré komplikovanejšie techniky, ktoré s branchovaním súvisia.

Odporúčania na prácu s GITom

Pri práci s vetvami dbajte na pravidelné zlúčenie zmien z hlavnej vetvy, aby ste minimalizovali konflikty. Využívajte tagy na označenie dôležitých verzií a používanie rebasingu pre udržanie čistej histórie. Tieto postupy vám pomôžu efektívne spravovať váš kód a zlepšiť spoluprácu v tíme.

Objavte online kurzy na Git a GitHub

Prehľad publikovaných článkov

  1. Seriál Online kurz Git – Začíname s Gitom – 1. diel
  2. Seriál Online kurz Git – Lokálna Práca so Súbormi – 2. diel
  3. Seriál Online kurz Git – V Hlbinách Súborového Systému – 3. diel
  4. Seriál Online kurz Git – Paralelné svety a Git branch – 4. diel
  5. Seriál Online kurz Git – Mergovanie s Konfliktom, Tagy a Skrytie Zmien – 5. diel
  6. Seriál Online kurz Git – Vzdialené repozitáre, GitHub, Bitbucket – 6. diel
  7. Seriál Online kurz Git – Clean, Reset, Rebase, Revert nástroje do každého počasia – 7. diel
  8. Seriál Online kurz Git – Najčastejšie problémy, faily a fuckupy – 8. diel

Autor

Miroslav Reiter

Programátor, manažér a marketér, ktorý mudruje vo vlastnej vzdelávacej spoločnosti IT Academy. Workoholik so 134 certifikáciami a 13 titulmi. Vytvoril som vzdelávaciu platformu vita.sk, pretože milujem vzdelávanie a všetko čo k nemu patrí. Pomáham firmám ale aj jednotlivcom zlepšovať ich podnikanie a IT. Certifikácie: Microsoft certifikovaný tréner, Google certifikovaný tréner, ITIL, PRINCE2 tréner. 40000+ vyškolených klientov a 1000+ firiem, ktorým som pomohol Referencie: Národná Rada SR, Slovnaft, IBM, Panasonic, Ministerstvo obrany SR, ČSOB, Generali, Tatra banka, Európska komisia, SPP, Pixel Federation, ESET.