Git
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ů
editovatCommit
editovatSlovo 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
editovatVě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
editovatFork 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
editovatClone 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
editovatPull 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
editovatInstalace
editovatAbyste 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ů.
- oficiální
- další
- příkazový řádek - viz. projektová wiki
- grafické rozhraní - viz. projektová wiki, přehled s náhledy apod.
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
editovatPrvní 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
editovatNa 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.
Vytvoření lokálního repozitáře
editovatChceme-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
editovatGIT 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 WindowsC:\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
editovatSoubor .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
editovatgit
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ář
editovatLoká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říkazemgit commit
uloženy do repozitáře; soubory se do ní přidávají příkazemgit 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
editovatK 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
editovatGit 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 commitugit 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
bezgit 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ů
editovatPři změnách souborů je vhodné dodržovat následující postup:
- Aktualizace informací v lokálním repozitáři ze vzdálených repozitářů
- Vytvoření a úpravy zdrojových souborů pomocí obvyklých nástrojů
- Vytvoření větve pro začlenění úprav s případným nastavením výchozí verze
- Vytvoření seznamu změn
- Uložení změn do lokálního repozitáře příkazem
git commit
- Přenesení změn do vzdáleného repozitáře příkazem
git push
Aktualizace informací v lokálním repozitáři
editovatPrvní 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ů
editovatSoubory 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
editovatVě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í
editovatPro 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
editovatZadá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
editovatNá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
editovatKonec 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
editovatTo, ž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
editovatPř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
editovatSeznam 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
editovatPokud 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
editovatPří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
editovatPokud 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
editovatPří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
editovatPokud 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
editovatPř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
editovatRebase (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
editovatZkopí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
editovatLoká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ů
editovatPř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ů
editovatPokud 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
editovatHledání v historii
editovatHistorii 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ů
editovatPro 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
editovatVí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
editovatPř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
editovatPokud 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.
Odkazy
editovatLiteratura
editovat- Český překlad knihy "Pro Git", Scott Chacon -- vyšlo v edici CZ.NIC, PDF volně ke stažení.
- HTML podoba "Pro Git" (český překlad) na oficiálních stránkách Gitu -- překlad vychází z výše uvedeného překladu vydaného v CZ.NIC.
- git - the simple guide stručný úvod do gitu od Rogera Dudlera (anglicky)
- Jasně, umím Git… článek Michala Hořejška
- GitHub: Permission denied (publickey). fatal: Could not read from remote repository na serveru stackoverflow.com
Externí odkazy
editovat- Encyklopedický článek Git ve Wikipedii