2011. május 8., vasárnap

HgEclipse - Mercurial az Eclipse-ben

Nemrégiben írtam egy rövid bejegyzést a Mercurial elosztott verziókezelő rendszerről. Ott a TortoiseHg-n keresztül mutattam be a rendszer lehetőségeit dióhéjban. Mivel azonban elsősorban a fejlesztésekhez használok verziókezelő rendszereket, szükségem volt egy Eclipse pluginre. Nagyon rövid keresés után rá is találtam a HgEclipse kiegészítőre. A kiegészítő telepítése nagyon egyszerű: a szokásos módon a honlapon található update URL-t kell megadni az eclipse-nek, ahonnan a rendszer lehúzza a kiegészítőt, és folyamatosan követi is annak frissítéseit. A HgEclipse nagyon jól integrálódik az Eclipse általános verziókezelő moduljába. Tehát a funkciókat a Team menüpont alatt fogjuk megtalálni, itt hozhatunk létre tárolót, innen commit-olhatunk, stb. Az egész annyira kézre áll, hogy ha lehet ilyet mondani, még kényelmesebb is a használata, mint a TortoiseHg-nek (pedig ahhoz sem kell atomfizikus diploma).  Nézzük is meg dióhéjban, hogy működik.

Első körben hozzunk létre egy Java projektet, mondjuk MercurialTest néven. Eztán a szokásos jobb gomb -> Team/Share project .../Mercurial módszerrel hozzuk is létre a tárolót. Ahogyan az előző bejegyzésben már írtam, maga a tároló nem több, mint néhány speciális rejtett fájl a verziókezelt mappában, így nincs más dolgunk, mint nyomni egy finish-t, és kész is a tároló. Nem kell megadnunk szervert, stb. Ha megvagyunk, hozzunk is létre egy állományt, mondjuk egy szokásos HelloWorld.java programot. Ha ez is megvan, akkor az SVN-ből már megszokott Team/Commit módszerrel rögzíthetjük a változásokat. Ilyenkor kell adnunk valami rövid leírást az adott verzióhoz (igen, kell, nem lehet elhagyni, ami nem is feltétlen baj), kiválasztjuk a fájlokat, és már kész is a verzió. Tehát a dolog ugyanúgy megy, mint SVN esetén, csak nem kell hozzá szerver. E helyett minden a projekt könyvtárában lévő lokális tárolóba kerül. A Team/Show history-val láthatjuk az egyes verziókban történ változásokat, összehasonlíthatjuk, hogy hol mi változott, tehát mindent megtehetünk, amit SVN-ben. Ahogyan az előző bejegyzésben már írtam, az elosztott verziókezelőknél a fejlesztő a lokális tárolóba dolgozik mindaddig, amíg nem végez egy feladattal. Ha a feladat elkészült (letesztelte, jól működik a programrész, stb.), csak akkor tolja fel a változásokat a központi tárolóba. Ezt a Team/Push... menüpont segítségével tehetjük meg. Itt értelem szerűen megadhatjuk a távoli tároló címét, ahová betoljuk a változásokat. Ez felel meg kb. az SVN commit-nak. (Mivel a Mercurial lokális tárolója valójában néhány fájlból áll, ezért elvileg megtehető az is, hogy valaki Mercurial-t használ lokális verziókezelésre, majd ha elkészült, a változásokat a régi SVN tárolójába tolja be. Ilyesmivel nem próbálkoztam, és nem is tudom mennyire lehet hatékony, de ha valaki nagyon ragaszkodik az SVN-hez, az próbálkozhat ilyesmivel. Így esetleg fájdalommentesebb lehet az átállás egyik rendszerről a másikra.) Próbaképp készítsünk néhány lokális változatot, majd térjünk rá az elosztott verziókezelés egyik legnagyobb előnyére, és klónozzunk.

