Seriál Online kurz Git – Vzdialené repozitáre, GitHub, Bitbucket – 6. diel

Jednou z najdôležitejších výhod používania Gitu je, že umožňuje zdieľať zdrojový kód na diaľku. O tom, že je v tom Git naozaj dobrý, vypovedá aj fakt, že je to distribuovaný verzionovací systém. Ale všetky operácie, ktoré sme s repozitárom doteraz v predchádzajúcich dieloch seriálu robili, boli len lokálne. Dnes sa teda pozrieme na to, ako sa pracuje so vzdialeným repozitárom typicky GitHub, GitLab či BitBucket.

Github informácie

Rýchle preopakovanie alebo kde sme to skončili

V prechádzajúcich častiach sme si ukázali, ako funguje v Git-e verzionovanie, aj ako sa dá pracovať s paralelnými verziami kódu. Hlavná jednotka zmeny je commit, všetky zmeny sú zaznamenávané pomocou špeciálnych objektov (blob, tree) a všetko má svoj hash, ktorý predstavuje jedinečný identifikátor daného objektu. Ukázali sme, ako sa používajú branche, ako sa mergujú a čo robiť, keď dôjde ku konfliktu. To všetko bola práca s lokálnym repozitárom na tvojom počítači. Aj keď scenár s lokálnym repozitárom je možný, omnoho pravdepodobnejšie je, že sa budeš pripájať na vzdialený repozitár ako napríklad GitHub či BitBucket, aby si svoju prácu zdieľal s kolegami alebo s celým svetom.

Repozitáre GitHub

Aj keď možno fráza „odošlem zmeny do vzdialeného repozitára“ znie jednoducho, v skutočnosti to také jednoduché nie je. Musíš si uvedomiť, že lokálne máš u seba repozitár s niekoľkými paralelnými verziami kódu, rovnako ako niektorý tvoj kolega má u seba niečo podobné, ale s možnými odlišnosťami. A teraz je úlohou Gitu zabezpečiť, aby ste si všetci navzájom vymenili informácie podľa možnosti s čo najmenšími problémami.

Git Github rozdiely

Vzdialený repozitár

Na ukážku práce so vzdialeným repozitárom potrebujeme samozrejme vzdialený repozitár. Pre účely tohto blogu zvolím nejakú free verziu, kde je ho možné jednoducho vytvoriť. Úplne logická voľba by bol github.com. Je to najznámejší free git repozitár a veľmi pravdepodobne si už o ňom počul. Github.com je dobrá voľba, ak hľadáte git repozitár na zdieľanie výsledkov práce s celým svetom. Už umožňuje aj robiť privátne repozitáre, teda také, kde je obsah dostupný len vybranej skupine používateľov.

Github vlastnosti

Aj pre túto možnosť použijem v tomto prípade server bitbucket.org. Ten prevádzkuje firma Atlassian a umožňuje zadarmo vytvárať privátne repozitáre s určitými obmedzeniami (napríklad počtom používateľov, ktorí majú do neho prístup). Ďalšou alternatívou pre privátne free Git repozitáre je visualstudio.com (opäť zadarmo s určitými obmedzeniami). Takže som si vytvoril na Bitbuckete účet a tiež repozitár https://bitbucket.org/it-academy-sk/blog_example

Repozitár bitbucket

Vytvárame klon

V 1. diely seriálu (Začíname s Gitom) sme vytvorili nový repozitár príkazom init. To bol naozaj prípad, kedy sme začínali od nuly. V prípade blog_example repozitára ho ale za nás vytvoril Bitbucket (v zmysle toho, že na môj pokyn zavolal príkaz na jeho vytvorenie).

Git init vytváranie repozitárov

To, čo teraz potrebujem, je dostať klon tohto repozitára na môj lokálny počítač. To sa robí (celkom logicky) príkazom clone. Poďme teda na to:

> git clone https://bitbucket.org/it-academy-sk/blog_example

Cloning into ‚blog_example’…

Checking connectivity… done.

Na disku sme tak získali plnohodnotný repozitár, s ktorým môžeme pracovať rovnako ako s tým, ktorý sme vyrobili príkazom init.

Mapovanie branchov

Repozitár, ktorý som si takto vyklonoval na disku, nie je prázdny. Obsahuje zatiaľ commit, v ktorom bol pridaný jeden súbor – index.html. Zaujímavejší je ale výpis všetky branchov, ktoré sa v  ňom nachádzajú:

