Git
Git je populární open-source systém pro správu verzí kódu.
- decentralizovaný
- nelineární
- inkrementální ukládání změn
Intro
Instalace
- Linux: systémový balíček (
apt-get install git
,pacman -S git
, apod.) - Windows: existuje několik možností
- základní verze: https://git-scm.com/download/win
- Git for Windows: https://gitforwindows.org/
- balíček pro MSYS2:
pacman -S git
Kromě toho je k dispozici mnoho GUI a rozšíření/pluginů pro IDE. My se však budeme držet příkazového rozhraní pro terminál – to je myslím nejlepší způsob, jak pochopit princip fungování.
Základní pojmy
- repozitář
- commit
- větev
- working tree
Základní použití – získání existujícího projektu
git clone <url>
– naklonování existujícího repozitáře- např.
git clone https://github.com/blinry/nelder-mead-optimizer.git
pro náš projekt
- např.
Všechny další příkazy je potřeba spouštět uvnitř projektu, tedy v adresáři,
který obsahuje .git/
(vznikne např. při klonování).
git status
– zobrazení stavu lokálního repozitářegit diff
– zobrazení rozdílu mezi soubory v pracovním adresářem (working tree) a posledním commitem (přesnějiHEAD
)git log
,git log -p
– zobrazení historie commitůgit log --graph
git log --oneline --graph --all --decorate
(lze vytvořit alias, např.git config --global alias.ll "log --oneline --graph --all --decorate"
a pak použítgit ll
)
Workflow pro programátory
Synchronizace databáze se vzdáleným repozitářem:
git fetch
– zjistí aktuální stav vzdáleného repozitáře („remote“)git pull
– synchronizuje stav aktuální větve z remote do lokální (ve skutečnostigit fetch
+git merge
/git rebase
, viz dále)git push
– synchronizuje stav aktuální větve z lokální do remote
Vytváření nových větví, přepínání mezi větvemi:
git checkout
– přepínání stavu souborů v adresáři dle zadaného stavu v gitugit checkout <commit_id>
– přesun na zadaný commit (libovolný přesun v historii,HEAD
bude odkazovat na<commit_id>
)git checkout <branch>
– přepínání mezi větvemi,HEAD
bude odkazovat na<branch>
git checkout <path/to/file>
– zahození lokálních změn v souboru/adresáři, přepsání stavem dle aktuálníHEAD
(ekvivalentní s příkazemgit restore <path/to/file>
)
git checkout -b <branch>
– vytvoření nové větve dle aktuálníHEAD
Konfigurace metadat (stačí provést jednou; --global
aplikuje nastavení pro
všechny projekty, uloží se v home adresáři daného uživatele; --local
změní
nastavení jen v daném projektu):
git config --global user.name "John Doe"
– nastaví jméno autoragit config --global user.email "john.doe@example.com"
– nastaví email autora
Přidávání změn:
git add
– přidat soubory do zásobníku změn (stage) pro následující commitgit add -p
– interaktivní přidávání změn (lze přidat jen některé soubory nebo i část změn v daném souboru pro lepší logické členění změn do commitů)
git restore <file>
– zahození necommitovaných změngit reset <file>
nebogit restore --staged <file>
– vrátígit add
git mv <file_old> <file_new>
– přejmenování souborugit rm <file>
– smazání souborugit commit -m <message>
– vytvoření commitugit commit --amend
– sloučení změn do posledního commitu (taky se hodí pro opravu zprávy commitu)
NIKDY nepoužívejte příkazy git add .
nebo git commit -a
(git commit --all
)!!!
Vždy commitujte jen to, co opravdu chcete. Navíc commity by měly být systematicky rozdělené a rozumně „velké“.
Soubor .gitignore
Umožňuje ignorovat specifické soubory a adresáře, které nikdy nechceme commitovat.
Takové soubory nebudou zobrazeny v git status
a nebudou přidány do stage příkazem git add
(pokud to explicitně nevynutíme).
- komentáře začínají znakem
#
- každý řádek obsahuje název souboru/adresáře, který chceme ignorovat
- lze používat
*
pro napasování libovolného řetězce (např.*.backup
zahrnuje všechny soubory končící řetězcem „.backup“)
Do gitu typicky nepatří:
- lokální konfigurace editoru/IDE (např.
.vscode/
,.settings/
,.project/
) - „záložní“ soubory (
*.bak
,*.backup
,*.orig
) - binární soubory vzniklé překladem ze zdrojových kódů (např.
*.exe
, v Linuxu je to horší – binární soubory se musí explicitně uvést nebo umístit do podadresáře (např.bin
))
Další pojmy
- remote
- head, HEAD
- ref – pojmenované odkazy, např. head, HEAD, remote, tag
- stage
- stash
A spousta dalších, viz https://git-scm.com/docs/gitglossary
Merge a rebase
git merge <branch>
– sloučí zadanou větev do aktuální větve (viz obrázek)- commity ze zúčastněných větví jsou ve výsledném logu seřazeny chronologicky – není hned jasné, ve které větvi vznikly
- může dojít ke konfliktům – ty musí vyřešit uživatel, git je uloží v dodatečném commitu na konci větve (tzv. „merge commit“)
- z pohledu na merge commmit nemusí být hned patrné, jak byly které konflikty řešeny a které commity je způsobily
- zdůraznit, že
git merge
nelinearizuje historii (každý commit má pořád stejného předchůdce a následníka) – pouze v logu to tak vypadá
git rebase <branch>
– znovu aplikuje commity z aktuální větve, aby počátek („base“) této větve byl na zadané větvi (viz obrázek)- dojde k logickému uspořádání commitů (linearizace historie)
- při aplikaci každého commitu je potřeba potenciálně řešit konflikty způsobené právě tímto commitem – více práce pro uživatele, ale vede to k čistší a přehlednější historii změn
- po rebase lze provést merge bez konfliktů (a bez vytvoření dodatečného commitu) – tzv. fast forward, dojde k prostému přesunu např.
refs/heads/main
git rebase --interactive
, přepisování historie
Mergetool
git mergetool poskytuje rozhraní mezi gitem a editorem pro řešení konfliktů. Git postupně otevírá editor se soubory, kde je třeba vyřešit konflikty. Pro každý soubor se otevře okno s typicky 4 verzemi (některá případně může chybět v závislosti na nastavení):
- společná verze před rozvětením (BASE)
- verze z větve, do které mergujeme (LOCAL)
- verze z větve, kterou mergujeme (REMOTE)
- výsledná verze, kde je potřeba vyřešit konflikt (MERGED)
Podporované nástroje zobrazí příkaz git mergetool --tool-help
.
VSCode/VSCodium
VSCode/VSCodium podporuje 3-way merge od verze 1.70. Konfigurace:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'
Poznámka: Pro VSCodium použijte codium
místo code
.
VSCode/VSCodium lze analogicky nastavit jako defaultní editor pro git (např. pro editování commit message):
git config --global core.editor 'code --wait'
A také jako diff tool:
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
WinMerge
Jednoduchý nástroj pro Windows je např. WinMerge. Konfigurace:
git config --global merge.tool winmerge
git config --global mergetool.winmerge.path "C:/Users/klinkjak/AppData/Local/Programs/WinMerge/WinMergeU.exe"
(cestu je třeba příslušně upravit)
Testovací repozitář, kde si lze vyzkoušet řešení konfliktů: https://github.com/redguardtoo/test-git-mergetool
Hledání chyb v kódu pomocí binárního vyhledávání
Ukážeme si git bisect – nástroj pro hledání commitů v historii pomocí metody bisekce (půlení intervalu). Hodí se např. pro identifikaci commitů, které způsobily nějakou chybu.
Na začátku musí být pracovní adresář čistý – zkontrolujte pomocí git status
.
git bisect start
– spustí bisekcigit bisect bad
– označí aktuální commit (HEAD
) jako špatnýgit bisect good <some-commit-id>
– označí nějaký předchozí commit jako dobrý
Dále git iterativně přepíná stav souborů v pracovním adresáři podle commitů, které je třeba zkontrolovat.
Po každém kroku je třeba označit daný commit jako špatný (git commit bad
) nebo dobrý (git commit good
).
V průběhu je možné sledovat stav bisekce pomocí příkazů git bisect visualize
a git bisect log
.
Na konci by měl algoritmus skončit ve stavu prvního špatného commitu.
Příkaz git bisect reset
vrátí stav na začátek (commit před spuštěním git bisect start
).
Odkazy
Rebase:
- A rebase-based workflow
- Why is rebase-then-merge better than just merge (complement)
- Introduction to Git rebase, force-push, and merge conflicts
- Git Branching - Rebasing
Workflow, best practices:
Cheat Sheet: