Tento článek patří do seriálu Jak na git. Ostatní články seriálu:
- Jak na git díl 0 - Co, proč, jak?
- Jak na git - díl 1. - git init, remote, config, clone, add, commit, push
- Jak na git - díl 2. - git status, log, checkout, reset
- Jak na git - díl 3. - git revert, stash, diff, clean
- Jak na git - díl 4. - git pull, git fetch, git branch, git merge
- Jak na git - díl 5. - git tag, git cherry-pick a opravy rozbitého gitu
- Jak na git - díl 6. - git rebase a interaktivní rebase
- Git - přidávání částí souborů do commitu
- Git - tutoriály a návody
Příkazy git merge
a git rebase
dělají v podstatě stejnou věc. Pracují na spojení změn z dvou větví, jen každý trochu jinak. Příkaz merge byl vysvětlen ve 4. díle a jak název napovídá, vytvoří nový commit, ve kterém jsou změny spojeny. Jak ale název napovídá u rebase, zde dojde ke změně báze neboli základny. Commity se přesunou, změní pořadí, stlačí se do jediného apod.
Pro ujasnění: Při použití rebase se příkazu merge nelze vyhnout. Použije se ale vždy a pouze až na konci, při mergování do master větve. V průběhu vývoje by se měl provádět jen rebase.
Proč přemýšlet o rebase namísto merge?
Použití merge je bezpečnější než rebase, protože se nejedná o destruktivní operaci - jen se přidávají další commity. Pomocí rebase se ale dá udržet přehlednější historie gitu. Převážně pokud je nastavena politika vývoje na squash and rebase. To znamená, že se před mergnutím do masteru udělá squash (vše se sloučí do 1 commitu), poté rebase (přesun) na master a poté teprve merge. Při použití přepínače --no-ff
(no fast-forward), bude graf master větve vypadat jako na obrázku vlevo. V případě klasického merge dojde k větvení jako na obrázku vpravo.
Jak na rebase
Rebase krásně znázorňují obrázky níže. Větev feature obsahuje celkem 3 commity, které vycházejí z masteru. Do masteru ale postupně přibyly další 2 commity. Nyní lze master mergnout do feature větve, nebo provést rebase. A ten je právě výhodnější, podrobněji v git dokumentaci.
# Provede rebase aktuální větve na větev master
git rebase master
# Velmi často lidé také používají
git rebase origin/master
Po provedení rebase bude výsledný graf vypadat, jak je znázorněno níže. Důležité je si všimnout, že commity mají jiný hash než původně! Rebase veškeré commity přepracuje. Chová se v podstatě jako opakovaný cherry-pick a po dokončení staré commity smaže.
Pravidla bezpečného rebase:
Používat jen na neveřejné větve - Pokud s větví pracuje více lidí, rebase se nesmí provádět. Jako třeba s master větví. Lze dělat rebase na master, nikdy ale ne master na jinou větev.
Force push je nutný - Po rebase git bude chtít, aby se prvně provedl pull remote větve. To se ale nesmí udělat a musí se provést git push --force
. Rebase je spjatý s force push a proto je snad nyní pravidlo 1 jasnější. Proto se také hodí nastavit master větev jako protected, třeba v Gitlabu.
Interaktivní rebase
Při rebase nastává problém, pokud soubory obsahují konflikty. Rebase si žádá vyřešit konflikt po každém commitu, čímž se může celý rebase protáhnout. Řešení se nabízí pomocí interaktivního rebase. Ten umožňuje u commitů ve větvi měnit jejich pořadí, upravit obsah či commit message nebo commit úplně vymazat. Hlavně ale umožňuje squashing, neboli sloučení commitů, což je znázorněno na obrázku níže. Poté se při rebase budou konflikty řešit pouze jedinkrát.
# Interaktivní rebase na aktuální feature větvi vyžaduje určení počátku větve
git rebase -i 55aa66
# S tím pomůže příkaz merge-base, který vrátí commit kde se větev feature oddělila od masteru
git rebase -i $(git merge-base feature master)
# Navíc místo 'feature' lze použít HEAD pokud je větev feature aktuálně check-outnutá
git rebase -i $(git merge-base HEAD master)
# Obsah souboru pro konfiguraci a provedení rebase
pick dd7216 First squash c4e7bb Seconds squash 14a73c third
Po spuštění interaktivního rebase se otevře editor s podobným obsahem jako je znázorněno výše. Zde je možné vybírat akce u jednotlivých commitů. V tomto případě se první commit použije a další 2 se sloučí do prvního. Po uložení se stane přesně to, co na obrázku níže.
Vždy jen merge nebo rebase
Míchání rebase a merge v jedné větvi není úplně vhodné. Pokud se provede merge masteru do feature větve a někdy v budoucnu bude potřeba provést rebase, merge commit se zahodí. To znamená, že pokud se již merge commitem vyřešily konflikty, bude je potřeba řešit znovu. V gitu pro rebase sice existuje přepínač --rebase-merges
, ten ale nefunguje, jak by se dalo očekávat.
Pokud kdokoli z týmu řekne "Mergni si master", stačí když se provede rebase na master a poté force push. Není ale problém používat rebase, pokud ostatní používají merge. Merge se vždy použije až úplně na konec, když se změny squashnou a poté mergnou do masteru.
Zkušenosti s gitem a rebase lze sdílet v komentářích. Další počtení je na atlassian.com
K tomuto článku již není možné přidávat další komentáře
Komentáře
Mám vytvořené dvě zkratky:
mergef = merge --no-ff
# používám, když zařazuju nějakou feature větev (nepoužívám squashování featury do jednoho commitu)rebasef = rebase --rebase-merges
# používám, když si aktualizuji změny z rodičovské větve. Ten přepínač mi zajistí, že se mi kolejničky nerozpustí.committo = <trošku složitější>
# který mi commitne aktuální stav stage do konkrétního commitu. Díky tomu můžu různé drobnější opravy posílat kam to patří.Jak je v článku zmíněno, někdy je ten rebase strašně dlouhej, konflikt za konfliktem. Osvědčilo se mi se na něj vykašlat. Vytvořím si novou branch z požadovaného vrcholu, a pak si z původní branche commity vyzobu po jednom. Mnoha konfliktům se vyhnu, a těch pár co zbude je tak nějak zkousnutelnějších, víc pod kontrolou. Pak přejmenuju.
Merge (bez --no-ff) s oblibou používám k přimergování řady commitů až po nějaký konkrétní (mergovat se dá nejen branch, ale i tag, nebo hash commitu). Například v nějaké featuře se mi začnou hromadit už odladěný commity s obecnější logikou, a tak si řeknu, že je už mohu pustit vejš, a že vlastně nepatří do této featury... A tak.
U nás se push --force do veřejných větví nebojíme, klidně i do masteru. Ano, je zde riziko, že kolegovi něco přemažu, ale prakticky se to nestává (před push vždy zavolat fetch, tím si zkontroluju, a pak už je to orpavdu jen vteřinové riziko). Naopak to používáme v rámci review, aby před schválením byly opraveny všechny připomínky a featura už byla jen tak jak má být.
Zajímavé řešení, každopádně na všech projektech, kde jsem byl byl Push do masteru zakázán. Pro adminy byl sice povolen, ale i pro ně byl force push byl zakázán.
Na posledních 2 projektech navíc byl v Gitlabu i Githubu vynucen rebase a squash. Proto jsem tento článek psal, protože je to řešení, které se na těchto projektech používat muselo.