> git branch -a

* master

remotes/origin/master

Okrem štandardnéhomasterbranchu je tam ešte jeden, ktorý sa volá „remotes/origin/master“. Je to takzvaný tracking branch, ktorý reprezentuje master branch na serveri. Poďme to postupne rozobrať.

Git práca branche

Ako sme si vysvetľovali skôr, branch je vlastne ukazovateľ na commit. Ak máš u seba niekoľko branchov, tak sú to paralelné série commitov s jedným ukazovateľom za každý branch. Rovnako to vyzerá na vzdialenom serveri. Tam existuje tiež množina branchov, a teda vlastne séria commitov. Tie dva svety branchov (tvoj lokálny aj ten vzdialený) žijú ale nezávislými životmi. To znamená, že ty môžeš branch meniť, a tiež sa v rovnakom čase môže meniť jeho dvojička na serveri. Je to reálna situácia, s ktorou sa Git musí vysporiadať a nejako dokázať spojiť tvoje lokálne zmeny s tými, čo sa udiali na vzdialenom serveri.

Git branche

Práve na to slúžia remote tracking branche. Pri prvotnom klonovaní, ale potom aj pri každej aktualizácii tvojho repozitára, sa všetky branche z toho vzdialeného prekopírujú do tvojho lokálneho. Dá sa im špeciálne meno, ktoré pozostáva z troch časti:

remotes/<názov remote>/<názov branchu>

Presný vzhľad toho mena je konfigurovateľný, ale ak začínaš s východzími nastaveniami, tak remote tracking branch pre master bude vyzerať „remotes/origin/master“. Tá stredná časť je názov remotu, čo je v Gite špeciálny pojem, ktorý pomenováva odkaz na vzdialený repozitár (rovnako ako napríklad branch pomenováva odkaz na commit). K remotom sa ešte vrátime, ale zatiaľ ostávame pri branchoch.

Aktualizujeme remote tracking branche

Ako som spomínal, Git sa musí vysporiadať s tým, že môže u teba lokálne prebiehať vývoj na niekoľkých branchov naraz a v rovnakom čase môže prebiehať vývoj týchto branchov vo vzdialenom repozitári. Hlavná myšlienka riešenia je, že Git u teba vytvorí klony vzdialených branchov a v tvojom lokálnom repozitári sa potom pokúsi zmeny zmergovať. Najprv sa teda všetky zmeny musia preniesť zo vzdialeného repozitára k tebe. Na to v Gite slúži príkaz git fetch.

Príklad použitia je:

> git fetch –verbose

POST git-upload-pack (212 bytes)

remote: Counting objects: 3, done.

remote: Total 3 (delta 0), reused 0 (delta 0)

Unpacking objects: 100% (3/3), done.

From https://bitbucket.org/it-academy-sk/blog_example

8719ce7..b12bf9d  master     -> origin/master

Príkaz aktualizuje všetky tvoje remote tracking branche tak, aby boli v rovnakom stave ako sú vo vzdialenom repozitári. Výpis „master -> origin/master“ v podstate znamená, že branche „master“ vo vzdialenom repozitári je u teba lokálne sledovaný remote tracking branchom „origin/master“.

Git merge

Vykonanie príkazu git fetch nemá žiaden vplyv na tvoje lokálne branche, a teda ani na working copy (ktorá by mala odzrkadľovať stav aktuálne zvoleného branchu). Jeho jedinou úlohou je aktualizovať remote tracking branche, čo v praxi znamená preniesť všetky potrebné objekty (bloby, tree, commity), aby bolo možné pracovať s ich históriou.

Aktualizujeme lokálne branche

Remote tracking branche v repozitári sú aktualizované. Je na čase aktualizovať aj tvoje reálne, pracovné branche. Keďže sa tu bavíme o prenášaní zmien medzi branchami, tak ich aktualizácia nie je nič nové, ale obyčajný merge.

> git merge remotes/origin/master

Updating 8719ce7..b12bf9d

Fast-forward

index.html | 2 +-

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

V tomto momente sme dosiahli to, že lokálny pracovný branch (master) sa aktualizoval na základe stavu jeho dvojníka vo vzdialenom repozitári. Je to činnosť, ktorú ale budeš robiť veľmi často a aj preto v Gite existuje príkaz git pull.

