Modul 3 — Verzovací systémy: Git
13. Kolaborativní workflow: pull requesty, rebase, řešení konfliktů
Jak týmy spolupracují na sdíleném repozitáři přes pull requesty, rozdíl mezi rebase a merge a praktický postup řešení konfliktů.
Úvod a kontext
Předchozí lekce ukázala Git na úrovni jednoho vývojáře pracujícího
lokálně. Ve skutečném týmu ale desítky lidí commitují do stejného
repozitáře současně, a je potřeba proces, který zajistí, že se změny
dostanou do hlavní větve kontrolovaně — s revizí kódu (code review),
běžícími testy a bez rozbití práce ostatních. K tomu slouží pull
requesty (na GitHubu) nebo merge requesty (na GitLabu, stejná
myšlenka pod jiným názvem) a technika rebase jako alternativa k
prostému mergi pro udržení čitelné historie.
Teorie
Vzdálené repozitáře (remotes)
Lokální repozitář se synchronizuje se vzdáleným (typicky na GitHubu/
GitLabu) přes tzv. remote, obvykle pojmenovaný origin:
git clone <url>— stáhne kompletní repozitář a nastavíorigin.git fetch— stáhne nové commity ze vzdáleného repozitáře, ale
neslučuje je do vaší aktuální větve.git pull— je zkratka zagit fetch+git merge(neborebase,
podle konfigurace) do aktuální větve.git push— odešle vaše lokální commity na vzdálený repozitář.
Pull request workflow
Typický kolaborativní postup (tzv. "feature branch workflow" nebo
"GitHub flow", podrobněji v příští lekci):
- Vytvoříte větev z aktuální
main(git switch -c feature/x). - Uděláte commity s vaší změnou.
- Odešlete větev na server (
git push -u origin feature/x). - Na GitHubu/GitLabu otevřete pull request (PR) — návrh na
sloučení vaší větve domain, který zobrazí diff všech změn. - Kolegové PR revidují (code review), nechávají komentáře, případně
žádají úpravy. Automatizovaná CI pipeline zpravidla pustí testy. - Po schválení se PR sloučí (merge, squash nebo rebase — viz níže) do
main, obvykle přes tlačítko v UI, ne ručnímgit mergena
lokálu.
PR tedy není čistě git koncept — je to funkce hostovací platformy
postavená nad gitovými větvemi, která přidává proces revize a
diskuze kolem sloučení.
Merge vs. rebase
Když se vaše větev za dobu vývoje "rozjede" od main (na main
mezitím přibyly nové commity od kolegů), máte dvě možnosti, jak svou
větev aktualizovat:
git merge main(do vaší větve) — vytvoří nový merge commit,
který spojí obě historie. Výhoda: nemění existující commity, bezpečné.
Nevýhoda: historie obsahuje "uzlíky" merge commitů, které ji ztěžují
čtení.git rebase main— Git vezme vaše commity, dočasně je odloží,
přetočí vaši větev na aktuální špičkumaina pak vaše commity
jeden po druhém znovu aplikuje nad ní. Výsledkem je lineární historie
bez merge commitů — vypadá to, jako byste svou práci začali až na
nejnovějšímain.
Klíčové pravidlo: nikdy nerebasujte větev, kterou už používají jiní
lidé (typicky sdílenou main) — rebase přepisuje historii (mění
hashe commitů), a pokud si ji někdo jiný mezitím stáhl, vznikne
rozpor mezi verzemi. Rebase je bezpečný na vaší vlastní feature
větvi, kterou zatím nikdo jiný nepoužívá.
Konflikty a jejich řešení
Konflikt nastane, když Git (při merge i rebase) narazí na místo, kde
se stejná část souboru změnila odlišně na obou stranách a nedokáže
rozhodnout, která verze je správná. Git zastaví operaci a označí
konfliktní místa přímo v souboru:
<<<<<<< HEAD
verze z vaší aktuální větve
=======
verze z větve, kterou slučujete
>>>>>>> feature/x
Řešení: ručně upravit soubor na výslednou podobu (odstranit značky
<<<<<<<, =======, >>>>>>> a ponechat správný obsah, případně
kombinaci obou), pak soubor označit jako vyřešený a operaci dokončit.
Praktický příklad
Typický cyklus práce s pull requestem od začátku do konce:
$ git switch -c feature/pridat-export
$ # ... úpravy kódu ...
$ git add export.py
$ git commit -m "Přidat export do CSV"
$ git push -u origin feature/pridat-export
Na GitHubu se otevře PR z feature/pridat-export do main. Mezitím
kolega slouží jinou funkci a main postoupí. Aktualizace vaší větve
rebasem, aby PR obsahoval jen vaše skutečné změny bez zbytečného
merge commitu:
$ git switch feature/pridat-export
$ git fetch origin
$ git rebase origin/main
Pokud rebase narazí na konflikt:
$ git rebase origin/main
Auto-merging export.py
CONFLICT (content): Merge conflict in export.py
error: could not apply 7b2e4aa... Přidat export do CSV
Řešení konfliktu ručně a pokračování rebase:
$ cat export.py # najít značky <<<<<<< ======= >>>>>>>
$ vim export.py # ručně vybrat/spojit správný obsah
$ git add export.py # označit konflikt jako vyřešený
$ git rebase --continue
Pokud by se rebase zamotal, lze ho kdykoliv bezpečně zrušit a vrátit
se do stavu před ním:
$ git rebase --abort
Po úspěšném rebase je potřeba větev znovu odeslat na server — protože
rebase změnil hashe commitů, obyčejný push by byl odmítnut jako
"non-fast-forward", proto force-push (bezpečný jen na vlastní feature
větvi, nikdy na sdílené main):
$ git push --force-with-lease origin feature/pridat-export
--force-with-lease je bezpečnější než holé --force — odmítne
přepsání, pokud mezitím na serveru přibyl commit, o kterém nevíte
(např. od kolegy), a předejde tak nechtěné ztrátě jeho práce.
Shrnutí
Kolaborace v Gitu probíhá přes vzdálené repozitáře (fetch/pull/
push) a pull requesty, které přidávají proces revize kódu kolem
sloučení větví. Merge a rebase řeší stejný problém — sladit
rozdělené historie — odlišně: merge je bezpečný a zachovává historii
věrně, rebase produkuje čistší lineární historii, ale nesmí se použít
na již sdílené větvi. Konflikty vznikají při protichůdných změnách
téhož místa v souboru a řeší se ručně podle značek <<<<<<</=======/
>>>>>>>, následovaným add a continue (u rebase) nebo commitem
(u merge).
Kontrolní otázky
- Čím se liší
git mergeodgit rebasez pohledu výsledné
historie commitů? - Proč je nebezpečné rebasovat větev, kterou už stáhli jiní lidé?
- Co znamená konfliktní blok
<<<<<<< HEAD ... ======= ... >>>>>>>
a jak se řeší? - Proč je
git push --force-with-leasebezpečnější volba než
git push --forcepo rebasu feature větve?
Lekce na sebe nejsou zamčené — libovolnou lekci můžete otevřít i označit jako hotovou v jakémkoliv pořadí.