Git je systém správy verzí – slouží k udržování informací, jak byly v průběhu času měněny zdrojové soubory ve stromě adresářů, který odpovídá určitému softwarovému projektu. Některé systémy správy verzí umožňují efektivně sledovat změny pouze jednoho souboru, ale získání stavu projektu v minulosti je velmi pomalé; git je schopen velmi rychle nastavit obsah celého pracovního stromu na stav po provedení libovolného commitu.

Repozitář je informační struktura, ve které jsou uloženy veškeré informace o historii projektu.

Jednou z výhod gitu je, že vytváří plnohodnotné kopie repozitářů. O tom, který repozitář určitého projektu je hlavní, tak rozhoduje pouze dohoda účastníků konkrétního projektu. Velmi často existuje na serveru jeden hlavní repozitář projektu, do kterého má právo zapisovat pouze správce projektu nebo skupina vybraných osob, a repozitáře pro jednotlivé účastníky projektu, do kterých mají právo zapisovat jejich vlastníci (správu repozitářů zajišťuje např. GitHub). Uživatel pak pro svou práci používá jeden nebo více lokálních repozitářů, jejichž součástí je i pracovní strom.

Slovníček pojmů

editovat

Slovo commit se v Gitu používá ve třech významech. Prvním je uložení sady změn v jednom nebo více souborech do verzovacího systému příkazem git commit. Je potřeba dát pozor na to, že pokud se nepoužije parametr -a, git při commitu nezaznamená okamžitý stav projektu (tj. seznam a obsahy všech souborů zařazených do projektu), ale obsah staging area (tj. soubory, na které se od posledního commitu provedlo git add, každý v tom stavu, kdy se toto přidání provedlo). Druhým významem slova commit je právě tento stav projektu, který je jednoznačně identifikován pomocí SHA1 haše, což je 40ciferné šestnáctkové číslo. Uživatel může pro určení commitu použít i několik prvních znaků haše (aby jednoznačně identifikovaly jeden commit). Třetím významem slova commit je pouze seznam změn nebo úprav (patch) od předchozího commitu. Git aplikuje změny v souborech tak efektivně, že si často ani neuvědomujeme, za kolika příkazy se skrývá aplikace změn mezi dvěma stavy projektu na nějaký jiný stav – nejčistším případem je příkaz git cherry-pick, který aplikuje změny ze zadaného commitu na aktuální stav projektu, ale i základem funkce příkazů git rebase, git merge a git pull je aplikování změn provedených jinde na určitý stav projektu.

Větev (anglicky branch) je uživatelem pojmenovaná sada commitů vyčleněných pro implementaci určité vlastnosti nebo opravy; v repozitáři bývá jedna nebo více „hlavních“ větví, do kterých se shromažďují příspěvky od jednotlivých účastníků projektu. „Nejhlavnější hlavní“ větev se jmenuje master a obvykle reprezentuje nejnovější stabilní větev projektu. Další „hlavní“ větve mohou být vývojové, pro starší releasy projektu a podobně. Základem úspěšné týmové práce v gitu je neměnit soubory v hlavních větvích, ani nehromadit své změny v jedné starodávné větvi, ale vytvářet novou větev pro každý zásah do projektu, který tvoří logický celek.

Větev v Gitu není nic jiného pojmenovaný odkaz na (nejnovější) commit (hlava větve); další commity ve větvi jsou určeny předchůdci hlavy. Při provedení nového commitu do větve se hlava větve jednoduše nastaví na tento přidaný commit, přičemž commit, na který hlava původně ukazovala, se stane předchůdcem nového commmitu. V Gitu nemá smysl hledat nejstarší commit větve; v podstatě je to nejstarší commit celého projektu. Pro dvě větve je však možné určit jejich posledního (nejnovějšího) společného předka, tedy místo, kde se tyto větve rozdělily. Protože si git pamatuje, ve kterém okamžiku (po kterém commitu) bylo provedeno rozvětvení, není dobré se pokoušet používat staré větve pro nové úpravy; pokud je třeba navázat na starší úpravy, je vhodné provést přerovnání větve (git rebase) nebo převzít požadované starší úpravy příkazem git cherry-pick.

Fork je rozdělení repozitáře pro různé release nebo uživatele; typicky se používá pro vytvoření vzdáleného repozitáře pro určitého uživatele, do kterého má tento uživatel právo zápisu, což mu dovoluje přispívat do projektu; fork není funkcí samotného gitu, ale nějaké jeho nadstavby (např. GitHub), proto se obvykle provádí na WWW stránce provozovatele vzdálených repozitářů; přebíráním větví z různých forků se kumulují výsledky paralelní práce více vývojářů.

Clone vytvoří (příkazem git clone URL) lokální kopii vzdáleného repozitáře. S lokální kopií můžete provádět jakékoli operace; zda budete moci své úpravy přenést do vzdáleného repozitáře, záleží na nastavení práv ve vzdáleném repozitáři. Obvykle do společného vzdáleného repozitáře nemůžete zapisovat, ale můžete zapisovat do svého forku tohoto repozitáře.

Pull request

editovat

Pull request je žádost o přetažení větve (tj. zkopírování úprav provedených v této větvi) do jiného forku (obvykle z mého soukromého do společného). Pull request je záležitost některé z nadstaveb systému git. Vedoucí projektu určuje, za jakých okolností se pull requestu vyhoví (Code review, úspěšné testy, schválení vedoucím), a jak má začlenění větve do projektu vypadat (jeden nebo více commitů, prosté sloučení nebo sloučení po přeskládání s viditelným nebo bez viditelného slučovacího commitu).

