Proč jsem přešel z Mercurialu na Git

Delší dobu jsem jako hlavní verzovací systém pro své projekty vcelku spokojeně používal Mercurial. Před týdnem jsme se ale po dlouhém rozmýšlení rozhodl přejít na Git, který jsem do té doby považoval (a stále považuju) v mnohých směrech za horší. V tomto článku bych rád popsal, jak se vyvíjely mé úvahy a co mě přimělo ke změně. U čtenářů budu přitom předpokládat porozumění základním principům distribuovaných verzovacích systémů.

Mercurial a Git

Mercurial a Git jsou oba distribuované verzovací systémy založené na myšlence orientovaného acyklického grafu změn a adresaci těchto změn pomocí hashů. Oba vznikly přibližně ve stejné době (jaro 2005), ze stejného důvodu (potřeba vyřešit verzování linuxového kernelu) a celkově u nich najdeme na úrovni koncepce i ovládání víc společných vlastností než rozdílů.

Mercurial je nicméně o něco jednodušší, menší, konzistentnější, elegantnější, a rozšiřitelnější. Je na něm celkově vidět, že nevznikl tolik chaotickým vývojem jako Git, ale že jeho tvůrci při přidávání jednotlivých vlastností mysleli i na celek. Na povrch se to projevuje velice přehledným a logickým ovládáním (myšleno z příkazové řádky) a srozumitelnou nápovědou – ani jedním z toho Git rozhodně nedisponuje. (Pokud vás zajímá detailnější srovnání obou systémů, zabruste k Davidovi Grudlovi nebo se podívejte na video od Scotta Chacona.)

Z těchto důvodů jsem se rozhodl používat Mercurial jako primární verzovací systém pro své projekty. To mi vydrželo do té doby, než jsem začal letos v lednu pracovat na SUSE Studiu (které tvoří tým asi 18 lidí ve velmi rychlém tempu vývoje a které je spravováno Gitem) a pár týdnů na to vydal PEG.js. Na prvním projektu jsem poznal, co je to opravdu distribuovaný vývoj, a na druhém jsem si pořádně uvědomil, že volba verzovacího systému není jen volbou technickou a osobní, ale i sociální. Z obou stránek se Mercurial ukázal jako horší systém.

Shrnuto v bodech, mé důvody k přechodu na Git byly následující:

Pojďme si tyto důvody podrobněji rozebrat.

Důvod první: Lepší podpora rozvětveného vývoje v Gitu

Dobrá podpora rozvětveného vývoje je jedním z nejdůležitějších aspektů verzovacího systému. Dost možná tím vůbec nejdůležitějším. Jakýmsi prubířským kamenem této funkcionality se pro mě stal následující příklad.

Ultimátní VCS use case

Mějme tým několika vývojářů pracujících na společném projektu. Jeden z nich dostane za úkol vyvinout větší funkci (tj. rozprostírající se přes několik commitů). Kód si chce začít psát u sebe jako sadu změn oproti hlavní vývojové větvi, která nebude zatím nikde zveřejněna. Je možné, že během práce zjistí, že na to jde špatně, a bude potřebovat sadu změn zahodit a začít znovu odjinud. Během práce si bude také často potřebovat odskočit zpět k hlavní vývojové větví, např. kvůli napsání nějaké opravy. V neposlední řadě bude občas potřebovat aktualizovat svou sadu změn, aby odpovídala aktuálnímu kódu v hlavní vývojové větvi.

Až vývojář svou funkci dostane do rozumného stavu, bude chtít vytvořenou sadu změn sdílet s ostatními, aby mohli jeho úpravy zrevidovat, okomentovat atd. Výsledkem bude pravděpodobně několik dalších změn. Nakonec bude celá funkce dokončena, začleněna do hlavní vývojové větve a následně nasazena.

Řešení v Mercurialu

Jak si s tímto příkladem poradí Mercurial? Ukazuje se, že je na něj možné jít čtyřmi různými způsoby, z nichž ale ani jeden není ideální.

Větve

Mercurial má vestavěnou podporu větví (branches), se kterou by bylo možné příklad zvládnout, nebýt jedné důležité věci: jakmile je větev jednou vytvořena, nejde ji přejmenovat ani smazat (protože název větve je uložen v commitech). To je v souladu s celkovou filozofií Mercurialu, že co je v repozitáři, to je vytesáno do kamene, ale brání to efektivně používat větve Mercurialu pro menší funkce a věci, u kterých si nejsme jisti, kde nakonec skončí a jestli je úplně nepřepíšeme. V tom případě je totiž nejspíš nebudeme chtít mít v repozitáři na trvalo.