Git pull je vlastne dva v jednom. Je to typický príklad porcelain príkazu, ktorý ma uľahčiť život. Z dokumentácie k tomuto príkazu citujem:

„Incorporates changes from a remote repository into the current branch. In its default mode, git pull is shorthand for git fetch followed by git merge FETCH_HEAD.“

To znamená, že namiesto teba vykoná obe kroky automaticky za sebou (ak si prišiel zo sveta Subversion, tak git pull = svn update). FETCH_HEAD je pekná ukážka toho, ako funguje svet Gitu pod kapotou (teda svet plumbing príkazov). Git fetch aktualizuje branch a následne je potrebné ho mergovať. Príkaz merge ale musí vedieť, ktorý branch sa má mergovať. Preto fetch najprv nastaví referenciu FETCH_HEAD na vrchol remote tracking branchu a následne je zavolaný príkaz git merge s tou referenciou.

Na tomto príklade vidno nie len to, že referencie sú šikovný nástroj, ktorý sa dá používať na riešenie rôznych situácií, ale aj to, že Git príkazy sú nezávislé a komunikujú len v obmedzenej miere (chvalabohu nikoho napríklad nenapadlo do git merge príkaz naprogramovať nejakú kontrolu toho, že pred ním bežal fetch, a teda že má mergovať niečo špeciálne). Slabé väzby v ekosystéme príkazov vždy znamenajú stabilný systém ako celok a to je pre Git bod k dobru.

To som ale trochu odbočil, takže späť k vzdialeným repozitárom. Ukázali sme si, ako dostať zmeny zo vzdialeného repozitára lokálne. A teraz si ukážeme, ako ich tam poslať.

Odosielanie zmien

Opakom príkazu git pull je git push. Jeho účelom je odoslať vaše lokálne zmeny do vzdialeného repozitára. Odosielajú sa samozrejme len commitnuté zmeny. Príklad jednej zmeny (v súbore index.html) môže vyzerať takto:

> git push –verbose

Pushing to https://bitbucket.org/it-academy-sk/blog_example

Counting objects: 3, done.

Writing objects: 100% (3/3), 260 bytes | 0 bytes/s, done.

Total 3 (delta 0), reused 0 (delta 0)

POST git-receive-pack (422 bytes)

To https://bitbucket.org/it-academy-sk/blog_example

b12bf9d..f1e15e0  master -> master

updating local tracking ref ‚refs/remotes/origin/master‘

To, čo sa v praxi udialo je, že sa git odoslal na server commit aj so všetkými objektami (blob, tree), ktoré sú vďaka tomuto commitu nové. V praxi to ale znamená, že Git posiela len inkrement zmien a vďaka hash-based content systému len naozaj to, čo sa zmenilo.

Okrem poslania objektov tiež aktualizoval remote tracking branche, a tiež branche vo vzdialenom repozitári. Čo by sa ale stalo, ak by sa vo vzdialenom repozitári medzi tým, ako sme urobili pull a push objavila ďalšia zmena? Git toto hneď na začiatku pushovania kontroluje a v prípade, že je tam zmena, zahlási, že je nutné najprv urobiť pull a potom skúsiť znova push. V praxi to znamená, že vždy dochádza k mergovaniu – spájaniu zmien – lokálne v niektorom klone repozitára, kde to používateľ má pod kontrolou a vie prípadne vyriešiť konflikty.

Takže už vieš základné kung-fu príkazy pre prácu so vzdialeným repozitárom (pull a push) a tiež ako sa Git vysporiada s tým, že si žije prakticky nezávislý život u teba lokálne, aj na vzdialenom serveri. Nebol by to Git, ak by to nemohlo byť ešte zaujímavejšie. A to napríklad v tom smere, že tvoj lokálny repozitár môže byť naraz pripojený na viacero vzdialených. Doteraz som túto tému obchádzal, ale teraz je čas pozrieť sa na objekty typu remote.

Pracujeme s remote

Objekt remote by sa dal najlepšie prirovnať k objektu referencie. Je to pomenovaný odkaz. V tomto prípade ale na vzdialený repozitár, ktorý je definovaný hlavne URL adresou. Aby bolo jasnejšie, o čom sa bavíme, tu je príklad definície remote v našom testovacom repozitári:

