ObecnéNávody

Git

Git - poznámky ze studia

1. Nezapomínat, že i git umí dokončování tabulátorem. Týká se příkazů, parametrů i seznamů větví, značek apod. Zkoušet všude :-)

1.  Základní úlohy

1.1  Vznik repozitáře

Inicializace pracovního adresáře pro sledování gitem:

 $ git init

Získání kopie existujícího repozitáře:

 $ git clone cesta (url) k repozitáři

V kořenovém adresáři vznikne podadresáč .git, který obssahuje vlastní repozitář a další potřebné soubory.

1.2  Nastavení

Se dělá příkazem "git config" a probíhá na třech úrovních:

systémové
/etc/gitconfig (parametr --system)
uživatele
~/.gitconfig (parametr --global)
repozitáře
.git/config (parametr --local)

Nejobvyklejší nastavení:

 $ git config --global user.name "Moje Jméno"
 $ git config --global user.email "můjmail@example.com"
 $ git config --global core.editor "vim"
 $ git config --list    # vypíše naše nastavení
 $ git config --list --global    # výpis jen pro tuto úroveň nastavení
 $ git help config    # tam jsou všechny parametry

Parametr --list vypíše obsah --local, jsme-li uvnitř pracovní kopie (s podadresářem .git či hlouběji).

Nastavení aliasů (znáte ze subversion). Pozor, návykové:

 $ git config --global alias.co checkout
 $ git config --global alias.ci commit
 $ git config --global alias.st status

Případně doplnit "chybějcí" příkaz:

 $ git config --global alias.unstage 'reset HEAD --'

1.3  Výpis změn

Přehled lokálních úprav:

 $ git status     # přehledně
 $ git diff      # podrobně

1.4  Obnova souboru

Odvolání změn v souboru:

 $ git checkout soubor

1.5  Přidání souboru

Přidání nových souborů, nebo změn do stage:

 $ git add soubor

Reverzní operace - "vytažení" ze stage. Pokud na lokálním souboru proběhly nějaké změny, sloučí se.

 $ git reset HEAD soubor

Nahrání souborů ze stage do repozitáře, tj vytvoření revize:

 $ git commit

Bez parametrů zahrne všechny soubory ve stage a spustí textový editor pro zapsání popisu změn. Lépe po jednotlivých souborech (parametr -v přidá výpis diff, aby se nám připomnělo co se měnilo):

 $ git commit -v soubor

U jednoduchých změn můžeme zadat popis změny rovnou, bez práce v editoru:

 $ git commit soubor -m "popis změny"

Pro rychlý commit všech změněných a smazaných souborů lze přeskočit "add" pomocí parametru -a. Pozor, nezahrne nové, gitu dosud neznámé, soubory. Tam je ruční add nutný:

 $ git commit -a -m "popis změny"    # soubor zde nelze!

1.6  Smazání souboru (a obnova)

 $ git rm soubor
 $ git commit soubor -m "popis změny"

Pokud chci soubor obnovit ještě před commitem (po git rm):

 $ git checkout soubor

Chci-li obnovit soubor po git rm i commit:

 $ git checkout master~1 soubor

Soubor se na disku obnoví a bude ve stage jako přidný (add). Checkout vlastně vytáhne z repozitáře cokoliv i zpředchozích revizí.

Smazat soubor v indexu, ale ne na disku:

 $ git rm --cached soubor

Smažu-li soubor prostým rm:

 $ rm soubor

můžu ho obnovit opět pomocí checkout:

 $ git checkout soubor

nebo, pokud jsem ho opravdu chtěl smazat, srovná to git rm:

 $ git rm soubor

1.7  Přesun (přejmenování) souboru

Pouhé přejmenování:

 $ git mv název nový_název

Přesun do jiného adresáře:

 $ git mv soubor adresář/soubor

I tady si to lze rozmyslet a vrátit zpět před commitem:

 $ git reset HEAD adresář/soubor
 $ git reset HEAD soubor
 $ mv adresář/soubor soubor    # ano, opravdu bez "git"

Po commitu to vracet nebudeme, prostě si to znovu přesuneme tam, kde to původně bylo :-)

1.8  Historie změn (log)

Prostý log (je-li dlouhý, automaticky se zobrazí v less):

 $ git log

