Modul 3 — Verzovací systémy: Git
12. Základy Gitu: commit, branch, merge
Co je verzovací systém a proč je Git standardem, základní pracovní cyklus (commit, branch, merge) a nejpoužívanější příkazy pro každodenní práci.
Úvod a kontext
Každý netriviální software se v čase mění — přidávají se funkce, opravují
chyby, refaktoruje se kód. Bez nástroje, který tyto změny sleduje, je
extrémně těžké odpovědět na otázky typu "kdo a proč změnil tento řádek?",
"jak vypadal kód před týdnem?" nebo "jak bezpečně zkombinovat práci dvou
lidí na stejném projektu?". Verzovací systémy (version control systems,
VCS) řeší přesně tohle. Git je dnes de facto standardní distribuovaný
verzovací systém — používá ho naprostá většina softwarových projektů a je
základním nástrojem každého DevOps inženýra, protože nejen kód aplikací,
ale i infrastrukturní konfigurace (Terraform, Ansible, Kubernetes manifesty)
se dnes verzují v Gitu.
Teorie
Distribuovaný vs. centralizovaný VCS
Starší systémy (např. SVN) byly centralizované — existoval jeden
centrální server s historií a vývojáři si stahovali jen aktuální verzi.
Git je distribuovaný: každý, kdo si repozitář naklonuje (git clone),
získá kompletní historii všech změn od začátku projektu. To umožňuje
pracovat offline, dělat commity bez připojení k síti a mít víc rovnocenných
kopií historie (typicky se ale i tak používá jeden sdílený server, např.
GitHub/GitLab, jako "zdroj pravdy").
Tři oblasti: working directory, staging area, repository
Git rozlišuje tři stavy souborů:
- Working directory — soubory tak, jak je vidíte a editujete na disku.
- Staging area (index) — místo, kam příkazem
git add"připravíte"
změny, které chcete zahrnout do dalšího commitu. Umožňuje to udělat
commit jen části rozpracovaných změn. - Repository (
.git) — trvalá historie commitů, uložená lokálně ve
skryté složce.gituvnitř projektu.
Typický cyklus: upravíte soubor (working directory) → git add (staging
area) → git commit (trvalý záznam v repository).
Commit jako snímek stavu
Commit je nezměnitelný (immutable) záznam obsahující: snímek celého
sledovaného obsahu projektu v daném okamžiku, autora, časové razítko,
zprávu popisující změnu a odkaz na rodičovský commit (nebo commity u
merge). Commity tvoří orientovaný acyklický graf (DAG) — každý commit ví,
odkud "vyrostl". Identifikátor commitu je hash (SHA-1), např.
a1b2c3d... — obsah určuje identitu, takže dva commity se stejným
obsahem, autorem, časem a rodičem by měly stejný hash.
Branch (větev)
Branch je jen pojmenovaný ukazatel na konkrétní commit — nic víc.
Když uděláte nový commit na aktuální větvi, ukazatel se posune na nový
commit. Větve umožňují paralelně vyvíjet různé funkce nebo opravy, aniž
by si navzájem překážely: vývojář si vytvoří vlastní větev, dělá v ní
commity, a teprve když je funkce hotová, změny se sloučí zpět do hlavní
větve (často pojmenované main nebo historicky master).
Merge (sloučení)
git merge sloučí historii jedné větve do druhé. Git najde nejbližšího
společného předka obou větví a spočítá rozdíly od něj k oběma koncům.
Pokud se změny nekříží (týkají se různých částí souborů nebo různých
souborů), Git je automaticky spojí. Pokud se stejná část souboru změnila
odlišně na obou větvích, vznikne konflikt a Git požádá o ruční
rozhodnutí, která verze (nebo kombinace) má zůstat.
Existují dva základní typy mergů:
- Fast-forward merge — pokud cílová větev od rozdělení vůbec
nepokročila, Git jen posune ukazatel dopředu, bez vytvoření nového
"merge commitu". - Three-way merge — pokud obě větve mezitím dostaly nové commity,
vznikne nový merge commit se dvěma rodiči, který reprezentuje spojení
obou historií.
Praktický příklad
Založení nového repozitáře a první commit:
$ mkdir muj-projekt && cd muj-projekt
$ git init
Initialized empty Git repository in /home/user/muj-projekt/.git/
$ echo "# Můj projekt" > README.md
$ git status
On branch main
No commits yet
Untracked files:
README.md
$ git add README.md
$ git commit -m "Přidat README"
[main (root-commit) f3a1c9d] Přidat README
1 file changed, 1 insertion(+)
Vytvoření nové větve pro funkci, práce na ní a sloučení zpět:
$ git branch # výpis existujících větví
* main
$ git checkout -b feature/login # vytvoří a přepne na novou větev
Switched to a new branch 'feature/login'
$ echo "def login(): pass" > login.py
$ git add login.py
$ git commit -m "Přidat kostru přihlašování"
[feature/login 7b2e4aa] Přidat kostru přihlašování
1 file changed, 1 insertion(+)
$ git checkout main
Switched to branch 'main'
$ git merge feature/login
Updating f3a1c9d..7b2e4aa
Fast-forward
login.py | 1 +
1 file changed, 1 insertion(+)
Protože main mezitím nedostal žádný nový commit, proběhl fast-forward
merge — žádný samostatný merge commit nevznikl. Zobrazení historie:
$ git log --oneline --graph
* 7b2e4aa Přidat kostru přihlašování
* f3a1c9d Přidat README
Novější syntaxe pro přepínání větví (Git 2.23+) odděluje přepínání
(switch) od vracení souborů (restore), což je méně matoucí než
přetížený checkout:
$ git switch -c feature/login # ekvivalent "checkout -b"
$ git switch main
Shrnutí
Git je distribuovaný verzovací systém, kde repozitář obsahuje kompletní
historii projektu jako graf commitů. Práce probíhá přes tři oblasti —
working directory, staging area a repository — a příkazy add/commit
mezi nimi přesouvají změny. Branch je jen pojmenovaný ukazatel na commit,
který umožňuje izolovaný vývoj; merge pak historie spojí zpět, buď
jednoduchým posunem ukazatele (fast-forward), nebo vytvořením merge
commitu se dvěma rodiči, případně s ručním řešením konfliktů.
Kontrolní otázky
- Jaký je rozdíl mezi working directory, staging area a repository, a
které příkazy mezi nimi přesouvají změny? - Co přesně je branch v Gitu — jaká datová struktura se za tím pojmem
skrývá? - Za jakých podmínek proběhne fast-forward merge a kdy naopak vznikne
samostatný merge commit? - Vytvořte lokálně repozitář, udělejte v něm dva commity na
main,
pak vytvořte větev, přidejte tam třetí commit a slučte ji zpět —
ověřte pomocígit log --graph, jaký typ merge proběhl.
Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.