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ů.

Odhadovaná délka studia: 60 minut · Stav: nehotovo

Technologie: Git

Ú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 za git fetch + git merge (nebo rebase,
    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):

  1. Vytvoříte větev z aktuální main (git switch -c feature/x).
  2. Uděláte commity s vaší změnou.
  3. Odešlete větev na server (git push -u origin feature/x).
  4. Na GitHubu/GitLabu otevřete pull request (PR) — návrh na
    sloučení vaší větve do main, který zobrazí diff všech změn.
  5. Kolegové PR revidují (code review), nechávají komentáře, případně
    žádají úpravy. Automatizovaná CI pipeline zpravidla pustí testy.
  6. 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ím git merge na
    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čku main a 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

  1. Čím se liší git merge od git rebase z pohledu výsledné
    historie commitů?
  2. Proč je nebezpečné rebasovat větev, kterou už stáhli jiní lidé?
  3. Co znamená konfliktní blok <<<<<<< HEAD ... ======= ... >>>>>>>
    a jak se řeší?
  4. Proč je git push --force-with-lease bezpečnější volba než
    git push --force po 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í.