Výpis s diffem, jen poslední 2 revize:

 $ git log -p -2

Přidání statistik změn, názvů souborů, vč typu záznamu:

 $ git log --stat
 $ git log --name-only
 $ git log --name-status

Další možnosti nabízí parametry --pretty, --since, --until, --author:

 $ git log --pretty=oneline
 $ git log --since=2.weeks

Příkaz gitk spuštění v kořenovém adresáři projektu zobrází logy v okně (v Debianu je gitk samostatným balíčkem):

 $ gitk

1.9  Dodatečná oprava zápisu revize

Je-li potřeba opravit text poslední revize, nebo do ní ještě přidat zapomenutý soubor (tedy číslo revize se po opravě nezmění, nezvýší):

Jen oprava textu revize:

 $ git commit --amend

Commit a následné přidání souboru do té samé (poslední) revize:

 $ git commit -m 'initial commit'
 $ git add forgotten_file
 $ git commit --amend

Pokud už proběhlo publikování změn (git push), pak --amend nefunguje tak jednoduše a způsobí víc problémů než užitku.

2.  Vzdálené repozitáře

2.1  Stažení, aktualizace lokální kopie ze vzdáleného repozitáře

Úvodní stažení

 $ git clone url_repozitáře

Aktualizace ze vzdáleného adresáře

 $ git fetch    # jen stáhne
 $ git pull    # stáhne a začlení do lokální verze = fetch + merge FETCH_HEAD

2.2  Upload lokálních změn na server

 $ git push [server větev]

Jako výchozí se použije se server a větev, ze kterého se stahovalo. Nahrát můžeme jedině pokud od posledního stažení neproběhly na serveru žádné změny. Jinak to git odmítne. Musíme si pak znovu zaktualizovat svoji verzi podle vzdálené, případně začlenit změny a teprve pak můžeme zapsat. Je to proto, aby případné konflikty řešil vývojář na lokále a ne git na serveru.

2.3  Zobrazení použitých vzdálených repozitářů

 $ git remote    # zobrazí název, ne url
 $ git remote -v    # zobrazí url repozitáře(ářů)

Hlavní repozitář je vždy označen jako origin. Je jediným, do kterého můžeme (pokud vůbec můžeme) zapisovat. Ostatní případné repozitáře máme vždy jen pro čtení.

Obvykle ale pracujeme s jediným vzdáleným repozitářem. Další bychom si přidávali, kdybychom spolupracovali s více lidmi a chtěli mít přístup přímo do jejich repozitářů a ne jen do toho hlavního. Přidání se dělá přes "git remote add".

 $ git remote show origin [větev]

Ukáže podrobnosti o zdroji včetně seznamu všech větví (branch), které se tam nacházejí. Rovněž napíše, která větev je výchozi pro příkaz pull, a která pro příkaz push.

2.4  Přidání, přejmenování, odvolání vzdáleného repozitáře

 $ git remote add nick url_repozitáře

Nick je název, který můžeme používat místo celého url. Nick pro výchozí repozitář je onen origin.

V případě, že jsme začali s lokálním repozitářem a chceme to mít na serveru, tak na serveru uděláme bare repozitář s adresářem s příponou .git, pak na lokále uděláme výše zmíněné remote add a následně ještě první push musíme trochu obohatit:

 Na serveru:
 git init --bare myrepos.git

 Na lokále v dosud lokálním repozitáři:
 $ git remote add origin user@server:/cesta/myrepos.git
 $ git push --set-upstream origin master

čímž sdělíme nick lokálního repozitáře a větev, které se mají pushnout.

Pokud se mi přestěhoval server, tak to na lokále oznámím takto:

 $ git remote set-url origin user@server:/cesta/myrepos.git

a další pull/push už půjde odsud. Ověřit si to lze git remote -v.

Pokud by se jednalo o přestěhování u submodulu:

 $ git submodule set-url submodules/nazev user@server:cesta/nazev.git

"Smazání" (přestat používat):

 $ git remote rm nick

Odstraníme jen referenci. Vzdálený repozitář tak snadno zlikvidovat nejde.

Přejmenovat nick lze:

 $ git remote rename au zlato

2.5  Vzdálená větev napořád

