Hlavní navigace

Výlet do říše verzí: ideální verzovací systém?

3. 5. 2004
Doba čtení: 9 minut

Sdílet

Toto pondělí si na vás přichystalo tentokrát mírně teoretickou diskusi o tom, co by měl všechno umět takový ideální verzovací systém a jak by to asi tak měl umět.

CVS is not the answer.
CVS is the question.
‚NO‘ is the answer…
 – Theodore Ts'o 

V průběhu našich nepravidelných toulek CVS jsme narazili na mnoho otravných omezení. Další nepříjemnosti vás čekají při jeho jakémkoliv netriviálním používání, zatímco jiné chybějící věci vám nebudou scházet, dokud si nevyzkoušíte, jak úžasné je to s nimi.

Pojďme se teď trochu povznést nad všechny existující versioning systémy. Představme si ideální systém – ideální? V této chvíli již musíme uvažovat i cílovou skupinu, poněvadž každý druh projektu bude mít trochu jiný modus operandi svých vývojářů. Zaměříme se tedy na opensource software, a protože malým projektům by CVS mohlo možná i stačit, spíše se budeme bavit o těch středních a větších.

Sady změn

V CVS jde každý soubor sám za sebe – při commitu změn ve více souborech se změny v každém souboru commitují zvlášť a nejsou nijak provázány (nanejvýše byly commitnuty zhruba ve stejnou chvíli, i když takový commit do dvacítky adresářů může chvilku trvat).

Po našem ideálním systému (říkejme mu IVS) bychom chtěli, aby spolu současně commitnuté změny nějakým vhodným a spolehlivým způsobem svázal. Buď to může dělat tím, že bude počítat revize pro celé repository najednou, nebo si může číslovat sady změn (changesety) nezávisle na tradičních revizích souborů.

Přesouvání souborů

IVS by nemělo mít vůbec žádné problémy s přesouváním a přejmenováváním souborů. Soubor by proto neměl být v repository fyzicky identifikován svým jménem a neměl by být ukládán v závislosti na adresáři, kde se nachází. Nesmíme totiž zapomenout na to, že v každé větvi se může soubor jmenovat jinak a nacházet se úplně jinde, než kde čekáme.

Obyčejný adresář

V CVS měl adresář speciální postavení, soubory se ukládaly a organizovaly podle toho, v jakém adresáři se nacházely. To znamená, že adresář nejde dost dobře smazat, činí to zbytečné problémy při přesouvání souborů apod. Proto by měl v našem IVS adresář pokud možno co nejvíce vystupovat jako obyčejný soubor, u kterého si pouze poznamenáme, že zároveň obsahuje i nějaké jiné soubory. Měli bychom pak možnost adresáře přesouvat, přejmenovávat, vytvářet a mazat stejně jako všechny ostatní soubory.

Verzování metadat

I když nadpis zní hrozivě, v podstatě jde pouze o to, aby IVS sledoval kromě vývoje obsahu souboru (a jeho jména) i například vývoj jeho práv či vlastnictví. Zároveň by se měl nějak elegantně vyrovnat se symbolickými odkazy a podobnými nástrahami.

Efektivní komunikace přes síť

CVS se chová při komunikaci po síti značně neefektivně, částečně kvůli určité „hackovitosti“ protokolu pserver, který používá. My bychom chtěli raději něco, co přenáší pouze data, která jsou opravdu potřeba. Nejlépe by to mělo používat algoritmy podobné rsyncu, který zřejmě v této oblasti nemá v současnosti konkurenci.

Větvený vývoj

IVS by měl co nejvíce podporovat větvený vývoj, tedy možnost rozdělovat vývoj na různé nezávislé větve, které se pak budou moci mezi sebou vzájemně libovolně družit a spojovat. Možnost mít snadno a bez jakýchkoliv technických překážek třeba pro každou feature zvláštní větev by pak mohla zcela změnit styl vývoje mnoha programů, snad k lepšímu.

Proto je nutné co nejvíce větvení usnadnit. Samozřejmě musí být možné velmi snadno a rychle vytvořit novou větev (tedy v O(1) čase, také protože větvení konec konců využijeme spíše v těch rozsáhlejších projektech). Ovšem neměl by být na překážku ani samotný fakt, že už hodně větví máme – neměly by tedy samy o sobě nijak výrazně zvyšovat nároky na diskový prostor a zároveň by neměly zpomalovat práci se souborem. Týká se to však i zdánlivých banálností jako pravidelného vypisování informací o všech větvích v cvs log.