I kdyby permenentnost kódu nevadila ("Tak mám v repozitáři nepoužitý kód, no a co?"), nemožnost mazání a přejmenování větví povede k názvům ve stylu "experimental-feature-try1", "experimental-feature-try2", v čemž se snadno ztratí orientace. Mercurial nám navíc znepříjemňuje práci s repozitářem, ve kterém máme větve, které nechceme sdílet – nefunguje obyčejný hg push a je třeba explicitně říct, kterou větev chceme do cílového repozitáře protlačit. Je to sice drobnost, ale otravná.

Jak je vidět, větve nejsou pro jako řešení našeho příkladu úplně vhodné.

Klon repozitáře

Funkci z příkladu by bylo možné vyvíjet v naklonovaném repozitáři. Tím se vyřeší problémy s experimenty a neměnností repozitáře (pokud chci kód zahodit, smažu klon). Pro sdílení s ostatními stačí klon nahrát na vývojový server.

Problémem se ale stává nutnost přepínání mezi původním repozitářem a klonem. Znamená to např. zavírání otevřeného editoru, v případě webových aplikací restart serveru či dokonce přepisování jeho konfigurace. Tyto problémy lze částečně vyřešit třeba symbolickými odkazy na repozitáře, ale i tak není přepínání mezi více repozitáři příliš pohodlné, zvlášť pokud ho potřebujeme dělat poměrně často. Klonování tedy ideální cesta není.

Bookmarks

Neflexibilitu větví se snaží obejít rozšíření Bookmarks. To definuje koncept záložky, což je jen pojmenování nějakého commitu. Záložky se při změnách automaticky posouvají dopředu, takže fungují podobně jako větve. Jsou ale uloženy mimo repozitář a tudíž nejsou neměnné.

Z uložení záložek mimo repozitář plyne jejich hlavní problém – obtížné sdílení. Není je totiž možné sdílet prostým hg push jako ostatní kód. To způsobí problémy ve chvíli, kdy vývojář bude chtít umožnit ostatním se na kód podívat a případně do něj zasáhnout. Po každé modifikaci bude muset mimo Mercurial zpřístupnit aktualizovaný seznam záložek, což je činnost otravná (jak pro něj, tak pro ostatní) a náchylná k chybám. Záložky se tedy na náš úkol také úplně nehodí.

Mercurial queues

Poslední možností, jak příklad vyřešit, je požít rozšíření Mercurial queues. Změny nebudeme ukládat do repozitáře, ale uložíme si je stranou jako sadu klasických patchů oproti aktuální vývojové větvi. Mercurial queues nám to umožní dělat relativně efektivně a zároveň přimějí Mercurial koukat na uložené patche jako na normální commity. Až budeme chtít změny nasdílet, prostě patche někde vystavíme a při změnách je budeme upravovat či doplníme nové.

S Mercurial queues by šel náš příklad vcelku bez problémů zvládnout. Jedná se ale de facto o další nástroj pro práci se změnami v kódu – se svou vlastní filozofií a samostatnými příkazy. Iluze, že jsou patche normální commity, je jen velmi povrchní a má mnoho děr. Celý systém je v podstatě jeden velký hack okolo toho, že repozitáře v Mercurialu jsou neměnné.

Shrnutí

Jak je vidět, s Mercurialem uvedený příklad sice vyřešíme, nicméně stylem "jde to, ale dře to". Žádný způsob řešení není úplně ideální a elegantní. To je ve výrazném kontrastu s elegancí Mercurialu jako celku.

Při hlubším zamyšlení člověk přijde na to, že za všechny problémy v zásadě může chápání repozitáře jako neměnného – nebýt toho, náš příklad by šel nejspíš vyřešit poměrně snadno. Bohužel toto chápání je základ filozofie Mercurialu a podle mě je to jeho největší slabina na koncepční úrovni. Dá se do značné míry různými způsoby obejít, ale není to ono.

Řešení v Gitu