> git remote -v show

origin  https://bitbucket.org/it-academy-sk/blog_example (fetch)

origin  https://bitbucket.org/it-academy-sk/blog_example (push)

Máme v ňom len jeden remote s menom „origin“. To meno mu vybral Git a je to východzie meno, ktoré sa automaticky priradí prvému remote v repozitári (podobne ako master je východzie meno pre hlavnú vetvu kódu). Okrem toho vidíme, že je možné definovať samostatnú adresu pre fetch a pre push.

Git základné príkazy

Ako som spomínal, remote referencií môže byť viac. Príkaz remote má kolekciu subpríkazov, ktoré slúžia na manipuláciu s týmto zoznamom. Sú to napríklad tieto:

  • git remote add – pridanie novej referencie (je nutné zadať URL)
  • git remote show – detailný výpis referencie
  • git remote rename – premenovanie referencie
  • git remote rm – zmazanie referencie
  • git remote update – aktualizácia všetkých remote, v podstate git fetch pustený nad všetkými remote

Ku gitu som sa dostal ako dlhoročný používateľ Subversion, čo je centralizovaný verzionovací systém. Aj preto som na spočiatku na git repozitár pozeral ako na akýsi obraz vzdialeného repozitára, ktorý mám lokálne u seba (a ktorý je s tým vzdialeným repozitárom do smrti prepojený). Vôbec mi nešlo do hlavy, na čo je dobré pripájať tento obraz na ďalšie iné vzdialené repozitáre.

Git je ale v tomto smere iný. Každý repozitár je plnohodnotný a že vznikol z nejakého vzdialeného, len znamená, že ma automaticky definovaný remote, ktorý sa na vzdialený repozitár odkazuje (v skutočnosti po tom, čo si lokálne vyklonuješ repozitár, vieš zmazať remote „origin“ a získaš odpojený lokálny repozitár s akým sme tu pracovali v predchádzajúcich dieloch). To všetko znamená, že repozitár je nezávislá jednotka (aj keď bol vytvorený ako klon), ktorá môže byť jednou jednosmernou linkou prepojená na iný repozitár (tzv. upstream repozitár).

Vďaka tomu je možné vytvárať rôzne architektúry repozitárov ako je napojenie na viac repozitárov alebo reťazenie:

Git viac repozitárov

Git reťazenie repozitárov

To je nepochybne zaujímavé, ale stále je tu otázka, na čo je to vlastne dobré? A odpoveď je, že je to ďalší nástroj, ktorý ti Git dal do ruky a ktorý môžeš použiť vo vhodnej chvíli. Napojenie na viac repozitárov sa môže hodiť pri dlhšej migrácii z jedného vzdialeného repozitára na iný (prechod od jedného poskytovateľa repozitárov k inému). Reťazenie zase môže slúžiť na modelovanie životného cyklu zmien v projekte, kde sa najprv zmeny mergujú do vývojárskeho repozitára a potom do produkčného.

Pri návrhu takýchto architektúr ale treba byť opatrní („With great power comes great responsibility.“). To, čo je v architektúre jedna jednotka (jeden repozitár) je v skutočnosti svet sám o sebe s potenciálne veľkou množinou branchov. Ak sa taký repozitár zaradí do ďalšej komplexnej infraštruktúry, môže z toho (jemne povedané) začať bolieť hlava.

V každom prípade je to ďalšia možnosť, ktorú Git ponúka. Na jej plné pochopenie sa ale teraz z pohľadu 5 km nad morom presunieme omnoho nižšie a pozrieme sa detailnejšie ako fungujú remote tracking branches.

Remote tracking branche