A klónozás kicsit trükkös Eclipse-ben, ugyanis nem a Team, hanem az Import menüpontban találjuk a Mercurial/Clone repository menüpont alatt (ami utólag végiggondolva végül is logikus). A klónozásra kiválaszthatunk távoli tárolót (ez felel meg kb. az SVN checkout-nak), vagy lokális tárolót. Próbaképp válasszuk ki a fájlrendszerből az eredeti (azóta Mercurial tárolóvá tett) eclipse projektet. Ha elkészült a klón, itt is csináljunk néhány változatot, és az eredeti projektben is commit-oljunk néhányat, lehetőleg úgy, hogy a kétféle változtatás üsse egymást (így tesztelhetjük majd a merge-öt). Ha készen vagyunk, próbáljuk visszafésülni az eredeti projektbe az újonnan létrehozott változatokat. Ehhez az eredeti tárolón használjuk a Team/Pull menüpontot. (Az előző bejegyzésben már írtam arról, hogy a változásokat vagy tolni [push], vagy húzni [pull] lehet egyik tárolóból a másikba.) Itt válasszuk ki a második lokális tárolót (természetesen használhatnánk távoli tárolót is), és húzzuk át a változásokat. Ekkor az eredeti tárolóban létrejön egy másik ág, és a HgEclipse rögtön rá is kérdez, hogy összefésülje-e a két ágat. Ezt ugye érdemes megtenni, úgyhogy válasszuk a merg-öt. Ha jól dolgoztunk, a rendszer nem fogja tudni simán összefésülni a két ágat, és néhány állomány conflict-es lesz. Ez ismerős lehet SVN-ből is, hiszen ott is előfordulhat, hogy a szerveren lévő változatot nem lehet automatikusan összefésülni a lokális változattal. Mondjuk az SVN ilyenkor televágja minden krix-krax-al a fájlt, és létrehoz egy állományt a lokális, egyet pedig a szerveren lévő változatnak. A Mercurial ennél szebben jár el, hiszen ott csak egy kis jelet látunk a fájl mellet, hogy conflict van, de nem rontja el az eredeti tartalmat, alul pedig megjelenik egy Mercurial Merge ablak, ahol látszik a conflict-es fájlok listája. Itt ha jobb gombbal rákattintunk egy állományra, a legelső menüpont az Open in Merge Editor. Itt szépen egymás mellett látjuk a két változatot, és kézzel kijavíthatjuk a hibákat. Elég kényelmes kis eszköz. Ha készen vagyunk a kézi merge-öléssel, újra nyomjunk jobb gombbal a fájlra, és az SVN-ből már megszokott "mark resolved" menüponttal fogadjuk el a változásokat. Ha megvagyunk a javításokkal, az eredményt kitolhatjuk a szerveren tárolt közös tárolóba, stb. Dióhéjban körülbelül ennyi lenne.

Véleményem szerint a HgEclipse egy nagyon jól sikerült kis eszköz, és önmagában a HgEclipse is, és a Mercurial mint verziókezelő is jóval többet ad nekünk, mint a hagyományos SVN tárolók. Az én jóslatom, hogy egyre többen fogják felismerni az ebben rejlő lehetőségeket, és néhány éven belül mindenhol át fognak térni valamilyen elosztott verziókezelő (Git, Mercurial, stb.) használatára.

2011. május 7., szombat

Mercurial - első tapasztalatok egy elosztott verziókezelővel

Már régóta szemezek az elosztott verzió kezelő rendszerekkel. Egyfelől azért, mert úgy látom ez a trend (néhány év múlva az elosztott verzió kezelők fogják átvenni az  SVN helyét, úgy ahogyan annak idején az SVN került az akkoriban elterjedt CVS helyére), másfelől magam is szembesültem már az SVN néhány hátrányával, amiken az elosztott verziókezelő rendszerek felülemelkednek. No de mi is az az elosztott verzió kezelő rendszer?

