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.gitpro 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 --graphgit 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,HEADbude odkazovat na<commit_id>)git checkout <branch>– přepínání mezi větvemi,HEADbude 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 addgit 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ř.*.backupzahrnuje 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 mergenelinearizuje 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 winmergegit 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: