26. srpna 2014

Mercurial, strategie branch-by-feature


Mercurial je skvělý, distribuovaný Version Control System (VCS, či DVCS), který nabízí velkou míru volnosti, jak s nakládat s verzováním zdrojových kódů. Svobodu většinou chápeme jako pozitivní věc, někdy je ale přílišná nespoutanost na škodu. A tak definování nějaké verzovací strategie prospěje týmu i projektu.

Proč mít verzovací strategii?

Verzovací strategii branch-by-feature jsme s úspěchem použili na stávajícím projektu. Důvody, proč jsme si něco takového definovali byly dva:
  • Když jsem se mihnul na předcházejícím projektu (taky Mercurial), žádná strategie, či konvence definovaná nebyla . Člověk pak slýchal řeči jako: "Proč to furt merguješ?", "Vznikaj ti tam anonymní branche." a "Tam by's měl používat rebase.". Prostě klasický, tohle-tady-všichni-ví a takhle-to-děláme-ale-tobě-jsme-to-neřekli.
  • Chtěli jsme mít na nadcházejícím projektu formalizované code review.

Takže jsme se zamysleli, chvilku nad tím špekulovali a pak jsme, spolu s podporou dalších nástrojů, došli k následující strategii.

Strategie branch-by-feature

Princip téhle strategie je jednoduchý a dá se popsat jednou větou: Pro každou novou feature (nebo bug) se založí nový branch. Hotovo, vymalováno.

A teď trochu obšírněji. Na počátku je v repozitory pouze jeden permanentní branch. Zpravidla se mu říká development branch. Z něj se odlamují další branche pro vývoj - feature branche. Co přesně tyto branche představují, záleží na granularitě položek, do kterých jsme si práci rozdrobili. Může to být user story, feature, requirement, task. Ideálně je to něco, co evidujeme v issue tracking systému (ITS).

Probíhá běžný vývoj a všechny komity jdou do (odpovídajícího) feature branche. Když je vývoj hotový, proběhne code review a pokud jsou změny akceptovány, zamergují se do development branche a feature branch se zavře. Pokud jsou změny zamítnuty, provede se náprava, komitne se do feature branche, následuje code review atd.

Pokud jsou v této fázi nalezeny nějaké bugy, přistupujeme k nim stejně jako k feature. To jest, bug branch -> code review -> merge do development branche.

Verzovací strategie branch by feature

Po nějakém čase vznikne release branch. Pokud jsou v něm nalezeny chyby, vzniká bug branch a po code review přichází merge jak do release, tak do development branche.

Celý postup se dá shrnout do následujících bodů:
  1. Vytvoření nového feature/bug branche.
  2. Development.
  3. Změny projdou code review.
  4. Uzavření feature/bug branche.
  5. (Bugfixy jsou zamergovány do release branche.)
  6. Změny jsou zamergovány do development branche.

Z pohledu příkazové řádky vypadá proces takto:
$ hg branche fb-logging
$ hg commit -m 'Logging feat. has been developed.'
$ hg commit -m 'Close branch fb-logging.' --close-branch
$ hg update default
$ hg merge fb-logging
$ hg commit -m 'Merge branch fb-logging -> default.'

Zcela záměrně se vyhýbám popisu code review. To proto, abych nekradl materiál svému kolegovi Banterovi, který již jistě brzy napíše článek, o tom, jak to děláme (je to fakt cool, tak se těšte). Nicméně, ve vztahu k Mercurialu si nemůžu odpustit, jak to vypadá procesně. Přepokládám, že všichni čtete plynně BPMN ;-)  takže dalších slov netřeba. Mercurial aktivity jsou v oranžovém rámečku.

Code Review proces (oranžové jsou aktivity v Mercurialu, modré v RhodeCode)

Drobná a příjemná vylepšení

Což o to, proces, jako takový, je pěkný. Na někoho je ale možná trochu komplexní. Přece jenom, když mastíte celý život všechno jenom do trunku, může to být trochu moc věcí najednou. Tady je pár věcí, které nám to o něco usnadnili.

Konvence

Konvence jsme měli nastavené jednak pro názvy branchů a jednak pro komitovací komentáře. Název nového branche se skládal z prefixu a čísla issue v ITS. Prefix byl buď fb- (feature branch), nebo bb- (bug branch). Např. fb-42, bb-1024.

Konvence pro komentáře by se daly rozdělit do tří skupin: běžné komentáře, při zavírání branche a při vytváření releasu. Konvence samotná by se v Mercurialu dala pohlídat pomocí hooku, ale nedokopal jsem se k tomu, ho napsat. Pro "běžné", vývojové komentáře jsme měli konvenci WIP #nnn; some comment. WIP je zkratka pro Work In Progress, nnn je id issue v ITS. Např. WIP #1561; Payment button has been removed.

Zavření branche se skládá ze čtyř Mercurial příkazů (commit, update, merge, commit, viz výše) a obsahuje dva komentáře ve formátu:
  • Close branch <branch-name>.
  • Merge branch <branch-name> -> default.

Za zmínku stojí mergovací komentář, ze kterého by mělo být jasné, odkud kam merge proběhnul.

Komentáře při zavírání branche

Releasovací komentáře byly celkem čtyři a obsahovaly klíčové slovo [Release].

Komentáře při vytváření releasu

V předešlém popisu vás možná zarazilo, že jak pro zavření branche, tak pro vytvoření releasu je potřeba spustit čtyři Mercurial příkazy. Dělat to ručně by byla značná otrava a hlavně by celá konvence brzo erodovala do chaosu. Šťastni kdož používají automatizaci, neboť jen ti vejdou do křemíkového nebe.

Mercurial alias

S používáním aliasu přišel kolega Banter, takže mu za to patří dík a rád ho zde zvěčním. Alias samotný vypadá dost ošklivě, zato jeho použití je elegantní. Jedinou vadou na kráse je, že jsme nepřišli, jak alias spustit z grafických nástojů, jako je SourceTree, nebo TortoiseHg, takže ještě budeme muset zapracovat na tom, aby tuto fičurku mohli používat i kolegové, kteří ještě nepřišli na to, jak cool je používat command line ;-)

Alias stačí přidat do souboru ~/.hgrc a spouští se jednoduchým příkazem hg close-feature nnn, kde nnn je (v našem případě) čístlo issue v ITS, tj. to, co je za prefixem fb-.
[alias]
close-feature = ![ -z "$1" ] && echo "You didn't specify a branch!" && exit 1; \
                if hg branches | grep -q "fb-$1"; \
                    then $HG up fb-$1; \
                         $HG commit -m 'Close branch fb-$1.' --close-branch; \
                         $HG pull; \
                         $HG up default; \
                         $HG merge fb-$1; \
                         $HG commit -m 'Merge branch fb-$1 -> default.'; \
                    else echo "The branch fb-$1 does NOT exist!"; \
                fi

Gradle release task

Náš projekt buildujema Mavenem. Chvilku jsem se snažil napasovat Maven Release plugin na naši branchovací strategii, ale pořád to nějak nebylo ono - je to prostě moc šitý na SVN. Pak jsem ztratil trpělivost a odpovídající chování si napsal jako Gradle task. Časem se z toho snad vyklube Gradle plugin.

Task samotný tady uvádět nebudu, protože je asi tak na tři stránky. A taky potřebuje ještě trochu poladit. Zkrátka ještě není zralý na to, abych ho dal z ruky. V podstatě ale dělá pouze to, že přepíná branche, šolichá s Mercurialem a manipuluje s verzí v pom.xml. A dělá to ty hezké komentáře, co jsem uváděl výše.

Pros & Cons

Celý výše popsaný koncept jsme vymysleli ještě před projektem. Pak přišel projekt a emotivně musím říct, že nám to krásně fungovalo. Jako hlavní benefit vidím, že jsme si formalizovali proces code review a s výše uvedenou branchovací strategií to bylo velice efektivní. Dalším plusem byla, "úhledná" práce s Mercurialem, kdy byla radost se prohrabávat verzovanou historií.

K negativům bych zařadil komplexnost. Pokud by nám nešlo o code review, asi by tato strategie byla zbytečná. Zatím mi taky chybí nějaká podpora v grafických a automatizačních nástrojích. Jako milovníkovi CLI mi to osobně nijak nevadí, ale musím myslet taky na ostatní členy týmu (bývalý kolega jim říkal "makáči" :-)  pro které by mělo být používání nástrojů co nejsnadnější.

Je to všechno?

Abyste dostali plný obrázek, jak to celé fungovalo, chybí nám jeden velký dílek skládanky - jo, code review. Takže pokud vám něco nedává smysl, tak je to možná proto, že jsem v tomto článku celkem důsledně odpáral použití dalších dvou nástrojů, které byly s branchovací strategii organicky provázané - issue tracking system a RhodeCode, nástroj, ve kterém jsme, pomocí pull requestů, řešili code review.

Popis code review je na samostatný článek. Tak snad ho Banter brzo napíše a já sem pak lísknu odkaz. A jinak doufám, že vám zmiňovaná strategie branch-by-feature přijde inspirativní.

Happy branching! :-)

Mind Map



Související články

4 komentáře:

  1. Už toho na mě moc nezbylo :) O tom, co je to code review a proč ho dělat už jsem psal. Ještě zbývá vysvětlit, jak se používá RhodeCode a proč jsme si ho vůbec vybrali. Rovněž bude stát za to popsat, co přináší code review do programátorova běžného pracovního dne.

    OdpovědětVymazat
  2. Nechapu proc kazdy si timhle musi projit - vymyslet jakou strategii pouzivat - kdyz uz je dlouho k dispozici github flow, ktery je pro vetsinou projektu naprosto dostacujici.

    OdpovědětVymazat
    Odpovědi
    1. Trochu mi to zní jako one-size-fits-all. Relevantních důvodů, proč vymýšlet, nebo adaptovat verzovací strategii může být mnoho. Článek popisuje konkrétní implementaci, která vycházela z daných nástrojů a prostředí. V jiném kontextu by to mohlo dopadnout jinak.

      Vymazat
  3. (Mam zkusenosti jen s gitem, ale predpokladam, ze nize uvedeny problem je stejny i v mercurialu.)

    Moc by se mi libilo, kdyby se slo zeptat "git branch --contains bb-1234" a dostal bych vzdy spravnou odpoved. Vidim tam ale jeden problem.

    Mozna mi neco unika, ale stale hledam odpoved na otazku "z ktere branch vytvorit bug/feature branch". Ma prozatimni odpoved je "z nejstarsi release branch, ve ktere bude bug opraven". To proto, aby hezky fungoval merge.

    Problem ale nastava v okamziku, kdy:
    1) Bug je fixly ve verzi 2.0 (tzn bb-1234 vznikla z 2.0 a byla do ni zpet mergnuta)
    2) Az pote se rozhodne, ze je potreba fix dat i do 1.0 (zakaznik, nas pan)

    Co ted? Pokud bych se pokusil o merge bug branche do 1.0, tak mi s sebou vezme i vse z 2.0, coz nechci. Takze zbyva cherry-pick, ale pak uz nikdy nebude fungovat "git branch --contains" tak, jak bych chtel.

    Jak tento "problem" resite vy popripade mercurial?

    OdpovědětVymazat