Slučování větví

Náš IVS by měl samozřejmě umět bez problémů nejen větve vytvářet, ale i vzájemně je všelijak slučovat. Vlastně je to jeden ze zásadních problémů, se kterým se různé systémy vypořádávají různým způsobem a také s proměnlivou úspěšností. Díky tomu také toto téma bude jedním z těch klíčových při našem posuzování různých systémů.

V prvé řadě by se měl systém, pokud mu řekneme, aby sloučil dvě větve, sám a bez mrknutí oka vypořádat se situací, kdy jsme již jednou tyto větve slučovali. Měl by si v historii každé větve pamatovat, kdy se s kterou sloučila, a v pomyslném grafu jednotlivých revizí (tzv. DAG) pak najít posledního společného předka obou větví; toho pak vezme za základ slučování.

Samozřejmě jde i o to, jak už systém nakonec v praxi sloučení změn do nějakého (textového) souboru provede. RCS i CVS používají program diff3, který v případě konfliktů produkuje známé záplavy rovnítek a špičatých závorek. Ovšem konflikty vytváří velmi rád a ochotně, i když by to často šlo vyřešit smírem. Jistě by bylo na místě tento nástroj ještě vylepšit, a některé systémy tak opravdu učinily.

Když už jsme sloučili dvě větve, obvykle se tak v té cílové stane pouze v rámci jediné revize, ztratili jsme tedy veškeré informace o jednotlivých změnách z větve zdrojové. To je často velmi podstatná ztráta informace, bylo by proto skvělé, pokud by se tyto informace nějakým způsobem zachovaly. Minimálně si u slučovací revize udělat poznámku, kterých přesně revizí zdrojové větve se akce týkala, a nabídnout možnost při vypisování změn na příslušná místa nenápadně vložit i odpovídající seznam změn z cizích revizí.

Pokud udržujete či jste někdy udržovali nějaký opensource projekt, ke kterému se vám sbíhala řada různých patchů, nebo provádíte část vývoje v oddělených větvích, jistě jste už kolikrát vybírali z „balíčků“ změn nabízených přispěvateli pouze některé úpravy. Cize se tomu říká cherrypicking a takto si jednoduše vybírat z větve jen některé náhodné revize by samozřejmě měl umět i náš mytický IVS. To znamená i se správným slučováním historie a ochranou před dvojitým sloučením dané revize (i když slučujeme revizi, kterou už jsme si jednou sloučili v rámci nějakého většího merge nebo naopak).

Distribuovaný vývoj

Osobně jsem zastáncem vize, že právě tímto směrem leží budoucnost – vývoj bude se odehrávat na řadě míst světa odděleně, každý vývojář bude mít vlastní plnohodnotné repository projektu (tedy vlastní fyzicky segregovanou větev) a jednotlivé změny se budou různými mechanismy přelévat mezi různými vývojáři a „centrálním“ referenčním repository, kde bude žít oficiální verze projektu.

Mít možnost snadno si „naklonovat“ vlastní repository skýtá řadu výhod. Běžná práce s repository je pak mnohem rychlejší, můžete pracovat i v okamžiku, kdy nejste na síti (ať už sedíte v letadle, nebo na chatě kdesi v Beskydech), ale hlavně nepotřebujete k vlastnímu vývoji souhlas ani pražádný jiný úkon ze strany maintainera. Tím se výrazně snižuje bariéra pro zapojení se do vývoje projektu; i předtím jste sice mohli dle libosti psát a posílat patche, ovšem neměli jste k dispozici komfort verzovacího systému, ve kterém byste si své změny mohli udržovat. Mohli jste ovšem využít poněkud krkolomné podpory CVS pro vendor tagy, ovšem pak jste zase neměli přímý přístup k verzovacím informacím původního projektu.

Existují samozřejmě i nevýhody klonování – naklonované repository zabere podstatně více diskového prostoru než čistě vyexportovaná kopie souborů dané verze a zároveň to je samozřejmě i mnohem náročnější na síť, tedy zejména mimo vysokoškolské koleje pak takové naklonování může zabrat nepříjemně mnoho času. Proto by IVS měl podporovat i klasickou práci přes síť.