O remote tracking branchoch sme zatiaľ povedali, že ich hlavný účel je odzrkadľovať stav branchov vo vzdialenom repozitári. Takto je vlastne možné mergovať zmeny z alebo do týchto vzdialených branchov lokálne (sú to v podstate proxy branche tých vzdialených). V mnohých ohľadoch sú to branche ako každé iné (séria commitov s referenciou, ktorá označuje začiatok), avšak oproti tým štandardným majú ale aj niekoľko odlišností:

  1. príkaz git branch, ktorý štandardne zobrazuje zoznam branchov, ich nezobrazí. Je nutné zvoliť prepínač -r (pre zobrazenie len remote tracking branche) alebo -a (pre zobrazenie local aj remote tracking)
  2. majú špeciálny názov, ktorý sa štandardne skladá z troch časti. Príklad pre master z remote origin: remotes/origin/master. Ten „origin“ v názve je dôležitý, pretože vytvára tzv. namespace branchov z jedného remote. Teda sa nemôže stať, že ak sa lokálny repozitár pripojí na viac vzdialených repozitárov, pričom v každom je master branch, tak dôjde ku konfliktu mien.
  3. nedá sa do nich štandardne commitovať ani mergovať. Zmeny do nich sa dostávajú len cez git push príkaz. Je ale možné sa do tracking branchu prepnúť, napríklad príkazom: git checkout remotes/origin/master. Ak si to vyskúšaš, tak ťa git upozorní, že žiadne štandardné zmeny nie sú prípustné:
    You are in ‚detached HEAD‘ state. You can look around, make experimental
    changes and commit them, and you can discard any commits you make in this
    state without impacting any branches by performing another checkout.
  4. Existujú, len ak je pre nich definovaný remote. Ak si zmažeš remote „origin“, tak ak si spustíš príkaz git branch -r zistíš, že už žiadne remote tracking branche nemáš. Ak si zase remote pridáš spať a urobíš git pull (za predpokladu, že si nový remote nazval origin), tak sa remote tracking branche objavia späť.

S remote tracking branchami súvisí ešte jedna zaujímavá vec. Ako si si možno všimol, pri git pull a git push nikde nehovoríš, ako presne sa majú aktualizovať remote tracking branche v tvojom repozitári. A tiež nehovoríš, ako sa na základe týchto remote tracking branchov majú aktualizovať tvoje lokálne branche. Inak povedané, ako bude git vedieť, že na základe branchu master v remote origin má najprv aktualizovať tvoj lokálny branch remotes/origin/master a následne tvoj branch master. Odpoveďou je: konfiguračný súbor.

Ak si otvoríš súbor .git/config môžeš tam nájsť takúto časť:

[remote „origin“]
url = https://bitbucket.org/it-academy-sk/blog_example
fetch = +refs/heads/*:refs/remotes/origin/*
[branch „master“]
remote = origin
merge = refs/heads/master

Prvé tri riadky predstavujú definíciu remotu. Ten má v podstate len 3 časti: názov, URL a refspec. Názov a URL sú asi jasné, otázka je, čo je to refspec. Je to definícia mapovania branchov vo vzdialenom repozitári na tvoje remote tracking branche. Vďaka zástupným znakom nie je potrebné toto definovať pre každý branch zvlášť. Git automaticky namapuje branche s rovnakým menom.

Ďalšie tri riadky potom hovoria o tom, ako sa zmeny z remote tracking branchov prenášajú do tvojich lokálnych branchov. Každý takýto lokálny branch má definovanú svoju časť, kde je povedané jeho mapovanie. Takto je v podstate riadený celý tok zmien od branchov vo vzdialenom repozitári až po tvoje lokálne pracovné branche a späť.

Záver a sumarizácia

Ak začínaš na malom projekte, kde robíš sám alebo len v malej skupine a používaš vzdialený Git repozitár, tak jediné príkazy na prácu s remote, ktoré budeš používať sú git clone, git push a git pull. V takom prípade nejaká hlboká znalosť vnútorného fungovania vnútorného prepojenia na vzdialený repozitár nie je taká dôležité. Akonáhle ale zakopneš o väčší projekt s veľkým počtom účastníkov a zložitejším životným cyklom, môžeš očakávať, že zrazu sa stretneš s použitým Gitu, ktoré už nemusí byť tak jednoduché. Aj z toho dôvodu poznať to, ako funguje komunikácia Gitu na diaľku do väčšej hĺbky. Taká znalosť v niektorých prípadoch dokáže zabrániť problémom, prípadne ich pomôže vyriešiť.

Tip: Mrknite na náš online kurz Git.

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 a marketér, ktorý mudruje vo vlastnej vzdelávacej spoločnosti IT Academy. Workoholik so 114 certifikáciami a 12 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. Referencie: IBM, Panasonic, Ministerstvo obrany SR, ČSOB, Generali, Tatra banka, Európska komisia, SPP, Pixel Federation, ESET.