Chci-li mít více větví na serveru a k nim trvalý přístup, tak si tu větev vytvořím na lokále (1), nahraji ji na server (2) a nastavím "tracking" mezi lokální a vzdálenou větví (3).

Například ke stávající větvi "master" si přidám větev "devel":

 $ git branch devel      (1)
 $ git checkout devel   (1)
 $ git push origin devel:devel     (2)
 $ git branch --set-upstream-to=origin/devel devel    (3)

Když pak chci s novou větví začít pracovat na jiném stroji, dám:

 $ git remote update
 $ git checkout -b devel origin/devel

3.  Značky (verze, vydání)

Značky vyznačují nějaké významné místo ve vývoji. Obvykle jsou ve formátu v1.2.3.

3.1  Vytvoření značky

Můžou být buď obyčejné:

 $ git tag v1.1.8

nebo anotované, do kterých se ukládají i údaje o tom, kdo značku vytvořil

 $ git tag -a v1.1.8

s dalšími parametry je možné vložit podpis, gpg,...

U anotované se otevře editor pro zapsání poznámky ke značce, což můžeme zjednodušit parametrem -m "text popisu". Obyčejný tag žádný popis nemá.

Značku lze přidělit i dřívějšímu commitu (tedy dodatečně) tak, že přidáme kontrolní součet revize:

 $ git tag -a v1.1.8 b73ccb

3.2  Výpis stávajících značek

Prostý vypíše jen samotné značky:

 $ git tag

Parametr -n[číslo] vypíše i poznámku

 $ git tag -n

Více podrobností nabízí příkaz

 $ git show v1.2.5

3.3  Porovnání verzí dle značek

 $ git diff v1.1.5 v1.1.6

3.4  Nahrání, stažení značek se vzdáleným repozitářem

Při stahování (clone, pull) se automaticky stáhnou i značky. Nechceme-li:

 $ git pull --no-tags

Při uploadu se naopak značky nenahrávají. Chceme-li:

 $ git push --tags

Chceme-li jen jednu konkrétní značku, je to podobné jako u větví:

 $ git push origin v1.2.4

3.5  Odstranění značky

 $ git tag -d v1.2.4

4.  Větvení (branch)

4.1  Výpis větví

lokální:

 $ git branch    # seznam větví, aktivní označena *
 $ git branch -v    # včetně čísla revize a popisu
 $ git branch --merged | --no-merged

vzdálené:

 $ git branch -r    # jen vzdálené
 $ git branch -a    # i lokální
 $ git remote show origin    # podrobné informace

4.2  Vytvoření nové větve:

 $ git branch nazev_vetve

Vytvořením nové větve se neudělá kopie lokálního repozitáře, jen se vytvoří nový ukazatel na aktuální pozici ve stávající větvi. Přepínáním mezi větvemi v této fázi uvidíme v obou větvích totéž. Teprve commit do jedné větve jí odliší od té druhé.

4.3  Přepnutí do jiné větve:

 $ git checkout nazev_vetve

Obecně vzato checkout nastaví lokální pracovní verzi na to, o co ho požádáme. Zde na poslední stav dané větve.

Proto taky lokální změny jsou vidět, ať se přepneme do jaké větve chceme, dokud je necomitneme.

Zkratka pro vytvoření nové větve a rovnou se do ní přepnout:

 $ git checkout -b nazev_vetve

4.4  Smazání (odstranění) větve

Samozřejmě nemůžeme v té větvi být.

 $ git branch -d vetev

Pokud větev obsahuje nezačleněné změny, git jí odmítne smazat a upozorní na to, ale nabídne taky možnost smazat to natvrdo tak, že místo -d dáme -D.

 $ git branch -D vetev

4.5  Sloučení větví - merge

Nejprve se přepneme do větve, do které se má sloučení provést:

 $ git checkout master

A pak samotné sloučení:

 $ git merge vetev

Větev vetev se začlení do větve master.

Pokud v cílové větvi neproběhly mezi tím žádné změny, dojde vlastně jen k posunutí ukazatele větve master do bodu, kam se došlo ve větvi vetev. Tomuto sloučení se říká "fast forward". Je to vlastně, jako bychom všechny změny dělali sériově přímo ve větvi master - jen jsme si odskočili "vedle".

Složitější situace nastane, když slučujeme do větve, kde už změny také proběhly. Jsou dvě možnosti:

