Hlavní navigace

Výlet do říše verzí: CVS

15. 4. 2003
Doba čtení: 9 minut

Sdílet

Každý, kdo dospěje do určitého vývojového stadia, se od svých rodičů, školy, nebo alespoň kamarádů dříve či později musí dozvědět určité věci, zásadní mimo jiné i pro jeho budoucí život. Správně, dnes si konečně povíme něco o CVS.

Concurrent Versions System

V minulých dílech jsme si vyprávěli o RCS (Revision Control System), nástroji, který umožňuje sledovat historii nějakého souboru. CVS je vlastně pouze takové RCS na steroidech — umožňuje udržovat historii několika souborů zároveň, zpřístupnit soubory a manipulaci s nimi přes síť a nabízí i několik dalších zajímavých možností, z nichž většina více či méně souvisí s těmito dvěma základními rysy. Protože je to ale uvnitř opravdu pouze chytřejší RCS, některé věci pouze připomenu nebo vysvětlím jen stručně.

Na rozdíl od RCS již CVS nepracuje pouze s jednotlivými soubory, ale s celými skupinami souborů, říkejme jim projekty. Zároveň pokud mají takové soubory a jejich historie být přístupné i přes síť, bylo by vhodné mít je na nějakém jednom definovaném místě, odkud by si je každý dle potřeby vytáhnul. Proto CVS přichází s konceptem tzv. repository neboli skladiště. Obvykle se nachází v adresáři /cvsroot, /usr/local/cvs,/ho­me/cvs nebo na podobných místech, vybírejte dle vkusu. Repository obvykle reprezentuje nějaký konkrétní projekt, celé repository sdílí více či méně jednotnou konfiguraci, přístupová práva apod. Samotné repository se však ještě člení na moduly, pod kterými si lze představit nezávislé části nějakého projektu (například modul pro zdrojový kód, manuál, web, …). Moduly pak konečně obsahují vlastní soubory, které si pomocí CVS spravujeme.

Jak si zařídit své hnízdečko

Při vytahování (checkoutování) souborů z CVS musíme určit cestu k repository, ve kterém jsou soubory uloženy, a jméno modulu, jehož pracovní kopii chceme z CVS dostat. Cestu k repository budeme vždy určovat parametrem -d<cesta> (ono je to trochu složitější, ale vše v pravý čas) — je to však potřeba pouze občas, většinou pracujeme v adresáři, kde máme vycheckoutovanou pracovní kopii nějakého modulu, v takovém případě si CVS naštěstí tyto informace samo pamatuje.

Abychom si mohli CVS řádně ozkoušet, bude nejlepší si udělat nějaké vlastní malé pokusné repository. To je jednoduché:

pasky@machine:~$ cvs -d/home/pasky/cvsrepo init
pasky@machine:~$

Tichá voda břehy mele. Zjistíme, že se nám v domovském adresáři objevil podadresář cvsrepo/, v něm tedy bude sídlit naše repository. Obvykle nás fyzický obsah repository nebude příliš zajímat a o tom zajímavém si povíme stejně až příště. Ale už teď můžeme prozradit, že přímo v adresáři cvsrepo/ budou tvořit podadresáře samotné moduly. Hned po inicializaci tam bude pouze jeden, speciální, CVSROOT. Ten obsahuje konfigurační soubory, které platí pro všechny moduly v onom repository. Často si vystačíte pro běžnou práci pouze s jedním dalším modulem, kde budete skladovat ty pro vás opravdu užitečné soubory. Netrapte se tím.

CVS předpokládá, že už máte nějaké soubory, které chcete do repository importovat, to je vlastně také jediný normální způsob, jak vytvořit nový modul. Samozřejmě můžete začít na zelené louce, stačí vytvořit prázdný adresář, do kterého se přesunete a provedete import. My si ale pro názornost vytvoříme dva soubory, potom vytvoříme nový modul a importujeme do něj právě obsah aktuálního adresáře:

pasky@machine:~$ mkdir cvstest
pasky@machine:~$ cd cvstest
pasky@machine:~/cvstest$ echo ahoj > ahoj
pasky@machine:~/cvstest$ echo zdravim > zdravim
pasky@machine:~/cvstest$ cvs -d/home/pasky/cvsrepo
 import -m"Initial import." test PASKY INITIAL
N test/ahoj
N test/zdravim

No conflicts created by this import

pasky@machine:~/cvstest$