Na rozdíl od Mercurialu Git náš příklad zvládne levou zadní svými větvemi (branches). Na začátku úkolu si vytvoříme soukromou větev (topic branch), ve které pracujeme. Tu můžeme kdykoliv zahodit a začít znovu, můžeme ji i různě upravovat (přehazovat commity, slučovat je apod.) a měnit bod, ze kterého vychází (git rebase). Až budou změny připraveny ke zveřejnění, není problém je (stále jako větev) nahrát do centrálního repozitáře a dál na nich pracovat. Začlenění zpět do vývojového stromu je také bezproblémové.

Celý proces je tedy oproti Mercurialu naprosto hladký. Jedinou nevýhodou je, že příkazy pro některé operace jsou poněkud kryptické a manuálové stránky k nim naprosto nečitelné. Zde nás ale snadno zachrání buď dobrá knížka nebo Google. Zůstává jen mírná pachuť z páchání magie, které pořádně nerozumíme.

Důvod druhý: Větší rozšířenost Gitu

Podstatná výhoda Gitu proti Mercurialu je množství jím spravovaných projektů a tedy i větší množství uživatelů. Důsledek je, že například u open source projektů je použití Gitu v podstatě očekáváno. Pokud svůj projekt budete spravovat v něčem jiném, ubližujete mu tím, protože jisté množství potenciálních uživatelů či přispěvatelů prostě nebude ochotno se kvůli vašemu projektu učit nový verzovací systém. U Mercurialu tento problém není až tak zásadní, protože je stále relativně dost rozšířený a navíc je Gitu podobný. Nicméně významný faktor to stále je.

U rozšířenějšího systému obecně bývá výhoda i v množství podpůrných nástrojů, snadnějšího řešení problémů (je pravděpodobnější, že s ním nejste první) atd. Zde ale musím říct, že mezi Gitem a Mercurialem nespatřuju významné rozdíly – oba jsou rozšířené dost na to, aby jejich ekosystém fungoval vcelku dobře.

Důvod třetí: GitHub

Důležitým faktorem, který hrál roli při rozšiřování Gitu v posledních dvou letech, je existence GitHubu. Společná kolaborace více lidí na kódu se díky němu stala snadnou záležitostí, což mělo převratné důsledky na množství a rychlost vývoje zejména menších open source projektů (detailní rozbor by byl na samostatný článek). Dá se říct, že na GitHubu má dnes účet a několik projektů skoro každý a existuje okolo něj silná vývojářská komunita.

Mercurial má svůj BitBucket, který je do značné míry (nedokonalou) kopií GitHubu. Klíčová věc – komunita – mu ale chybí. Hezky jsem to mohl pozorovat na svém projektu PEG.js, který byl původně zveřejněn na BitBucketu a cca před týdnem jsem ho přesunul na GitHub. Zatímco na BitBucketu mu trvalo několik týdnů nasbírat 4 followery (lidí, co projekt sledují na úrovni commitů) a jeden fork (kopie repozitáře za účelem modifikací), na GitHubu se totéž podařilo asi za dva dny. A to měl BitBucket výhodu úvodního zveřejnění – přesun na GitHub jsem už jen tiše oznámil v mailing listu. A ne, nebylo to přechodem followerů z BitBucketu an GitHub.

Velikost komunity je klíčová pro jakýkoliv open source projekt, protože znamená větší pravděpodobnost, že se o něj začne někdo zajímat, bude ho testovat či do něj dokonce přispěje kódem. Pokud toto chcete (proč byste nechtěli?), je dnes GitHub jednoznačně nejlepší místem, kam projekt umístit.

Závěr

Výběr verzovacího systému je složitá záležitost. Já jsem svou volbu učinil před víc než rokem a nyní jsem rozhodnutí změnil. Nevnímám to jako chybu v původním úsudku, ale jako posun ve vývoji a přiznání si, že na některých vlastnostech (elegance, konzistence uživatelského rozhraní) záleží zřejmě méně, než bych si přál. Flexibilita Gitu při distribuovaném vývoji některé povrchnější výhody Mercurialu zkrátka trumfne. Navíc je Git rozšířenější a má za sebou silnější komunitu, na čemž záleží nejspíš ještě víc než na technických a uživatelských aspektech.

Dodal bych ještě, že moje kritéria pro rozhodování jsou pochopitelně velmi subjektivní a neúplná. Při zvážení nasazení verzovacího systému např. ve firmě je potřeba přemýšlet šířeji, než jak jsem činil já. Nicméně i tak se domnívám, že moje cesta a její závěry můžou někomu při rozhodování pomoci.