Jak na git - díl 4.

Git, Windows, Linux

Čtvrté pokračování Git tutoriálu. Konečně bude ukázáno, jak kromě nahrávání commitů na server také nové commity stahovat, využívat větve a v neposlední řadě, mnohými obávané konflikty. Ty jsou bohužel často nevyhnutelné i při naprosto běžném používání gitu.

Jak na git - díl 4.

Tento článek patří do seriálu Jak na git. Ostatní články seriálu:


Oficiální dokumentace k dnešním příkazům: git pull, git fetch, git branch, git merge

Stahování commitů ze serveru

Ve všech předchozích článcích byly popsány možnosti nahrávání commitů, sledování změn, vrácení apod. Pokud ale více lidí pracuje s jedním repozitářem, je potřeba si stáhnout commity ze serveru.

Stejně jako na dveřích je push z jedné strany a pull z druhé, tak i na gitu je nečekaně příkaz git pull. Pokud commity na sebe navazují, git provede fast-forward merge. Pokud na sebe ale nenavazují, vytvoří se merge commit při kterém mohou vzniknout konflikty. O tom níže.

# Stáhne nové commity z hlavního repozitáře, nejčastěji origin
git pull
# Repozitář lze specifikovat
git pull repository_name

# Lze upravit merge commit zprávu před vytvořením commitu
git pull --edit
# Nevytvoří commit, před sloučením lze provést úpravy/inspekci kódu
git pull --no-commit
# I když lze provést fast-forward merge, provede merge commit
git pull --no-ff

git pull

Postupnými kroky s git fetch a git merge

Příkaz git pull je ve skutečnosti zkratka pro 2 příkazy git fetch a git merge. Příkaz git fetch stáhne commity ze serveru, ale ponechá je nesloučené se speciálním ukazatelem FETCH HEAD. Poté lze provést samotné sloučení pomocí git merge.

# Stáhne všechny větve z hlavního repozitáře
git fetch
# Repozitář i větev lze specifikovat
git fetch origin master

# Smaže všechny reference, které na vzdáleném repozitáři neexistují
git fetch --prune

Po stáhnutí ať už jedné nebo více větví, git vypíše, o které větve se jedná. Změny poté lze vysledovat stejně jako v předchozích případech pomocí git log a git diff. Fetchnuté změny lze nyní sloučit pomocí git merge, který je popsán také níže při slučování větví.

# Vypsání všech commitů mezi lokální a vzdálenou master větví
git log --oneline master..origin/master
# git diff bude také fungovat

# Sloučení do aktuální větve vzdálenou větev origin/master
git merge origin/master

Více než 1 větev

Mít v gitu jen jednu větev by nebylo moc praktické a byla by to nuda. Proto je možnost vytvářet větví více, navzájem je mezi sebou slučovat, znova větvit apod. Nejčastější větve jsou master (hlavní, produkce) a dev (vývojová). Pro další nové funkcionality se mohou vytvářet nové větve, které se poté slučují do master či dev větve. To již je pak na domluvě každého týmu. Hlavní výhodou je možnost přepínání mezi větvemi, kdy se všechny soubory vždy změní tak, jak by měly v dané větvi vypadat.

Příkaz git branch vytvoří novou větev z aktuálního commitu, a další commit se přidá do té větve, ve které se git nachází. Větev není nic zázračného, jen množství pointerů, podobně jako HEAD. Commit vždy musí mít svého předchůdce, nelze proto vytvořit commit z ničeho a je důležité se rozhodnout, z jakého commitu a jaké větve se nová větev vytvoří.

# Vypíše seznam všech větví (--all přidá vzdálené)
git branch --all
# Vypíše seznam větví včetně názvu vzdálené, kterou sleduje
git branch -vv

# Vytvoří novou větev s názvem new_feature, neprovede ale checkout
git branch new_feature
# Větev vytvoří a přepne se do ní (provede checkout)
git checkout -b new_feature

# Vymaže větev pouze, pokud pokud je mergnutá do jiné větve 
git branch -d new_feature
# Přejmenuje aktuální větev 
git branch -m awesome_feature