První kroky

editovat

Instalace

editovat

Abyste mohli pracovat s gitem, je potřeba nainstalovat příslušný software, který je určen pro práci v

  • příkazovém řádku - obvykle plná škála příkazů, za předpokladu jejich rutinní znalosti je práce velmi efektivní
  • nebo grafickém rozhraní - většinou pokrývá omezený rozsah nejčastějších příkazů využívaných svátečnějšími uživateli

Volba konkrétního klienta značně závisí na osobních preferencích, vodítkem může být např. zda se jedná o svobodný software, pro jaký operační systém je určen nebo zkušenosti jiných uživatelů.

Instalační pokyny pro oficiální verzi vám pomohou vyzkoušet si, zda vyhovuje vašim potřebám.

Načtení obsahu vzdáleného repozitáře

editovat

První načtení dat ze vzdáleného repozitáře provedeme pomocí příkazů:

git clone -v adresa_repozitáře
cd adresář

adresa_repozitáře je např. git://git.kernel.org/pub/scm/git/git.git; ve webové službě Bitbucket firmy Atlassian se adresa repozitáře zjistí kliknutím na ikonu Clone. Příkaz git clone vytvoří v aktuálním adresáři podadresář, jehož jméno stanovil tvůrce repozitáře, a v něm lokální repozitář, který obsahuje pracovní strom, a v podadresáři .git veškeré informace o historii projektu, dostupných větvích i jméno aktuální větve; nové verze programu git vypisují jméno zmíněného podadresáře vždy, starší verze pouze pokud je použit parametr -v. Pro další práci s repozitářem pomocí příkazů git je nutné se do tohoto adresáře (případně nějakého jeho podadresáře) přepnout. Pro pozdější načtení změn ve vzdáleném repozitáři slouží příkaz

git pull

Získání vlastní kopie vzdáleného repozitáře

editovat

Na levém obrázku je znázorněn výsledek po klonování společného repozitáře projektu. Příkaz git clone vytvoří lokální kopii repozitáře. Kromě kopie databáze obsahuje i pracovní strom (working tree), v němž git zpřístupňuje soubory a adresáře zvolené verze projektu a oblast změn (staging area), která slouží pro přípravu commitu. Do společného vzdáleného repozitáře však běžný uživatel zpravidla nemůže zapisovat.

Uspořádání, které umožňuje uživateli do projektu přispívat, je znázorněno na pravém obrázku. Uživatel si musí nejdříve vytvořit (fork) vlastní kopii repozitáře na vzdáleném serveru (zpravidla nazývanou origin) a klonovat tuto kopii. Pro přenesení změn do společného repozitáře (blessed) se používají pull requesty.

 
Příkaz git clone adresa vytvoří na lokálním počítači kopii zadaného vzdáleného repozitáře; vytvoří i prázdnou stage area a vytvoří pracovní strom, který obsahuje nejnovější stav větve master.
 
Příkaz git clone adresa vytvoří na lokálním počítači kopii zadaného vzdáleného repozitáře; vytvoří i prázdnou stage area a vytvoří pracovní strom, který obsahuje nejnovější stav větve master.

Vytvoření lokálního repozitáře

editovat

Chceme-li použít git pro svůj vlastní projekt, pro který zatím neexistuje vzdálený repozitář, vytvoříme lokální repozitář z adresářového stromu na lokálním disku následujícím postupem:

mkdir adresář
cd adresář
git init

První příkaz vytvoří prázdný adresář, do kterého se druhým příkazem přepneme. Poslední příkaz vytvoří v aktuálním adresáři podstrom se jménem .git, který obsahuje vše nezbytné pro repozitář. Repozitář je ale zatím prázdný; teprve commitem se do něj přidají soubory.

Konfigurace

editovat

GIT má 3 úrovně konfigurace:

  • systémovou (pro všechny uživatele na počítači); příkaz git config je nutné použít s parametrem --system; konfigurace je v souboru /etc/gitconfig
  • uživatelskou (pro všechny projekty daného uživatele); příkaz git config je nutné použít s parametrem --global; konfigurace je v souboru ~/.gitconfig (ve Windows C:\Users\$USER\.gitconfig); tento soubor je vhodný pro nastavení uživatelského jména a e-mailové adresy
  • lokální (pro jeden projekt); konfigurace je v souboru .git/config; v tomto souboru jsou mimo jiné definovány vztahy mezi jmény a adresami vzdálených repozitářů a mezi lokálními a vzdálenými větvemi

Jméno a e-mailová adresa uživatele je typickou ukázkou konfiguračních informací na uživatelské úrovni, proto se provádí s parametrem --global:

git config --global user.name "John Doe"
git config --global user.email johndoe@example.com

– zapíše do ~/.gitconfig

git config --list

– vypíše všechna nastavení

git config user.name

– vypíše zadané nastavení; lze používat ve skriptech.

Pokud chcete mít pod kontrolou, co provede příkaz git pull, pokud zjistí, že došlo k odvětvení vzdálené i lokální větve, zadejte jeden z následujících příkazů; pro zamezení jakéhokoli slučování:

git config --global pull.ff only

pro provedení přeskládání:

git config --global pull.rebase true

pro provedení sloučení:

git config --global pull.rebase false

Pokud jste nuceni používat starou verzi Gitu (nižší než 2.0) je vhodné v globální konfiguraci nastavit, že příkaz git push má přenášet do vzdáleného repozitáře pouze změny v aktuální větvi:

git config --global push.default current

Soubor .gitignore

editovat