Feltételezem, hogy az olvasó találkozott már  központi (centralizált) verziókezelő rendszerrel. Ilyen például a hagyományos SVN, vagy a CVS. Központi verziókezelő esetén ugye van egy központi szerver (pl. SVN szerver), a felhasználók pedig innen húzzák le, illetve ide tolják fel a saját változtatásaikat. Minden feltöltést egy letöltésnek kell megelőznie, ami az adott felhasználó gépén összefésüli a szerveren lévő és a lokális változatot. Ha az összefésülés automatikusan megoldható (általában megoldható), akkor azt a rendszer el is végzi, ha nem, akkor kézzel kell összefésülnünk a változatokat, majd az eredményt feltöltjük, és onnantól az lesz az aktuális változat. Az egyes változatokat vissza lehet keresni, egy egy állományt vissza lehet állítani a régi állapotra, és változatonként pontosan nyomon követhetjük, hogy hol mi változott. Nagyon röviden valami ilyesmiről szól egy központi verziókezelő.

Elosztott (decentralizált) verziókezelő esetén más a helyzet. Ott egyetlen központi tároló (repository) helyett minden felhasználónak van egy saját külön tárolója a gépén. A külön tárolónak külön verziókezelése van, tehát nem kell minden egyes változást a központi szerverre tölteni. Itt rögtön látszik is az elosztott verziókezelők egyik nagy előnye az SVN-el szemben. SVN esetén ha valami nagy feladaton dolgozunk, és a feladat megoldása közben menteni szeretnénk egy egy mérföldkövet, azt csak az egyetlen központi tárolóba menthetjük. Így választhatunk, hogy vagy félkész dolgokat mentegetünk a központi tárolóba (ezzel esetleg működésképtelen vagy hibás verziókat hozva létre), vagy mindaddig nem készítünk verziót, amíg el nem készültünk a feladattal (így viszont a feladat ideje alatt elveszítjük a verziókezelésből eredő előnyöket). Mindkét megoldás rossz, de mindenki saját szája íze szerint választhat, hogy melyik a kisebbik. Elosztott verziókezelés esetén nincs ilyen gond, hiszen a saját lokális tárolójába mindenki olyan verziókat hoz létre, amilyet akar (lehetnek ezek között működésképtelen, félkész állapotok is), és csak akkor tölti fel az egészet a központi tárolóba, ha kész van a feladattal (esetleg le is tesztelte, stb.). Így a központi tárolóban mindig egy stabil, használható változat van. Ez már önmagában nagy előny, de mint látni fogjuk, ez csak egyetlen a sok közül. Tehát egy központi tároló helyett itt több tároló van, és mivel nincs is olyan fogalom, hogy "központi" tároló, ezekből a lokális tárolókból tetszőleges hierarchiák kialakíthatóak. Tehát pl. egy projekt 2 nagyobb feladatra osztható, és mindkét feladaton dolgozik 2-2 fejlesztő. Ebben az esetben a két fejlesztőnek van 1-1 saját tárolója, ezekből mindig az adott feladat tárolójába szinkronizálnak, és ha az adott feladat elkészül, akkor tolják ki az egészet a "központi", stabil tárolóba. De hasonló módon akár az is megoldható, hogy 2 "központi" tárolót tartunk fenn. Az első egy "testing", a második egy "stabil" változat. Ha elkészül egy feladat, akkor az a "testing" rendszerbe kerül. Itt valaki leteszteli, és ha megfelelőnek találja, kihelyezi a stabil tárolóba. Ez csak néhány példa a teljesség igénye nélkül, de sok más elrendezés is elképzelhető. Például ami nekem nagyon tetszett, hogy nyílt forrású projektek esetén ha a forráskód tárolására SVN-t használnak, akkor két módon lehet beszállni a fejlesztésbe. Az egyik, hogy elérési jogot kérünk a tárolóhoz. Itt ugye az a dolog veszélye, hogy akinek ilyen jogot adunk, abban nagyon meg kell bízni, hisz kontrollálatlanul telepakolhatja a kódot mindenfélével. A másik eset, hogy a felhasználó elvégzi a módosítást, majd generál egy patch-t, amit elküldhet a projekt vezetőjének, aki átnézi azt, és kézzel befésüli az SVN-be. Ez viszont elég kényelmetlen. Elosztott verziókezelő esetén a dolog igen gördülékenyen megy, mivel a felhasználó csinál egy másolatot (klónt) a központi kódbázisról, elvégzi a változtatásokat (itt használhatja a lokális változatának saját verziókezelését), majd ha elkészült, értesíti a projekt gazdáját, hogy húzza át a módosítást a fő kódbázisba. Ez tulajdonképpen olyan mint mikor az ember patch-et küld egy SVN-es projekthez, de sokkal egyszerűbb, és gördülékenyebb.