1. Změny se nepřekrývají (například ve větvích se upravovaly jiné soubory: Pak git provede "rekurzivní" sloučení automaticky sám - najde si v historii bod odkud se dají větve sloučit a provede třícestné sloučení obou větví a společného předka. Výsledkem je nový snímek

2. Změny se překrývají - měnila se táž část téhož souboru: Git ohlásí konflikt, vytvoří novou verzi konfiliktního souboru, kde je konflikt zobrazen a chce od nás, abychom ruční editací vytvořili námi požadovanou verzi souboru.

Informace o konfliktu se můžeme dovědět dodatečně i z příkazu git status. (kdybychom šli mezi tím spát).

Konfliktní soubor obsahuje něco takového:

 <<<<<<< HEAD:index.html
 <div id="footer">contact : email.support@github.com</div>
 =======
 <div id="footer">
 please contact us at support@github.com
 </div>
 >>>>>>> iss53:index.html

Rovnítková čára odděluje dvě verze. Horní je v aktuální větvi, dolní je v začleňované větvi. Můžeme nechat to, co je nad čarou, nebo to co je pod čarou, nebo to předělat úplně jinak. Po editaci samozřejmě odstraníme ty tři gitem přidané řádky.

Po této editaci je nutné zadat

 $ git add vyřešený_soubor

čímž oznámíme, že konflikt byl vyřešen.

 $ git mergetool

Spustí "vizuální" možnost pro řešení konfliktu, např vimdiff.

4.6  Sloučení větví - rebase

Rebase je jiný způsob jak začlenit práci v samostatné větvi do té hlavní přehledněji.

Udělal jsem si feature_branch a teď v ní dělám. Rád bych jí začlenil do "master", ale tam mezi tím taky proběhla nějaká práce. Můžu zkusit merge a doufat, že to projde (změny v obou větvích se vzájemně nekříží).

A nebo si do své feature_branch "do-nahraju" před první změnu všechny změny z master větve a tím jí umístím/posunu na vrchol změn, které se během práce ve feature_branch odehrály v master. Tomu se říká "rebase". Tedy posunutí báze od které se moje větev odklonila.

V příkazech takto:

 $ git checkout feature_branch   (1)
 $ git rebase origin/master   (2)
 $ git push --force origin feature_branch  (3)

Rebázi dělám do feature_větve, takže se do ní přepnu (1). Následně udělám rebase podle vzdálené větve master (2). Nakonec svoji posunutou větev nahraji do odpovídající větve na serveru (3).

Parametr --force je nutný, protože tam aktualizovaný stav prostě potřebuji narvat a neřešit jemnosti - ty díky rebase skončily :-)

Rebase dělám na origin/master a ne na lokální master spíš kvůli opatrnosti. Kdyby lokální master nebyl aktualizovaný, udělal bych pomocí rebase celkem slušný zmatek. Takhle se o to nemusím zajímat.

Pak můžu sloučit feature_branch do master obvyklým způsobem:

 $ git checkout master
 $ git pull
 $ git merge feature_branch

Kdybych vyměkl, můžu se po rebase vrátit:

 $ git reset --hard origin/feature_brach

Můžeme se taky podívat na rozdíly mezi feature_branch a master před rebase a po rebase. Pokud v master větvi byly nějaké změny, bude diff po rebase krásně přehledný a bude obsahovat jen změny v té mojí feature_branch. Před rebase tam je spousta "balastu" ze změn v master od jiných vývojářů.


4.7  Zrušení vzdálené větve

Chceme-li zrušit větev na vzdáleném repozitář nabízejí se dvě varianty příkazu:

 $ git push --delete origin branch   # (1)
 $ git push origin :branch           # (2)

Vyčištění lokálních záznamů o zrušené vzdálené větvi

Smazal-li někdo na serveru nějakou větev, kterou jsme lokálně sledovali, nezmizí se sledování automaticky ani po fetch/merge. Příkaz branch jí lokálně neukazuje, ale

 $ git remote show origin

jí vypíše, nicméně s poznámkou, že bychom si jí měli "dočistit" příkazem:

 $ git remote prune [--dry-run] origin

--dry.-run samozřejmě jen ukáže, co by se dočišťovalo bez toho, aby se to udělalo.