Ha, už se nám to komplikuje. Parametr -m by měl být již poctivým čtenářům jasný, určuje log message, ta se v historii importovaných souborů přiřadí k revizi, která se vytvořila importem těchto souborů. Pokud tento parametr vynecháte, CVS vám snaživě samo pustí váš oblíbený textový editor, kam vám tuto zprávu nechá napsat.

První „skutečný“ parametr (test) je jméno modulu, který se má vytvořit. Za ním následuje tzv. vendor tag („značka výrobce“) a release tag („značka verze“). To je neštěstí všech, kdo píší různé tutorialy o CVS, protože téměř nikdy ještě v tuto chvíli nechtějí mluvit o tagování, natož o vendor a release tagu. Takže tam zatím prostě napište, kdo má soubory, které importujete, na svědomí (třeba pasky) a jaká je to jejich oficiální verze (napište třeba initial, to znamená počáteční). Nebo tam napište, co chcete, bývá však zvykem tagy psát velkými písmeny.

Často bývá člověk v rozpacích, co napsat importu za parametry, nejrozumnější je asi moc se tím netrápit a prostě tam napsat něco podobného, jako je uvedeno v příkladě výše. Pokud nehodláte využít možnosti sledování cizích projektů, je to stejně jedno. Aha, vy vlastně ještě nevíte, co to je. To je ale škola života, ani později často při importu nebudete tušit, že byste někdy něco takového chtěli dělat (i když nakonec třeba chtít budete, a zrovna, když tam při importu napíšete nějaké ptákoviny), ačkoli už budete vědět, co to je.

Dovnitř a zase ven

Teď bychom tedy v našem modulu chtěli něco dělat, například si nejdříve vytáhnout z repository pracovní kopii jeho poslední verze:

pasky@machine:~$ cvs -d/home/pasky/cvsrepo co test
cvs checkout: Updating test
U test/ahoj
U test/zdravim
pasky@machine:~$

RCS je opravdu vidět na každém kroku — místo co můžeme použít delší formu checkout, ovšem takhle je to myslím pohodlnější.co samozřejmě umí brát spoustu dalších parametrů, ovšem podrobněji si o těch zajímavějších povíme až později, zatím nám stačí tato základní forma.

V adresáři test/ nyní tedy máme onu vytouženou pracovní kopii, navíc nám tam však okouní podadresář CVS/. Ten uvidíme v každém adresáři, který pochází z CVS, obsahuje určité informace o původu a stavu této kopie. Nejlépe je si tohoto adresáře pokusit nevšímat.

Nyní tedy zkusíme zpět do repository dostat (tzv. commit) změny, které jsme udělali v naší pracovní kopii. Opět můžeme použít ci, nebo delší formu commit.

pasky@machine:~/test$ echo kuk >> ahoj
pasky@machine:~/test$ cvs ci -m"Hezke citoslovce."
cvs commit: Examining .
Checking in ahoj;
/home/pasky/cvsrepo/test/ahoj,v  <--  ahoj
new revision: 1.2; previous revision: 1.1
done
pasky@machine:~/test$

CVS si tedy samo najde soubory, které byly v pracovní kopii oproti repository změněny, a uloží do repository jejich nové verze. Pokud chceme CVS konkrétně říci, které soubory mají být commitnuty, stačí jejich jména napsat jako parametry příkazu ci.

Uživatelé RCS možná čekali, že soubor ahoj zmizí, ovšem v CVS tomu tak není, pracovní kopie zůstává tam, kde je, dokud ji sami nesmažeme. Ale jak ji aktualizovat, pokud v repository někdo zatím udělal nějaké změny? Dejme tomu, že před commitnutím našeho citoslovce jsme si z repository vycheckoutovali ještě jednu pracovní kopii. Teď ji tedy zaktualizujeme:

pasky@machine:~/test2$ cvs up
cvs update: Updating .
U ahoj
pasky@machine:~/test2$

Hop a máme i zde žhavé novinky dne! I tento příkaz má kratší a delší formu, up a update. Osobně jsem spíše zvyklý používat v tomto případě tu delší, ale měl bych být konzistentní alespoň na papíře. update také rozumí spoustě parametrů, my si nyní řekneme pouze o dvou z nich. Obvykle tento příkaz totiž bude ignorovat případně přibyvší nové adresáře, to mu rozmluvíme parametrem -d. Ovšem bohužel v CVS nejdou standardně mazat staré, prázdné a opuštěné adresáře, parametrem -Palespoň můžeme prohlásit, že nechceme, aby update vytvářelo takovéto prázdné adresáře. Oblíbená forma tohoto příkazu je tedy cvs update -dP.

O životě souborů…

CVS ignoruje soubory, o kterých nic neví, jenom update na ně upozorní tím, že je vypíše a umístí před jejich jména otazníky (pokud ho chceme umlčet, stačí do daného adresáře umístit soubor .cvsignore a do něj na jednotlivé řádky napsat jména souborů (nebo masky), na která updatenemá nadávat). Zároveň také tento příkaz obnoví všechny soubory, které se nám z naší pracovní kopie nějak vytratily. Proto pokud chceme legálně smazat nebo přidat nějaký soubor, musíme na to jít jinak. CVS musíme o dané události říci příkazem cvs add soubor, respektive cvs delete soubor. Pak to ještě musíme stvrdit commitem:

pasky@machine:~/test2$ echo lala > zpivanky
pasky@machine:~/test2$ cvs add zpivanky
cvs add: scheduling file `zpivanky' for addition
cvs add: use 'cvs commit' to add this file permanently
pasky@machine:~/test2$ rm zdravim
pasky@machine:~/test2$ cvs delete zdravim
cvs remove: scheduling `zdravim' for removal
cvs remove: use 'cvs commit' to remove this file permanently
pasky@machine:~/test2$ cvs ci -m"Zacal jsem zpivat a
 prestal jsem zdravit."
cvs commit: Examining .
Removing zdravim;
/home/pasky/cvsrepo/test/zdravim,v  <--  zdravim
new revision: delete; previous revision: 1.1.1.1
done
RCS file: /home/pasky/cvsrepo/test/zpivanky,v
done
Checking in zpivanky;
/home/pasky/cvsrepo/test/zpivanky,v  <--  zpivanky
initial revision: 1.1
done

Pozn.: Autoři CVS zřejmě došli k závěru, že popisy souborů, jak je známe z RCS, jsou vlastně v zásadě k ničemu (a nemůžeme s nimi myslím tak docela nesouhlasit), proto se již tento institut v CVS nevyskytuje. Prostě je to soubor, pokud se o jeho podstatě chcete podělit s diváky, učiňte tak v log message při přidávání onoho souboru.

…a jiné pohádky

Ještě než se pro dnešek rozloučíme, povíme si něco málo o několika dalších užitečných a poměrně jednoduchých příkazech. Již dříve jsme si říkali o RCSoidním rlogu, jeho blízký příbuzný v CVS se nazývá cvs log a dává velmi podobný výstup — kromě základních informací o souboru (kdo, kdy, jak, s kým a za kolik) vypíše tento příkaz i kompletní informace o jeho historii. Bez parametrů vypíše historii všech souborů v aktuálním adresáři a jeho podadresářích, většinou však dáte přednost informacím pouze o jednom souboru, jehož jméno předáte příkazu jako parametr.

Velmi užitečné, prospěšné a bezesporu i chvályhodné je nechat si před commitem vypsat diff obsahující změny, které jsme provedli. To umí příkaz cvs diff, který normálně dělá právě toto. Dokáže ve skutečnosti ukazovat rozdíly i mezi jednotlivými revizemi, o tom si však povíme příště. Příkaz bere stejné parametry jako standardní diff.

Cloud23

Zajímavý je příkaz cvs annotate, který vypíše obsah daného souboru, ale pozor! Před každou řádkou totiž vypíše, kdo a kdy ji naposledy změnil. To může být při dohledávání některých změn pravé požehnání, zvláště když revizí není právě málo a popisy změn nejsou příliš výřečné:

pasky@machine:~/test2$ cvs annotate ahoj
Annotations for ahoj
***************
1.1          (pasky    10-Apr-03): ahoj
1.2          (pasky    10-Apr-03): kuk
pasky@machine:~/test2$

V příštích dílech si povíme o tom, jak naučit CVS rozpoznávat více uživatelů, jak si povídat přes síť, osvojíte si tagování, větvení a pak zase slučování, nakonec si řekneme o některých příkazech trochu více a dozvíme se něco i o konfiguraci a tajemném modulu CVSROOT. Nezapomeneme ani na různé externí nástroje, které nám práci s CVS mohou zpříjemnit nebo nám pomoci s různými věcmi. Ale nebojte se, ani po CVS náš seriál nekončí. Ukážeme si, co v CVS chybí a co z toho nám mohou nabídnout konkurenční projekty, kterých není vlastně tak málo. Dozvíte se, co to jsou a co umí Subversion, Arch, BitKeeper, OpenCM a další.