A legismertebb ilyen elosztott verziókezelő a Git. Ezt a linux kernel fejlesztéséhez hozták létre, de mostanra már sokan mások is használják. Git-ben tárolják pl. az Android forráskódját, a Diaspora-t, az Appcelerator forráskódját, a Facebook is ezt használja a nyílt forrású projektjeihez, és még több ezer projekt, amiket jellemzően a GitHub-on hosztolnak. (A GitHub egy Git alapú code hosting szolgáltatás, olyan mint a Google Code Hosting, vagy a Sourceforge, bár sok szempontból sokkal fejlettebb.) A Git után szorosan második helyen áll a Mercurial. Nem kell szégyenkeznia a Git mellett, hisz olyan projektek használják, mint a Mozilla böngésző, Python programozási nyelv, OpenJDK, Netbeans, stb. Ráadásul a Google Code Hosting-on is használhatjuk az SVN alternatívájaként. Valójában nekem is szimpatikusabb a Mercurial, mint a Git, mivel a Git egy scriptekből és programokból összelapátolt valami, míg a Mercurial tulajdonképpen egy Python program, így ha kell, ebbe jobban bele lehet nyúlni (pl. plugint fejleszteni hozzá), ráadásul az egész valahogy jobban egyben van. Van még néhány elosztott verziókezelő (pl. Bazaar, amit az Ubuntu Linux használ), de mind közül nekem a Mercurial tetszett legjobban, így erről fogok írni.

