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 :-)
Obsah stránky (hide)
- 1. Základní úlohy
- 1.1 Vznik repozitáře
- 1.2 Nastavení
- 1.3 Výpis změn
- 1.4 Obnova souboru
- 1.5 Přidání souboru
- 1.6 Smazání souboru (a obnova)
- 1.7 Přesun (přejmenování) souboru
- 1.8 Historie změn (log)
- 1.9 Dodatečná oprava zápisu revize
- 2. Vzdálené repozitáře
- 3. Značky (verze, vydání)
- 4. Větvení (branch)
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.