# Přepnutí do existující větve
git checkout master

git branch

Nahrání větve, nebo commitů do vzdáleného repozitáře

Pokud je vytvořena nová větev, soubory upraveny a vytvořen commit, je potřeba nahrát novou větev na server. Je potřeba také zajistit, aby lokální větev sledovala tu vzdálenou. Pokud lokální větev new_feature sleduje větev origin/new_feature, lze provádět git push a git pull bez dalších parametrů.

# Vytvoří na vzdáleném repozitáři větev se stejným názvem a nastaví
# sledování s aktuální větví, 2 možné zápisy
git push -u origin
git push --set-upstream origin

# Nahraje všechny větve
git push --all

Stáhnutí nové větve ze vzdáleného repozitáře

Pokud spolupracovník vytvoří větev a nahraje ji do repozitáře, někdy je potřeba si ji stáhnout. K tomu slouží opět zmíněný příkaz git fetch následovaných příkazem checkout.

# Stáhne info ze serveru, včetně nových větví a vypíše jejich názvy
git fetch
# Přepne do nové větve a automaticky nastaví sledování
git checkout new_branch_from_coworker

Slučování a řešení konfliktů

Slučování se provádí pomocí git merge příkazu. Dříve byl využit pro sloučení commitů po fetchi, používá se také pro sloučení dříve rozdělených větví. Je to opět jednoduché, stačí se přepnout do větve, do které je potřeba sloučit jinou větev a poté použít git merge.

# Sloučí větev new_feature do aktuální větve (např master)
git merge new_feature

# Následující přepínače jsou stejné jako u git pull
# Lze upravit merge commit zprávu před vytvořením commitu
git merge new_feature --edit
# Nevytvoří commit, před sloučením lze provést úpravy/inspekci kódu
git merge new_feature --no-commit
# I když lze provést fast-forward merge, provede merge commit
git merge --no-ff

Fast-forward vs. 3-way merge

Pokud větev, která je slučování přímo navazuje na výslednou, provede se fast-forward merge. Při tomto kroku git pouze přesune pointery na nová místa. Pokud ale v průběhu byly soubory změněny v obou větvích, je potřeba vytvořit merge commit, někdy označován jako 3-way merge. Většinou není potřeba řešit, kterou možnost je potřeba použít, je tu ale ta možnost specifikovat použitou metodu.

Fast-forward vs. 3-way merge

Konflikty při slučování

Pokud je více lidmi provedena změna stejného úseku kódu, git nedokáže provést spojení jednotlivých verzí a je potřeba zásah programátora. V takové případě git upozorní, že v souboru je konflikt, označní tento úsek a ponechá k ruční opravě.

git pull
# Zobrazí
# Auto-merging functions.php
# CONFLICT (content): Merge conflict in functions.php
# Automatic merge failed; fix conflicts and then commit the result


git status
# both modified: functions.php

Kód bude vypadat přibližně takto, když 2 lidé změnili stejný řádek.

<<<<<<< HEAD
function myAwesomeFunction($params){
=======
function myAwesomeFunction($format, $params){
>>>>>>> e28fbd0a3b987a0d700a79d1d19ae73bf39eff2d

Pokud je potřeba opravdu ruční opravy, nejjednodušší je otevřít editor, kódem se prokousat a opravit jej. Poté soubory přidat pomocí git add a provést commit.

Někdy ale může nastat konflikt i v souborech, které přímo upraveny nebyly, nebo kdysi dávno. V takovém případě lze použít příkazu git checkout a vybrat naši verzi nebo naopak jejich verzi. Při tomto řešení git ignoruje změny v souborech z jedné, nebo druhé strany a vyřeší konflikt ponecháním pouze jedné verze.

# Zahodí změny v "jeho" verzi a ponechá pouze "mou"
git checkout functions.php --ours
# Naopak pro ponechání pouze "jeho"
git checkout functions.php --theirs

Dnešní díl patřil opět mezi ty delší, kdyby něco nebylo jasné, doptejte se v komentářích. Již se pomalu blížíme do finále s gitem.

K tomuto článku již není možné přidávat další komentáře