Soubor .gitignore obsahuje seznam jmen souborů, které nemá git při své činnosti brát v úvahu. Každý řádek souboru je tvořen specifikací jmen souborů, která může obsahovat i relativní cesty a žolíkové znaky, nebo komentář začínající znakem křížek (#). V souboru je vhodné uvést jména záložních souborů, které vytvářejí editory a další programy pro vytváření a úpravu zdrojových souborů, stejně jako jména souborů, které jsou produkovány ze zdrojových souborů. Například *.o soubory vytvářené překladači, jména souborů vytvářených programy Lex a Yacc; při generování HTML souborů z XML, může být v .gitignore uvedeno

*.htm
*.html

V projektu nebo adresáři, v němž jsou HTML soubory zdrojovými soubory, je v .gitignore samozřejmě neuvádíme.

Podobný význam mají soubory .git/info/exclude (pro jeden projekt) a $HOME/.config/git/ignore (pro všechny projekty daného uživatele na daném stroji).

Nápověda

editovat
git
git help

– vypíše témata nápovědy

git help téma
git téma --help
man git-téma

– vypíše nápovědu k zadanému tématu

Lokální repozitář

editovat

Lokální repozitář vytvořený příkazy git clone nebo git init umožňuje prohlížení i úpravy souborů. Můžeme si představovat, že se skládá ze 3 oblastí:

  • pracovní adresář nebo strom (working directory, working tree) – skutečný adresář nebo strom adresářů na disku
  • oblast připravených změn (staging area, index) – jedná se o seznam souborů (včetně obsahu v okamžiku, kdy se na soubor provedl příkaz git add), které budou příkazem git commit uloženy do repozitáře; soubory se do ní přidávají příkazem git add, odebírají git reset
  • repozitář (repository) – vlastní repozitář se mění pomocí git commit, do vzdáleného repozitáře se změny propagují pomocí git push

Soubory v pracovním stromě, které nejsou součástí staging area, jsou untracked, ostatní jsou buď tracked nebo modified; pomocí git add cesta/soubor se převede untracked nebo modified soubor do seznamu souborů, které se budou příkazem git commit ukládat do repozitáře (tzv. staged soubory), opakem git add soubor je git reset -- soubor.

Informace o repozitářích

editovat

K nejčastěji používaným příkazům patří příkaz

git status

který vypíše jméno aktuální větve, její stav podle informací uložených v lokálním repozitáři, soubory ve staging area, změněné (modified) soubory a přidané (untracked) soubory.

Lokální repozitář vytvořený příkazem git clone je spojen se vzdáleným repozitářem, který lze zjistit příkazem

git remote -v

Nástrahy Gitu

editovat

Git jakožto nástroj pro verzování souborů by měl přispívat k lepší ochraně dat, která jsou pomocí něho udržována. Tuto úlohu plní velmi dobře, ale protože se jedná o podstatně komplikovanější nástroj než je kopírování, přemísťování a mazání souborů v adresářové struktuře, mohou se začátečníkovi přihodit některé nepříjemnosti. Díky vlastnostem Gitu jsou tyto nepříjemnosti omezeny na dvě oblasti:

  • uživatel přijde o vlastní práci
  • uživatel zkomplikuje práci týmu

Likvidace práce jiných členů týmu je při rozumném nastavení prostředí téměř vyloučena.

Aby se uživatel nepřipravil o vlastní práci, stačí trocha obezřetnosti a zapamatování si, které akce ji mohou způsobit. Hlavním zdrojem potíží může být fakt, že pracovní strom slouží jak k úpravě souborů uživatelem, tak k zobrazení stavu projektu. Každý příkaz git checkout může změnit obsah pracovního stromu a mohl by vést k přepsání změn, které uživatel provedl. Proti takto jednoduchému přepsání souborů je však Git zabezpečen, a pokud soubor s určitým obsahem nepatří do aktuální verze uložené v repozitáři a byl by přepsán jiným obsahem, vypíše varování a akci neprovede. Existuje však několik příkazů, které vás mohou o nearchivované verze souborů připravit:

  • git checkout -- cesta – před provedením tohoto příkazu je nutné si být jistý, že zadané soubory nebudeme potřebovat, případně, že je máme bezpečně zkopírované (např. mimo pracovní stromu projektu)
  • git reset --hard – tento příkaz provádět pouze pokud nemáme žádné untracked soubory a pokud máme poznamenané číslo aktuálního commitu
  • git branch -D větev – tento příkaz raději odložit na dobu, kdy budeme mít jistotu, že mazaná větev neobsahuje nic užitečného (pokud byla větev zkopírována jinam pomocí git cherry-pick, zkontrolujte, že byly zkopírovány všechny potřebné úpravy)
  • na git push -f nebo --force raději zapomeňte; bezpečnější je uvedení + před jménem větve – při některých postupech (git commit --amend větve, která už byla pushnuta do vzdáleného repozitáře, používání git rebase bez git merge) se bez některé z uvedených variant neobejdeme; rozhodně by se neměl používat pro společný repozitář, nejvýše jako náprava chyby; tuto nápravu byl měl provést uživatel s výbornou znalostí Gitu

Jakmile je jednou proveden commit souboru, uplatní se základní vlastnost Gitu, že do databáze se pouze připisuje. Proto je možné obnovit i smazaný commit. Git však po určité době nereferencované objekty maže, proto je nutné co nejrychleji vytvořit například větev odkazující se na smazaný commit, který budeme ještě potřebovat.

Zkomplikování práce týmu nevhodným zásahem do verzovacího systému lze zabránit technicky (zálohování) organizačně (neposkytováním práva zápisu do společného repozitáře) nebo dobrým proškolením uživatelů. Pokud by došlo k poškození hlavního repozitáře, je možné využít distribuovanosti Gitu a repozitář obnovit z jiné kopie.

Postup při změnách souborů

editovat
 

Při změnách souborů je vhodné dodržovat následující postup:

  1. Aktualizace informací v lokálním repozitáři ze vzdálených repozitářů
  2. Vytvoření a úpravy zdrojových souborů pomocí obvyklých nástrojů
  3. Vytvoření větve pro začlenění úprav s případným nastavením výchozí verze
  4. Vytvoření seznamu změn
  5. Uložení změn do lokálního repozitáře příkazem git commit
  6. Přenesení změn do vzdáleného repozitáře příkazem git push

Aktualizace informací v lokálním repozitáři

editovat

První z příkazů

git fetch --all -v
git status

načte informace o změnách ze všech vzdálených repozitářů, ale neprovede žádné změny v lokálním stromě. Následující příkaz vypíše, jak se změny ve vzdáleném repozitáři slučují se stavem lokálního repozitáře a pracovního stromu.

Pokud nemáme v pracovním stromě žádné změněné soubory, lze příkazem

git pull --ff-only

aktualizovat obsah lokálního stromu – příkaz lze použít i bez parametru --ff-only. Pokud však byla rozštěpena lokální a vzdálená větev (například tím, že někdo zahodil jeden nebo více commitů pomocí git reset následovaného forced push), pull bude provádět slučování (merge) obou větví se všemi případnými problémy (konflikty atd.). Použití --ff-only nám dá možnost rozmyslet si, jestli chceme opravdu provést slučování; pokud by se například jednalo o cizí větev, ve které nemáme co mergovat, bude lepší přesunout lokální hlavu na pozici vzdálené hlavy příkazem

git branch -f jméno_větve plné_jméno_vzdálené_větve

Aktualizace informací provádíme, abychom svoje změny aplikovali na nejnovější verzi souborů v projektu. Pokud naopak potřebujeme udělat úpravu starší verze, použijeme příkaz git checkout identifikace_commitu.

Vytvoření a úpravy zdrojových souborů

editovat

Soubory z projektu jsou přístupné v pracovním stromě, proto je možné je jednoduše prohlížet, upravovat a vytvářet pomocí editorů a dalších nástrojů. Mnoho použití příkazu git mění soubory v pracovním stromě, proto před jejich použitím změněné soubory uložte provedením commitu nebo zkopírujte na bezpečné místo (např. mimo pracovní strom).

Práce s větvemi

editovat

Větev je sada spolu souvisejících commitů. Commity jsou spolu svázány pomocí svých předchůdců (úplně první commit nemá žádného, běžný commit má jednoho, slučovací commit má dva nebo více předchůdců). Git neumožňuje, aby větev obsahovala vzájemně nesouvisející commity, takže větev je jednoduše odkaz na její nejnovější commit. Vzhledem k tomuto pojetí větve lze těžko stanovit, jaký je druhý konec větve (nejstarší commit), lze však určit, který commit je poslední (nejnovější) společný předek dvou větví.

Pro zjištění existujících větví lze použít příkaz

git branch

– který vypíše větve v lokálním repozitáři, před jménem aktuální je hvězdička. Pokud doplníme parametr -r, vypíše větve ve vzdálených repozitářích spojených s projektem; s parametrem -a vypíše větve v lokálním i ve vzdálených repozitářích.

Přepínání větví

editovat

Pro přepnutí na jinou větev (změnu aktuální větve) slouží příkaz:

git checkout větev

Přepnutí větve způsobí změnu souborů v pracovním stromě. Pokud se soubory v pracovním stromě pracujeme, mohou nastat různé problémy, ale git se v tomto případě chová slušně a bezpečně:

  • Pokud jsou v pracovním stromě změněné necommitnuté soubory, které by byly přepsány, git vypíše varování a změnu větve neprovede:
$ git checkout treti-radek-a-druhy-soubor
error: Your local changes to the following files would be overwritten by checkout:
        druhy_soubor.txt
Please, commit your changes or stash them before you can switch branches.
Aborting
  • Stejně je tomu v případě, že by došlo k přepsání dříve untracked souboru souborem, který je v nové verzi přítomen:
$ git checkout treti-radek-a-druhy-soubor
error: The following untracked working tree files would be overwritten by checkout:
        druhy_soubor.txt
Please move or remove them before you can switch branches.
Aborting
  • Problémy mohou také nastat, pokud jsou zdrojové soubory otevřeny v editoru nebo nějakém jiném programu. Některé programy vůbec změnu souboru nezaregistrují, a uživatel tak může editovat špatnou verzi souboru. Jiné nechají uživateli vybrat, jestli chce nahrát novou verzi nebo pokračovat v práci s původní. Jiné programy mohou dokonce zabránit gitu, aby soubory změnil.

V příkazu git checkout lze místo jména větve lze použít libovolnou identifikaci commitu. Pokud používáme značky (anglicky tags), lze přepnout na zadanou značku:

git checkout tags/jméno_tagu

Vytvoření větve

editovat

Zadáním jména větve v příkazu git branch vytvoříme novou větev zadaného jména. Větev se oddělí v místě commitu zadaného druhým parametrem, případně v aktuálním místě aktuální větve (tj. HEAD):

git branch jméno_nové_větve [odkaz_na_commit]

Obvykle se chceme na nově vytvořenou větev přepnout (nastavit ji jako aktuální):

git checkout jméno_větve

Oba tyto příkazy lze sloučit do jednoho:

git checkout -b jméno_nové_větve [odkaz_na_commit]

Můžeme také vytvořit větev v lokálním repozitáři duplikováním větve ze vzdáleného repozitáře:

git checkout -b jméno_větve origin/jméno_větve

Pokud chceme, aby větev měla stejné jméno jako ve vzdáleném repozitáři a aby se sledovaly změny, které se ve větvi na vzdáleném repozitáři objeví, lze použít příkaz:

git checkout --track origin/větev

Zrušení větve

editovat

Následující příkaz zruší větev:

git branch -d jméno_větve

Pokud větev nebyla namergovaná do jiné větve, příkaz vypíše varování a větev nezruší. Pokud chceme větev opravdu zrušit, použijeme místo -d velké -D.

Přesunutí konce větve

editovat

Konec větve se při commitu přesouvá automaticky na nejnovější commit ve větvi. Lze však i vynutit přeskočení na libovolný jiný commit:

git branch -f jméno_větve [odkaz_na_commit]

Pokud není zadán odkaz_na_commit, přeskočí se na aktuální HEAD.

Historie větve

editovat

To, že větev je tvořena jen odkazem na její poslední commit, není tak docela pravda. Git si obvykle pamatuje i historii větve (tj. které commity byly postupně tím posledním commitem ve větvi). Tuto historii lze vypsat příkazem

git reflog jméno_větve

Vytvoření seznamu změn

editovat

Přidání aktuálního stavu souboru do seznamu změn:

git add cesta/soubor [ cesta2/soubor2 ... ]

Pokud byl soubor znovu změněn, stačí příkaz git add zopakovat (pro každý soubor v seznamu změn se pamatuje stav při posledním přidání). Pokud byl soubor zařazen do seznamu změn omylem, lze to napravit příkazem

git reset -- cesta/soubor [ cesta2/soubor2 ... ]

Přejmenování a rušení souborů v repozitáři

editovat

Seznam změn ovlivňují také příkazy pro přejmenování a zrušení souboru.

Přejmenování souboru (lze použít i pro přemístění do jiného adresáře):

git mv cesta1/soubor cesta2/jméno

Odstranění souboru z projektu:

git rm cesta/soubor

Zrušení úprav souboru

editovat

Pokud jste nějaký soubor změnili, nezanesli tuto změnu ani do indexu ani neprovedli commit, a úpravu souboru chcete zrušit, lze použít následující postup:

cp cesta/soubor cesta/soubor_zaloha
git checkout -- cesta/soubor

Druhý příkaz přepíše zadaný soubor verzí z aktuální větve lokálního repozitáře. Přitom nevypisuje žádná varování a původní soubor už nejde obnovit, proto je prvním příkazem naznačeno, že je vhodné si vytvořit jeho zálohu, pro případ, že byste přeci jenom chtěli upravenou verzi použít.

Další příkaz zruší všechny změny v souborech v pracovní oblasti:

git checkout -- .

Uložení změn do lokálního repozitáře

editovat

Příkaz

git commit -m "Popis commitu"

uloží aktuální seznam změn do aktuální větve lokálního repozitáře.

Pokud nebyly přidány žádné soubory, lze spojit vytvoření seznamu změn a uložení změn do lokálního repozitáře použitím parametru -a:

git commit -a -m "Popis commitu"

V tomto případě se před commitem do seznamu změn automaticky přidají všechny modified soubory.

Modifikace commitu

editovat

Pokud jste po provedení commitu zjistili, že jste zapomněli provést nějakou „malou“ úpravu, lze právě provedený commit dodatečně upravit. Nejdříve pomocí git add zadejte jména všech dodatečně upravených souborů, a pak příkazem git commit s parametrem --amend upravte poslední commit:

git add cesta/soubor [ cesta2/soubor2 ... ]
git commit --amend

Pokud jste již stihli na upravený commit provést git push, bude nutné použít git push se znakem plus před jménem větve, protože se nebude jednat o pouhé přidávání do vzdáleného repozitáře.

Přenesení změn do vzdáleného repozitáře

editovat

Příkaz

git push

přenese změny z lokálního repozitáře do vzdáleného repozitáře. Pokud je pracovní strom propojen s více vzdálenými repozitáři, přenesou se změny do repozitáře origin. Pokud se mají přenést do jiného repozitáře, je nutné zadat jeho jméno použité v příkazu git remote add:

git push jméno

Pokud větev ve vzdáleném repozitáři neexistuje, příkaz vypíše varování a je třeba nastavit upstream, přesně podle textu, který je součástí varování:

git push --set-upstream origin jméno_větve

Slučování změn

editovat

Pokud na jednom projektu pracuje více osob nebo pokud vytvoříme více větví, neobejdeme se bez slučování větví. Následující příkaz zamerguje zadanou větev do aktuální větve; proto je potřeba se předem přepnout do větve, do které se má mergovat; mohou nastat potíže, pokud existují necommitované změny, proto je třeba mergování provádět s čistým pracovním stromem.

git merge větev

Pokud byl soubor změněn pouze v jedné ze slučovaných větví, použije se změněná verze. Pokud byl soubor změněn v obou větvích, pokusí se git aplikovat změny, ke kterým v souboru došlo v zadané větvi, na soubor v aktuální větvi (jako příkaz patch). Pokud patch selže, je ohlášen konflikt:

$ git merge pro-posledni-radek
Auto-merging pokus.txt
CONFLICT (content): Merge conflict in pokus.txt
Automatic merge failed; fix conflicts and then commit the result.

do souboru jsou umístěny obě verze vyznačené řetězci <<<<<<<, ======= a >>>>>>>:

Nejaky text.
<<<<<<< HEAD
Druhy radek.
Toto je posledni radek.
=======
Zmeneny druhy radek.
>>>>>>> jina

a sloučení je potřeba provést ručně. Pro ruční sloučení je možné použít běžný textový editor nebo speciální nástroj. Seznam dostupných nástrojů pro slučování souborů vypíše příkaz

git mergetool --tool-help

Zvolený nástroj nastavíme příkazem

git config --global merge.tool gvimdiff

Vlastní slučování pak lze provést příkazem

git mergetool cesta/soubor

Gvimdiff zobrazuje nahoře ve třech sloupcích soubor z výchozí větve, společného předchůdce a soubor ze slučované větve, dole pak text s oběma variantami, jak je ukázáno výše.

Gvimdiff2 zobrazuje pouze tři sloupce, přičemž v prostředním je text s oběma variantami; gvimdiff3 zobrazuje pouze jedno okno s oběma variantami textu.

Složitější akce

editovat

Přemístění commitů do jiné větve

editovat

Často se stává, že člověk začne upravovat soubory v nesprávné větvi. Pokud ještě neprovedl commit, stačí pomocí git checkout -b jméno_větve [odkaz_na_commit] vytvořit novou větev (větve jsou složeny z commitů, takže před provedením commitu jsme ještě žádnou větev nezměnili); pomocí odkazu_na_commit lze odbočit větev i z jiného místa (větve, commitu), než do kterého je právě přepnuto. Pokud byla větev už vytvořena, ale neobsahuje žádný commit, lze použít git checkout jméno_větve.

Pokud už byl proveden jeden nebo více commitů, je rozhodně vhodné vytvořit novou větev a úpravy přesunout do ní. Nejdříve provedeme commit všech rozpracovaných souborů, pak řekneme, že nová větev končí právě zde (vytvoříme novou větev) a nakonec příkazem git reset --hard vyhodíme nechtěné commity z původní větve (novou větev jsme vytvořili, ale přepnuti jsme stále v původní, teprve git checkout nás přepne do nové větve):

git add cesta/soubor [ cesta2/soubor2 ... ]
git commit -m "Uložení rozpracovaných souborů"
git branch nová_větev
git reset --hard odkaz_na_commit
git checkout nová_větev

Rebase (do češtiny překládané jako přeskládání) je alternativou ke slučování pomocí merge. Pokud byla větev, ve které se provedly změny, oddělena z jiné větve před delší dobou, takže v původní větvi došlo ke změnám, můžeme chtít místo viditelného sloučení větví aplikovat změny ze své větve na hlavní větev. Příkazem

git rebase hlavní_větev [ moje_větev ]

se zkopírují se všechny úpravy z větve moje_větev od místa, kde se oddělila od větve hlavní_větev, na konec hlavní větve. Původní commity sice zůstanou zachovány, ale jejich kopie se objeví ještě jednou na konci hlavní větve a moje_větev bude po provedení operace ukazovat na kopie commitů. Není-li uvedena moje_větev, bude přeskládána (překopírována) aktuální větev. Použití uvedeného příkazu git rebase se jmény dvou větví je ekvivalentní dvojici příkazů

git checkout moje_větev
git rebase hlavní_větev

Pokud byl některý ze souborů upraven v obou větvích takovým způsobem, že algoritmus slučování není schopen určit, jak má vypadat výsledek, je ohlášen konflikt, který musíme vyřešit, jak je popsáno v části Slučování změn. Git také informuje o možnosti příslušný patch přeskočit pomocí git rebase --skip, nebo přeskládání odvolat příkazem git rebase --abort. Pokud se rozhodneme pro řešení konfliktů, je nutné po jejich odstranění všechny upravené soubory zadat do seznamu změn pomocí příkazu git add cesta/soubor jako před provedením commitu a zadat příkaz

git rebase --continue

V ideálním případě se rebase dokončí; může se však stát, že rebase narazí na konflikt v některém z dalších commitů v přeskládávané větvi. Pak se celý kolotoč opakuje, ale nebojte se, jste o další krůček blíž k cíli. Pokud však jsou obě větve dlouhé a obsahují mnoho konfliktů, může být lepší místo rebase zamergovat hlavní větev do naší, zejména, když máme představu, jak má vypadat konečný výsledek a nestojíme o postupné úpravy souborů. Jinou možností je sloučit commity ve svojí větvi do jednoho, a tento souhrnný commit pomocí rebase zkopírovat na konec větve, do které má být začleněn.

Protože se provedením rebase přesune lokální hlava rebasované větve na konec její nově vytvořené kopie, dojde k rozštěpení lokální a vzdálené větve. Pro jejich spojení je nutné provést forced push, který lze vynutit uvedením znaku + před jménem větve:

git push origin +rebasovaná_větev

kde origin je jméno repozitáře a znak plus před jménem rebasované větve zajistí, že se provede push pouze pro zadanou větev. Vhodnější je vytvořit si ještě před rebase kopii rebasované větve, která nebude ve vzdáleném repozitáři a rebase provést s ní:

git checkout -b kopie_větve
git rebase hlavní_větev

Výběr změn bez historie

editovat

Zkopírovat několik posledních commitů ve větvi na jiné místo lze příkazem

git rebase --onto cíl za_čím konec

který zkopíruje všechny commity ve větvi konec po commitu za_čím za commit cíl. Pokud je třeba kopírovat jen vybrané commity, lze použít příkaz git cherry-pick jak je popsáno dále.

Každý commit obsahuje odkaz na předchozí commit (případně na dva, pokud vznikl pomocí merge). Pokud je potřeba začlenit do určité větve změny z jiné větve bez odkazů na jejich historii, lze použít následující příkaz:

git cherry-pick odkaz_na_commit [ ... ]

Příkaz git cherry-pick dovádí do extrému přístup z git rebase: na aktuální větev aplikuje pouze změny provedené ve vybraných commitech. Přitom vytváří ukončené commity; pokud chcete mít změny pouze v pracovním stromě, je možné po cherry-pick provést git reset základní_commit, který zruší všechny commity po zadaném základním_commitu, ale ponechá modifikace souborů v pracovním stromě.

Používání více repozitářů pro jeden projekt

editovat

Lokální repozitář může být propojen s více vzdálenými repozitáři téhož projektu. Další vzdálené repozitáře lze přidávat příkazem

git remote add jméno adresa_repozitáře

Zadané jméno slouží k pozdějším odkazům na vzdáleným repozitář v příkazech git fetch, git pull a git push. Repozitář, který byl použit v příkazu git clone, má jméno origin; společnému repozitáři se obvykle dává jméno blessed.

Sloučení commitů

editovat

Při vývoji softwaru se často stává, že sada úprav tvořících jeden logický celek je rozložena do několika commitů. Mohou to být commity provedené na konci každého dne, commity způsobené postupnými opravami, úpravy provedené jako reakce na review, apod. Pokud jejich autor nechce, aby byl vidět bolestný proces vzniku jeho díla, může sloučit commity do jednoho nebo do menšího počtu, které tvoří určité logické celky. Preferovaným postupem je použití interaktivního režimu příkazu git rebase:

git rebase -i starý_commit

kde starý_commit je commit, po němž se mají commity slučovat (v tomto případě lze s výhodou použít operátor vlnka pro n-tého předchůdce: např. pro sloučení posledních 5 commitů použít HEAD~5). Po zadání příkazu se otevře editor, ve kterém je seznam commitů od nejnovějšího po nejstarší vypadající takto:

pick 69248ac Popis prvního commitu
pick d70002a Popis druhého commitu
...
pick 49f2d10 Popis posledního commitu

# Rebase 1949c51..49f2d10 onto 1949c51 (7 command(s))
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
# d, drop = remove commit
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

Řádky začínající znakem křížek napovídají, že interaktivní rebase má mnoho dalších možností; pro sloučení všech vypsaných commitů do jednoho je třeba slovo pick v prvním řádku ponechat a všechna ostatní nahradit slovem squash (nebo písmenem s) a soubor uložit; otevře se další soubor, do kterého je třeba napsat popis souhrnného commitu. Výsledkem jsou všechny změny v jediném commitu. Stejně jako při běžném rebase dojde k rozvětvení lokální a vzdálené hlavy větve, takže je potřeba provést push s plusem před jménem větve:

git push origin +rebasovaná_větev

Anulování commitů

editovat

Pokud chcete zrušit (odvolat) jeden nebo více commitů, závisí postup na tom, kam až se nežádoucí commity dostaly, a jestli po nich byly provedeny další (žádoucí) commity. Ve svém soukromém repozitáři (ať lokálním nebo vzdáleném) můžete klidně zrušit několik posledních commitů zkrácením větve příkazy:

git checkout větev
git reset --hard poslední_správný_commit

následovaném

git push origin +větev

Pokud se ale nejedná o poslední commity a především pokud byly nežádoucí commity již zpropagovány do společného repozitáře, je třeba použít jemnější způsob jejich zrušení:

git revert seznam_commitů

Tento příkaz zjistí změny provedené v uvedených commitech, v aktuálních souborech aplikuje úpravy, kterými se tyto změny vezmou zpět. Pokud dojde ke konfliktům, je třeba je vyřešit obdobným způsobem jako v případě příkazu rebase. Pokud se vše podařilo, vznikne v lokálním repozitáři nový commit obsahující soubory, které byly změněny. Tento commit je nutné přenést do vašeho vzdáleného repozitáře a začlenit do společného repozitáře.

Podrobnější informace

editovat

Hledání v historii

editovat

Historii aktuální větve lze zobrazit příkazem

git log

Příkaz vypisuje jednotlivé commity s jejich haši. U každého commitu je autor, datum změny a informace zadaná v příkazu git commit za parametrem -m. Slučování (merge) jsou zvlášť označeny. S parametrem --name-only nebo --name-status se bude vypisovat, jaké soubory se měnily v jednotlivých commitech.

Pro nalezení změn určitého souboru lze použít příkaz:

git log -- cesta/soubor

Pro barevné zobrazení historie celého projektu, včetně ASCII znázornění větví a vypsání jmen větví a tagů, lze použít grafický příkaz gitk & nebo

git log --all --decorate --graph --oneline --name-status

Poslední parametr použijeme, pokud se mají vypisovat i jména souborů.

Porovnávání commitů

editovat

Pro porovnávání commitů slouží příkaz git diff. Bez parametrů zobrazí rozdíly mezi working tree a staging area nebo aktuálním commitem (pokud je staging area prázdná). S parametrem --staged rozdíly mezi staging area a aktuálním commitem. Pokud je zadán jeden commit, zobrazují se rozdíly mezi modifikovanými soubory (ve working tree i staging area) a zadaným commitem:

git diff
git diff --staged
git diff HEAD

– výstup třetího příkazu je sloučením rozdílů z prvních dvou příkazů.

Při uvedení --staged a jednoho commitu se vypíšou rozdíly mezi zadaným commitem a staging area:

git diff --staged číslo_commitu_z_pull_requestu
git diff číslo_commitu_z_pull_requestu

– druhý příkaz vypíše rozdíly mezi modifikovanými soubory (ve working tree i staging area) a zadaným commitem.

Volby, které začínají znakem mínus a git diff je nezná, předává přímo příkazu diff, takže pro porovnání s ignorováním změn v počtu mezer lze použít příkaz

git diff -b -w

Pokud se zadají dva commity, bude výsledkem jejich porovnání:

git diff -b master SMSC-1079-5.4

– vypíše rozdíl mezi větví master a SMSC-1079-5.4; -b se předává příkazu diff, aby ignoroval rozdíly v mezerách

Diff obsahuje řádky tvaru index 358c9f8..8740525 100644. Zadáním dvou hexadecimálních číslic oddělených dvěma tečkami se vypíše diff daného souboru v příslušném commitu:

git diff 358c9f8..8740525

Další možností, jak omezit diff vybrané soubory je doplnit příkaz dvěma pomlčkami následovanými jmény souborů i cestami:

git diff popis_commitu -- cesta/soubor [ cesta2/soubor2 ... ]

Pokud nás zajímá diff pouze z jednoho commitu, lze na unixových systémech použít:

git diff 9883d27d0b302a2e0f4f04c2d072f52c186ad18a{^,}

– vypíše diff commitu 9883d27d0b302a2e0f4f04c2d072f52c186ad18a (shell expanduje xxx{^,} na xxx^ xxx; xxx^ je předchůdce xxx); stačí zadat i několik prvních znaků SHA haše.

Pro zjištění, jaké soubory byly změněny v posledním commitu v aktuální větvi, lze použít příkaz:

git diff --name-only HEAD^

Kompletní informace o commitu

editovat

Více informací o commitu v příkazech git log a git show lze získat použitím volby --pretty=fuller. S touto volbou se zobrazí autor, datum a čas nejen původního commitu, ale i jeho začlenění do větve po git rebase:

commit edb9342985b443443eb0266d8418c3e62851d1d0
Author:     Jan Vyvojar <Jan.Vyvojar@domena.top>
AuthorDate: Fri Apr 21 10:20:48 2017 +0200
Commit:     Karel Reviewer <Karel.Reviewer@domena.top>
CommitDate: Wed May 10 09:22:16 2017 +0200

    Fix of the bug #123456

Synchronizace lokální a vzdálené větve

editovat

Při použití parametru --track v příkazu git checkout nebo parametru --set-upstream v příkazu git push se pro lokální větev aktivuje sledování (trackování) vzdálené větve. Při zapnutém trackování vás příkaz git status upozorní, že lokální větev je pozadu (behind) za vzdálenou větví, před (ahead) vzdálenou větví (což znamená, že jste provedli jeden nebo více commitů a neprovedli git push), že se větve rozešly (Your branch and 'jméno_větve' have diverged), nebo že jsou synchronizované (Your branch is up-to-date with 'jméno_větve'). Git si informaci o trackování větve uchovává v souboru .git/config v sekci [branch "jméno_větve"] v podobě parametrů remote = vzdálený_repozitář a merge = odkaz_na_větev, přičemž odkaz na větev je tvaru refs/heads/jméno_větve.

Pokud nejsou synchronizované větve, které by synchronizované být měly, lze synchronizaci aktuální větve zapnout příkazem

git branch --set-upstream-to=repozitář/jméno_větve

Pokud naopak chcete z nějakého důvodu synchronizaci vypnout, použijte příkaz

git branch --unset-upstream

Problémy s přístupem ke vzdálenému repozitáři

editovat

Pokud se příkazy git fetch, git pull, git push nebo git clone nemohou spojit se vzdáleným repozitářem, může být příčina ve špatně zkonfigurovaném ssh. V takovém případě zjistíme adresu vzdáleného repozitáře příkazem

git remote get-url origin

Mělo by se vypsat URL repozitáře ve tvaru ssh://git@server.domain.top:7999/~myname/project.git. Informace použijeme k otestování spojení příkazem ssh:

ssh -T -p 7999 git@server.domain.top who

Příkaz by měl vypsat uživatelské jméno. Problém bývá často ve špatných právech adresáře $HOME/.ssh nebo souborů v tomto adresáři:

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0666 for '/homes/myname/.ssh/id_rsa' are too open.
It is recommended that your private key files are NOT accessible by others.
This private key will be ignored.
bad permissions: ignore key: /homes/myname/.ssh/id_rsa
Permission denied (publickey).

Adresář a většina souborů v něm mohou mít oprávnění k zapisu pouze pro vlastníka, soubory id_rsa, id_dsa,… musí mít oprávnění ke čtení i zápisu pouze pro vlastníka. V tom případě pomůže změna práv příkazem:

chmod 600 /homes/myname/.ssh/id_rsa

Dalším důvodem může být, že se ssh klient není schopen dohodnout s ssh serverem na šifrovacím algoritmu nebo algoritmu pro podpis. V tom případě získáme další informace doplněním parametru -v (pokud nestačí -vv, atd.):

ssh -T -p 7999 git@server.domain.top -v who

Pokud se ve výstupu objeví řádek

debug1: send_pubkey_test: no mutual signature algorithm

je nutné vygenerovat klíč klientského stroje pro takový algoritmus elektronického podpisu, který server podporuje, např.:

ssh-keygen -t ed25519

a obsah $HOME/.ssh/id_ed25519.pub přidat do povolených klíčů na vzdáleném serveru.

Literatura

editovat

Externí odkazy

editovat
  •   Encyklopedický článek Git ve Wikipedii