Toto je pokračovanie seriálu o verzionovacom (verzovacom) systéme Git, ktorého 1. diel si môžeš pozrieť tu (Seriál Git – Začíname s Gitom – 1. diel). V ňom som vysvetlil to, čo to Git je a aké má výhody ho používať. Tiež som ukázal jednoduchý príklad pridania súboru do repozitára. Takto vyzbrojení základnými poznatkami sa môžeme pustiť do širšej práce so súbormi. Git je nepostrádateľný nástroj pre vývojárov, ktorý umožňuje efektívnu správu verzií kódu. Práca so súbormi v lokálnom prostredí je jednou z kľúčových oblastí, kde Git exceluje. V tomto článku sa pozrieme na rôzne aspekty práce so súbormi lokálne v Gite, vrátane pridávania, úprav, odstraňovania súborov a riešenia konfliktov.
Začiatok s Git
Ak ešte nemáte Git nainštalovaný, môžete si ho stiahnuť a nainštalovať z oficiálnej stránky. Po inštalácii si nastavte svoje meno a e-mail, ktoré budú používané pri commitovaní zmien.
Nastavenie mena a e-mailu:
git config –global user.name „Vaše Meno“
git config –global user.email „vas.email@example.com“
Vytvorenie nového Git repozitára:
cd moj-projekt
git init
Pridávanie a commitovanie súborov
Pridávanie súborov do Git repozitára je jednoduché. Stačí vytvoriť alebo upraviť súbory a potom ich pridať do staging area pomocou príkazu git add
. Následne môžete zmeny commitovať do histórie projektu.
Príklady:
git add hello.py # Pridanie súboru do staging area
git commit -m „Pridanie hello.py s jednoduchým programom“ # Commitovanie zmien
Keď upravíte existujúce súbory, je dôležité pravidelne commitovať zmeny, aby ste mali prehľadnú históriu projektu.
Príklady:
echo „print(‚Hello, Git!‘)“ >> hello.py # Úprava existujúceho súboru
git add hello.py # Pridanie zmien do staging area
git commit -m „Uprava hello.py s novým výstupom“ # Commitovanie zmien
Git príkazy alebo než sa začnú sypať ďalšie príkazy
Ešte predtým než sa ponoríme do sveta príkazov je dobré si o nich niečo povedať:
- porcelán vs. potrubia – nie, neodbočil som náhle do vodoinštalačnej oblasti. Toto je naozaj stále o Gite (a humore jeho tvorcov). Git rozlišuje 2 typy príkazov: nízkoúrovňové príkazy – potrubia alias plumbing a vysokoúrovňové porcelánové príkazy alias porcelain (budem používať anglický výraz, pretože sa s prekladom akosi neviem stotožniť). Plumbing príkazy sú podrobnejšie príkazy, ktoré vedia detailnejšie manipulovať s repozitárom. Naproti tomu porcelain príkazy sú jednoduchšie na použitie, pretože čiastočne zakrývajú komplexnosť Gitu. Príkaz commit, ktorý sme použili v predchádzajúcom dieli, je porcelain príkaz podobne ako väčšina, s ktorými budeme teraz pracovať. Je veľmi pravdepodobné, že ak s Gitom začínaš, tak sa k plumbing príkazom spočiatku ani nedostaneš. A prečo takéto názvy? Je to prirovnanie k tomu, čo obsahuje každé správne WC. Porcelánová záchodová misa, na ktorú sa dá sadnúť a potrubia, ktoré sa starajú o samotný odsun a prísun toho, čo treba. Tá misa je akýmsi nadstavcom k potrubiam, aby sa dali ľahšie použiť.
- meno a email – ak si Git nainštaluješ a začneš ho používať, je pravdepodobné, že pri niektorom z prvých príkazov od teba bude pýtať meno a email. Nehanbi sa ich poskytnúť. Tvoje meno bude zobrazené pri commitoch a email tiež nájde svoje uplatnenie (napr. zostavovací server Jenkins ho dokáže použiť a pomocou neho poslať email autorovi commitu, ktorý spustil zostavenie)
- dlhé vs. krátke parametre – niektoré parametre príkazov existujú v dvoch verziách. Krátkej a dlhej. Napríklad parameter so správou pre príkaz commit sa dá napísať takto:
>git commit -m „Text správy …“alebo>git commit –message „Text správy …“Všimni si jednej pomlčky pri krátkom výraze a dvoch pri dlhom. - help – každý príkaz ma svoju man stránku, ktorá sa dá zobraziť takto:> git help <názov príkazu>
Git je komplikovaný nástroj s mnohými možnosťami a zároveň je dobre dokumentovaný. Bola by škoda nevyužiť jeho plný potenciál.
Rekapitulácia základov Git
Predtým, než pôjdeme ďalej, je dobré si zhrnúť základné poznatky o tom, čo to vlastne znamená mať na disku repozitár:
- repozitár je v podstate obyčajný adresár, ktorý má v sebe .git priečinok, v ktorom má Git uložené všetky potrebné údaje.
- všetko ostatné v danom priečinku (vrátane podpriečinkov) je považované za working copy.
- zmeny, ktoré sa majú uložiť v rámci commitu do repozitára musia byť najprv vložené do staged changes – to je miesta, kde sa tieto zmeny akumulujú až do najbližšieho commitu. Zmeny zo staged changes sú zaradené do najbližšieho commitu.
Prezeranie histórie
Teraz sa pozrieme trochu bližšie na prezeranie histórie. Ešte predtým si ale v našom pokusnom repozitáry vytvoríme ďalší commit, aby sa bolo na čo pozerať. Takže ako prvé je potrebné zmodifikovať súbor index.html – doplníme znak „!“ na konci vety, takže po novom bude obsah „Hello world!“. Následne túto zmenu pridáme do staged changes:
> git add index.html
a commitneme
> git commit -m „Oprava textu“
[master 113ab5b] Oprava textu
1 file changed, 1 insertion(+), 1 deletion(-)
A teraz sa už môžeme vrhnúť na prezeranie histórie. Začneme príkazom git log:
> git log
commit 113ab5bc04fdd2d61d8c9ce3c5cb7080786a1dc2
Author: Miroslav Reiter <miroslav.reiter@it-academy.sk>
Date: Tue Mar 15 21:46:47 2021 +0100
Oprava textu
commit e4c0627bb8819340331ca6a42bde1bb0cd24bb19
Author: Miroslav Reiter <miroslav.reiter@it-academy.sk>
Date: Mon Mar 14 21:40:50 2021 +0100
Začiatok práce na stránke
To, na čo sa pozeráš, sú zoradené (najnovší hore) commity v tvojom master branchy. Každý commit má definovaného svojho autora, dátum a čas, obsah správy a svoj SHA1 hash (ten budeš mať určite iný ako ja, keďže je v ňom zahrnutý dátum a čas vytvorenia, ako aj údaje o autorovi commitu). To usporiadane ale nemusí ísť nutne časovo za sebou, pretože pre Git je podstatné poradie commitov ako ich jednotlivé časy. Ak sa Git pri commitoch neriadi časom tak čím? Každý commit (okrem toho úplne prvého, ktorý vytvoril repozitár – tzv. root commit) ma definovaného minimálne jedného rodiča. To je iný commit, na ktorý sa jeho potomok odkazuje. Commity tak vytvárajú niečo, čo sa nazýva acyklický smerový graf.
Graf je matematická štruktúra, kde sú jednotlivé body spojené priamkami. Smerová znamená, že na každej priame je definovaný smer (teda priamka je vlastne šípka). A acyklický znamená, že v grafe neexistujú cykly. Inak, ak by si vyštartoval z ľubovoľného bodu a pohyboval sa po jednotlivých priamkach medzi bodmi, tak sa nemôže stať, že sa vrátiš do toho istého bodu. Dôležité je poznamenať, že v Gite nie je smerovanie grafu v smere ako pribúdajú commity, ale opačné – teda proti toku práce a času (commit má odkaz na predchádzajúci commit – tzv. rodičovský commit).
Repozitár začína root commitom a následne sa pridávajú ďalšie. K vetveniu môže dôjsť, keď sa vytvorí branch, ktorý sme tu už skôr spomínali. Branch je vlastne osobitná vetva kódu, ktorá v čase vytvorenia je kópiu pôvodného branchu, ale inak sa vyvíja nezávisle od pôvodnej vetvy. Branchovanie je technika, ktorá ma obrovské uplatnenie v mnohých oblastiach vývoja softvéru. Od vytvárania dočasných branchov pre izolovanie práce na jednej úlohe/chybe, cez branchovanie na oddelenie viac a menej stabilných vetví, až po branche pre jednotlivé zverejnené verzie aplikácie.
A to som len škrtol povrch možností, čo sa dá s týmto nástrojom robiť. Plné popísanie rôznych vzorov vetvenia zdrojových kódov by znamenalo niekoľko ďalších článkov. Verzionovací systém je totiž jeden z tých nástrojov, ktoré sa majú prispôsobovať životnému cyklu aplikácie (a nie naopak). A práve preto je dôležitá jeho veľká pružnosť, ktorá ti umožní si vymodelovať štruktúru, aby sedela na aktuálny projekt. K tomu, ako vytvárať branche a ďalej s nimi pracovať, sa ešte vrátime v ďalších článkoch.
Zatiaľ späť k príkazu git log. Je to príkaz, ktorý ma obrovské množstvo prepínačov, ktoré dokážu filtrovať alebo formátovať výstup (ak si zadáš git help log, tak získaš prehľad, o čom hovorím). Na podrobný popis všetkého nie je priestor, ale ako príklad si môžeme ukázať ešte tento variant:
> git log –oneline –graph
* 113ab5b Oprava textu
* e4c0627 Zaciatok prace na stranke
Je to rovnaký zoznam ako v predchádzajúcom prípade, len pekne zhustený so skrátenými hasmi. Z commitu sa v takom prípade zobrazuje prvý riadok commit message. Preto je pri nich odporúčaný formát, kde prvý riadok popisuje stručne zmenu (niečo ako predmet pri emaile), nasleduje prázdny riadok a potom text popisujúci commit. Ja viem. Poviete si, ako to mám všetko zapísať do príkazového riadku pri commite? Ak správu s prepínačom nezadáte, tak si Git automaticky otvorí editor (ktorý má nastavený ako východzí, čo sa dá zmeniť v nastaveniach) a nechá vám správu zapísať. Ak to Gitu vyslovene neprikážete, tak vás nepustí vytvoriť commit bez správy (čo síce môže vyzerať tyransky, ale v skutočnosti ťa učí disciplíne, ktorá je v projektoch na dlhé trate na nezaplatenie). Tie hviezdičky naľavo vo výpise git log nie sú na skrášlenie, ale predstavujú graf commitov. Akurát že zatiaľ je to taký veľmi jednoduchý graf s dvoma bodmi. Prídu aj zložitejšie.
Ako ďalší príkaz archeologického výskumu (teda rýpania sa v histórii) je git show. Ten slúži na zobrazenie informácie o jednom vybranom commite. Takže príklad:
> git show 113ab5b
commit 113ab5bc04fdd2d61d8c9ce3c5cb7080786a1dc2
Author: Miroslav Reiter <miroslav.reiter@it-academy.sk>
Date: Tue Mar 15 21:46:47 2021 +0100
Oprava textu
diff –git a/index.html b/index.html
index 70c379b..6769dd6 100644
— a/index.html
+++ b/index.html
@@ -1 +1 @@
-Hello world
\ No newline at end of file
+Hello world!
\ No newline at end of file
Čo nám git show ukázal? Začína to základným výpisom údajov, ktorý sme videli už predtým. Potom nasleduje takzvaný diff. Hneď prvý riadok v podstate popisuje to, o čo sa akože Git snaží. Spustí diff príkaz (presnejšie diff príkaz s výstupom v Unified formáte) nad dvoma súbormi. Tie súbory sú ale virtuálne, lebo v skutočnosti sú to len dve verzie toho istého súboru. Výsledok je ale rovnaký ako keby sme diffovali dva rôzne súbory. Tak ako git log aj git show má viacero prepínačov, ktorými sa dá ovládať jeho výstup.
Z príkazov pre históriu stojí za to ešte spomenúť git shortlog a git blame. git shortlog slúži na získanie výstupu, ktorý sa podobá changelogu pri zverejnení novej verzie. Ak ho spustíme nad našim priečinkom, dostaneme niečo takéto:
> git shortlog
Miroslav Reiter(2):
Začiatok práce na stránke
Oprava textu
Pri viacerých autoroch commitu by sme získali takýto zoznam pre každého z nich.
git blame je príkaz z trochu iného súdku ako tieto doteraz. Jeho hlavnou úlohou je nájsť, ktorý commit (a teda aj informáciu o jeho autorovi a dátume/čase) ako posledný zmenil daný riadok v súbore. Poďme teda na to:
> git blame index.html
113ab5bc (Miroslav Reiter 2021-03-15 21:46:47 +0100 1) Hello world!
Skúmaný súbor má len jeden riadok a preto ani výstup git blame viac neobsahuje. Ak by ich bolo viac, tak by bola takáto informácia vypísaná pre každý jeden. Na teraz stačilo hrabania sa v minulosti a poďme sa radšej pozrieť, ako sa formuje budúcnosť.
Ignorovanie súborov a zmien
Hlavný účel Git repozitára je sledovanie a verzionovanie zmien súborov. Existujú ale prípady, kedy sa vo working copy môžu nachádzať súbory, ktoré si Git nemá všímať. Sú to napríklad súbory s nastaveniami pre IDE, ktoré sú privátne pre každého vývojára. Alebo generované súbory, ktoré vznikajú pri kompilácii projektu, prípadne inom automatizovanom projekte. To sú súbory, ktoré dávať do repozitára by bolo zbytočné, zároveň by nás ale Git štandardne upozorňoval, že sa nachádzajú vo working copy (pri výpise git status) a on nevie čo s nimi. Preto existuje niekoľko spôsobov ako Gitu povedať, aby si tieto súbory nevšímal. Ten najčastejšie používaný je pomocou .gitignore súboru. Je to súbor, ktorý sa zvykne umiestňovať do koreňového priečinka repozitára a obsahuje zoznam súborov, ktoré má git ignorovať. Príklad obsahu takéhoto súboru je:
- *.class
- # Mobile Tools for Java (J2ME)
- .mtj.tmp/
- # Package Files #
- *.jar
- *.war
- *.ear
Toto je začiatok súboru .gitignore odporúčaného pre Java projekty. Na GitHube existuje projekt, kde viete nájsť pripravený .gitignore súbor pre veľa rôznych programovacích jazykov. Čo teda .gitignore môže obsahovať:
- cestu k súboru, ktorý sa má ignorovať (podporované sú wildcards alias zástupné znaky ako je napríklad * pre ľubovoľný počet znakov)
- cestu k priečinku, pričom sa v nej dá použiť reťazec **, ktorý ak:
- je pred názvom priečinku (**/foo) znamená, že je platný pre ľubovoľný podpriečinok foo kdekoľvek vo working copy
- je ako posledný v ceste (foo/**), znamená všetky súbory v danom priečinku
- je niekde uprostred (bar/**/foo), znamená všetky priečinky, ktorých cesta začína na bar a končí na foo (vrátane bar/foo)
- komentár začínajú znakom #
- negáciu podmienky začínajúcu na !
Osobne som sa nestretol zatiaľ ani s jedným projektom, ktorý by nejakým spôsobom .gitignore súbor nepoužíval. Aj vzhľadom na túto funkčnosť Gitu si môžeme povedať, že git rozpoznáva 3 druhy súborov:
- sledovaný (tracked) – súbor bol pomocou git add zaradený do repozitára
- ignorovaný (ignored) – súbor bol vylúčený zo spravovania pomocou Gitu
- nesledovaný (untracked) – ostatné súbory, ktoré nespadajú do prvý dvoch kategórií
Tak fajn, vieme ignorovať celé súbory, ale čo s prípadom, kedy chcem, aby bol súbor v repozitáry a ignorovaná bola len nejaká konkrétna zmena? A kedy vôbec niečo také budeme potrebovať? Ako príklad môže slúžiť konfiguračný súbor, ktorý je potrebné, aby v repozitáry bol (používa sa pri nasadení aplikácie, ale aby v tomto súbore mal každý vývojár nejakú hodnotu inú ako ostatní (techník ako sa s tým vysporiadať, ak máte po ruke nástroje automatizácie, je veľa, ak sa ale súbor často nemení a tím vývojárov nie je veľký, tak najjednoduchšie je, čo si teraz popíšeme). Postup je teda taký, že do repozitára sa umiestni správna verzia súboru, ktorá obsahuje nastavenia pre nasadzovanie, následne si vývojár tento súbor zmodifikuje a už len Gitu povie, aby ignoroval zmeny v ňom. Poďme si to teda skúsiť. Najprv v našom súbore urobím zmenu, tak že na koniec riadku doplním nový znak:
Hello world!?
Súbor je modifikovaný a git ho tak aj vidí:
> git status –short
M index.html
Použili sme skrátenú verziu výpisu – to M na začiatku je skratka od modified. Následne pomocou príkazu git update-index –assume-unchanged Gitu poviem, aby zmenu ignoroval:
> git update-index –assume-unchanged index.html
Ďalšie spustenie git status nám potvrdí, že Git už žiadnu zmenu nevidí:
> git status –short
Skrátená verzia git status neukázala nič, čo znamená, že Git nevidí žiadne zmeny vo working copy. Príkaz, ktorý sme v skutočnosti použili, je vlastne git update-index, ktorý má omnoho širšie použitie ako len ignorovanie zmien, ale pre tento moment ostaneme len pri tomto. index.html súbor je teraz v stave, že akékoľvek ďalšie zmeny v ňom sú ignorované. Čo ale spraviť, ak predsa len chcem, aby sa nejaká zmena mohla commitnuť a dostať tak do repozitára. Na to slúži prepínač –no-assume-unchanged, ktorým tento súbor vrátim medzi štandardné sledované súbory:
> git update-index –no-assume-inchanged index.html
A znova kontrola stavu:
> git status –short
M index.html
Odstraňovanie súborov
Ak potrebujete odstrániť súbor z projektu, Git ponúka príkaz git rm
. Tento príkaz odstráni súbor z pracovného adresára aj zo staging area.
Príklady:
git rm hello.py # Odstránenie súboru
git commit -m „Odstránenie hello.py“ # Commitovanie zmien
Zobrazenie stavu projektu
Príkaz git status
je užitočný na zobrazenie aktuálneho stavu pracovného adresára a staging area. Ukazuje, ktoré súbory boli zmenené, pridané alebo odstránené.
Príklady:
git status # Zobrazenie stavu projektu
Zobrazenie rozdielov
Príkaz git diff
umožňuje zobraziť rozdiely medzi pracovným adresárom, staging area a posledným commitom. Tento príkaz je užitočný na prehľad zmien pred commitovaním.
Príklady:
git diff # Zobrazenie rozdielov v pracovnom adresári
git diff –cached # Zobrazenie rozdielov v staging area
Riešenie konfliktov
Konflikty môžu nastať, keď sa zmeny v rôznych vetvách dotýkajú rovnakých častí súborov. Git označí tieto konflikty a umožní vám ich manuálne vyriešiť.
Kroky pri riešení konfliktov:
- Otvorte konfliktný súbor v textovom editore
- Manuálne upravte konfliktné časti podľa potreby
- Odstráňte značky konfliktov (
<<<<<<
,======
,>>>>>>
) - Potvrďte vyriešenie konfliktov príkazmi
git add
agit commit
Príklady:
git add konfliktný_súbor
git commit -m „Riešenie konfliktu pri zlúčení“
Práca s Git ignore
Súbor .gitignore
umožňuje špecifikovať súbory a adresáre, ktoré Git nemá sledovať. Tento súbor je užitočný na vylúčenie dočasných súborov, kompilovaných súborov a iných nepotrebných položiek z repozitára.
Príklad .gitignore
:
# Ignorovanie kompilovaných súborov
*.o
*.a
*.out
# Ignorovanie dočasných súborov
*.tmp
*.swp
# Ignorovanie adresára build
build/
Pridanie .gitignore
do repozitára:
git add .gitignore # Pridanie .gitignore do staging area
git commit -m „Pridanie .gitignore na ignorovanie kompilovaných súborov“ # Commitovanie zmien
Záver a sumarizácia
Lokálna práca so súbormi v Gite je základom efektívnej správy verzií. Používanie príkazov na pridávanie, úpravu, odstránenie a commitovanie súborov vám umožní udržiavať prehľadnú históriu vášho projektu a efektívne riešiť konflikty. Práca s .gitignore
súborom vám pomôže udržiavať váš repozitár čistý a bez zbytočných súborov. Hlavným účelom repozitárového systému je sledovanie verzií súborov. Aj preto sme sa hneď v tomto prvom praktickom diely o Gite venovali práci s históriou a súbormi. Dôležité je uvedomiť si, že všetky zmeny ktoré sú zaznamenávané, sa dejú len na základe commitov a že tie majú svoju pevnú postupnosť. Na základe týchto nemenných pravidiel je potom možné cestovať hore dole v čase a získať akúkoľvek verziu súborov. V ďalšom diely budeme pokračovať v práci so súbormi, a tiež si povieme, prečo je pre Git dôležitejší obsah než forma.
Objavte online kurzy na Git a GitHub
Odporúčania na prácu s GITom
Pri práci s Gitom dbajte na pravidelné commitovanie zmien, používanie vetiev na izolovanie rôznych častí projektu a sledovanie stavu projektu pomocou git status
. Tieto postupy vám pomôžu udržiavať váš projekt organizovaný a efektívny.
Prehľad publikovaných článkov
- Seriál Online kurz Git – Začíname s Gitom – 1. diel
- Seriál Online kurz Git – Lokálna Práca so Súbormi – 2. diel
- Seriál Online kurz Git – V Hlbinách Súborového Systému – 3. diel
- Seriál Online kurz Git – Paralelné svety a Git branch – 4. diel
- Seriál Online kurz Git – Mergovanie s Konfliktom, Tagy a Skrytie Zmien – 5. diel
- Seriál Online kurz Git – Vzdialené repozitáre, GitHub, Bitbucket – 6. diel
- Seriál Online kurz Git – Clean, Reset, Rebase, Revert nástroje do každého počasia – 7. diel
- Seriál Online kurz Git – Najčastejšie problémy, faily a fuckupy – 8. diel