Akit mélyebben érdekel a Mercurial, a http://hginit.com/ címen találhat egy nagyon jó angol nyelvű összefoglalót, de én is írok róla néhány sort.  Ha ki szeretnénk próbálni ennek a verziókezelő rendszernek a képességeit, legegyszerűbb, ha letöltjük a TortoiseHg klienst. Ez beépül a Windows jobb gombos menüjébe, így a fájlkezelőben könnyen adminisztrálgathatjuk a tárolóinkat. A kísérletezéshez hozzunk létre egy tárolót. Ehhez a TortoiseHg-ban van egy "Create repository here" menüpont. Az első dolog, amit észrevehetünk, hogy maga a tároló egy egyszerű könyvtár néhány speciális fájlal, tehát alapvetően nincs szükség szerverre a működéshez. Ez már önmagában is hasznos lehet. Ha kész a tároló, hozzunk is létre benne néhány fájlt. Eztán ha a tároló könyvtárán jobb gombot nyomunk, feltűnik az ismerős commit parancs. Ezzel az SVN-hez hasonlóan rögzíthetjük a változásokat, és létrehozhatunk egy verziót, ami az SVN-el ellentétben nem egy szerveren, hanem a könyvtárban lévő egyik mappában jön létre. Készítsünk még 1-2 verziót. Van egy Hg Workbench menüpont is, amivel szépen fában látjuk a változatokat, megnézhetjük a különbségeket, stb. Eztán jöhet a trükk, ami az elosztott verziókezelők legnagyobb előnye.  A menüben találunk egy "Clone" parancsot. Ezzel a teljes tárolót lemásolhatjuk még egy példányban, verzió történettel, mindennel együtt. A clone után mér két tárolónk van (két sima könyvtár néhány speciális fájlal és mappával). Most csináljunk néhány verziót az egyik, majd néhány verziót a másik tárolóban. Ez jelképezi a több fejlesztési ágat. A gyakorlatban ez úgy néz ki, hogy minden egyes fejlesztő klónozza magának a tárolót, és abba dolgozik. Szóval csináljunk néhány verziót mindkét tárolóban. Ha ezzel megvagyunk, akkor jön az, hogy a másodlagos (pl. fejlesztési) tárolót fésüljük vissza az elsődleges tárolóba. Ezt két módon tehetjük meg. Vagy a cél tárolóba húzzuk (pull) a változásokat a forrás tárolóból, vagy a forrás tárolóból nyomjuk (push) a cél tárolóba a dolgokat. Mindkét módozatnak van értelme. A push-ra jó példa, amikor mondjuk egy fejlesztő befejezett egy feladatot a saját lokális tárolójában, és ezt betolja a központi fejlesztői tárolóba (kb. az SVN commitnak felel meg). A pull használata pedig például akkor lehet hasznos, ha a stabil szál felelőse áthúzza a változásokat a fejlesztői szálból. Például GitHubon (ez Git alapú, de a mechanizmus ugyanaz) úgy szállhatunk be egy nyílt forrású projekt fejlesztésébe, hogy klónozzuk a projekt tárolóját, megcsináljuk a változtatásokat (ehhez semmilyen interakció nem kell ugye a projekt gazdájával), majd ha megvagyunk, küldünk egy pull request-et (ez kb. egy üzenet, hogy "kérlek, húzd át a változásokat az én tárolómból") a projekt gazdájának, aki átnézi, hogy mit változtattunk, és ha mindent rendben talál, áthúzza a változásokat a projekt fő tárolójába. Ez nagyon gördülékeny módja a nyílt forrású projekthez való hozzájárulásnak, sokkal kényelmesebb, mint SVN hozzáférést kunyerálni, vagy patch-eket küldeni. A példa kedvéért tehát nyissuk meg az elsődleges tárolónkat a Hg Workbench-ben, és húzzuk át a változásokat a másodlagosból. Ha ez megvan, láthatjuk a Workbench-ben, hogy a másodlagos tároló történettel, mindennel együtt egy új ágként megjelent a mi tárolónk történetében (magyarul látszik, hogy ott ketté vált a fa). Ez már majdnem jó, már csak egyesíteni kell a két ágat. Ehhez van a workbench-ben egy merge művelet. Ez igazából ugyanúgy megy, mint SVN-ben a merge-ölés, ha lehet, automatikusan merge-öl a rendszer, ha nem, akkor kézzel kell ezt megtenni. (Elvileg a Mercurial mindenféle metaadatot gyűjt a merge segítésére, de ezt a részét annyira nem próbáltam.) Ha megvan a Merge, jöhet egy commit, és rögtön egy szálon megy tovább az egész. Tehát ha a fejlesztés végén ránézünk a fára, lépten-nyomon kis elágazásokat és visszacsatolásokat fogunk látni, amik az egyes fejlesztések.

Úgy nagyon dióhéjban ennyi lenne a Mercurial bemutatása. Feltűnhetett, hogy minden műveletet a fájlrendszerben végeztünk, de ahogyan SVN esetén, természetesen itt is megvan a lehetősége annak, hogy egy repository-t egy szerveren tároljunk, amit HTTP-n, HTTPS-en, vagy SSH-n keresztül lehet elérni. A dolgok mechanizmusán ez nem sokat változtat, egyszerűen az ilyen tároló nem a fájlrendszerben van, hanem egy távoli szerveren, de ugyanúgy lehet klónozni, lehúzni (pull) róla a változásokat (ami az SVN update-hez hasonló), vagy felnyomni (push) oda a változásokat (ami az SVN commit-hoz hasonló). Célszerűen csak a fejlesztő lokális tárolóját érdemes a fájlrendszerben tartani, minden olyan tárolót, amit többen használnak (közös fejlesztési ág, stabil ág, stb.), szerveren érdemes tartani.

Remélem ezzel a kis ismertetővel sikerült kicsit kedvet csinálni az elosztott verzió kezelő rendszerek, azon belül is a Mercurial használatához.