Zmínili jsme se již o tom, že každé repository vlastně vystupuje jako samostatná větev projektu. Stavíme tedy na již uvedených předpokladech o dobré podpoře větvení, zároveň však zde zůstává otázka, zda by každé repository vždy mělo obsahovat pouze jedinou větev (interně samozřejmě stále bude nutno udržovat informaci o jiných větvích, se kterými jsme se někdy slučovali), či zda by mělo samo ještě navíc podporovat vývoj ve více větvích. Oba přístupy mají opět klady i zápory, rozhodnutí tedy necháme na každém konkrétním systému.

Krom repository každého vývojáře budou existovat jistě i určitá „referenční“ repository, kde se bude udržovat oficiální větev daného projektu. Někde může být vývoj organizován tak, že tuto oficiální větev udržuje jediný člověk, který do ní přijímá změny z jiných projektů (např. Linux kernel), zatímco ve většině jiných projektů by zřejmě do tohoto repository mělo mít přístup více lidí. Zde se nám tedy oddělují dva způsoby přenosu změn mezi větvemi. Při použití „pull“ si správce cílového repository „stáhne“ sám do své větve změny z cizích repository, zachovává si tak plnou kontrolu nad tím, co se v jeho větvi ocitne, stáhnout si může, co chce; tento přístup je vhodný pro pracovní větve jednotlivých vývojářů. Naopak při použití „push“ správce zdrojového repository rozhodne, co chce do cílové větve všechno natlačit; právě tuto metodu zřejmě zvolíme pro odesílání již hotových změn do oficiálního repository.

Co se týče vnitřní implementace, skýtá distribuovaný styl vývoje řadu úskalí. Zdrojové repository nemá informace o všech větvích, které z něj vycházejí, proto není jednoduché implementovat správně jejich pozdější slučování. Oříškem je již otázka nějakého dobrého identifikátoru jednotlivých revizí, fyzicky totiž čísla revizí budou v každém repository jiná.

Kompatibilita

Náš IVS by měl být nějakým způsobem kompatibilní s CVS, to je totiž klíčové pro jeho rychlé rozšíření. Zejména u rozsáhlých projektů je totiž radikální změna verzovacího systému obtížný a nevděčný proces, který se může dotknout tisíců, někdy i desítek tisíc lidí. Kompatibilita s CVS může tento dopad značně omezit, navíc když část vývojářů může v rámci CVS serveru již využívat alespoň část výhod IVS, jistě to pomůže přesvědčit zbytek vývojového týmu.

Klienti by měli být schopni komunikovat s CVS serverem, případně pracovat nad CVSoidním repository, a to při zachování co nejvíce vlastností IVS. Není nutné (a často ani možné) dosáhnout v takovém prostředí vysoké efektivity, ovšem přesto by i při práci s CVS měl IVS podporovat changesety, dobře umět slučovat a snad si i poradit s distribuovaným vývojem. Nutné informace lze přidávat například do log messages či jiných vhodných obyčejně nevyužitých míst (například do popisu souboru), odkud si je mohou ostatní IVS klienti vytáhnout zpět (CVS klienti by tyto informace měli lépe ignorovat, v horším případě zobrazovat uživateli, proto by informace měly být v čitelné a v co nejméně rušivé podobě). Zároveň by měl IVS server podporovat alespoň co nejjednodušší anonymní readonly přístup ze strany CVS klientů, aby vývoj mohly dále sledovat hordy betatesterů apod.

Kompatibilita by ovšem neměla zahrnovat pouze schopnost spolupráce CVS a IVS, ale i filosofii, která není radikálně odlišná. I když samozřejmě není nic špatného na drobných odlišnostech, celkově by se IVS nemělo používat diametrálně rozdílným způsobem. Rámcově toho ostatně na filosofii použití CVS nenajdeme tolik špatného.

CS24_early

Free software

IVS by samozřejmě měl používat plně zdokumentovaný síťový protokol a formáty. Zároveň by to měl být free software, aby se zajistilo jeho neomezené volné šíření a použití, přičemž se do jeho vývoje zapřáhne celá síla opensource komunity! :-)


V příštím dílu se zběžně porozhlédneme po scéně neideálních systémů pro správu verzí, uděláme si malou výstavku od Subversion přes GNU Arch až po BitKeepera a pokusíme se určit, jak má který z těchto nástrojů daleko k našemu ideálu.

Byl pro vás článek přínosný?