Subversion howto

A HupWiki-ből...

Tartalomjegyzék

Bevezetés

Ennek a howtonak a célja, hogy egy egyszerű példa segítségével bemutassa, hogy hogyan készíthetünk magunknak Subversion verziókezelő rendszert UNIX like rendszeren, valamint arról, hogy meglévő projectjeinket hogyan helyezhetjük a felügyelete alá. A hogyan csak felületes, ezért a részletekért olvasd el az SVN-bookot

A példa

A példa szerint két projectet szeretnénk létrehozni: MyProject1 és MyProject2. A MyProject1 egy éppen most induló project lesz, amihez még nem tartozik egyetlen sor forráskód vagy egyéb fájl sem. A MyProject2 egy már létező projectnek fog készülni. Az eddig elkészült kódok eddig nem voltak verziókezelő rendszer által támogatva, ezt most szeretnénk beálltani.

Telepítés

A Subversion telepítéséhez érdemes az adott rendszer csomagkezelőjét használni, mivel az SVN megtalálható a legtöbb disztribúcióban. Aki mégis az eredeti forrást szeretné felhasználni, az letöltheti azt a Subversion honlapjáról

Debian alapú rendszerre az apt-get install subversion paranccsal telepíthetjük.

A struktúrák kialakítása

Mivel szeretjük, ha a dolgaink egy helyen vannak, ezért az SVN repositorykat és minden hozzájuk tartozó fájlt egy helyre fogunk tenni. Tegyük fel, hogy ennek egy külön partíciót tartunk fent, amit a /devdirs könyvtárba csatoltunk fel. Lépjünk be a mappába a cd /devdirs; paranccsal. Készítsünk egy svnlayout nevű mappát, ebben létrehozzuk az SVN repositoryk sablon szerkezetét, ami az alap esetben ajánlott trunk, branches és egy tags mappából áll. A trunk mappa tartalmazza majd a fejlesztői ágat. A branches a dokumentumok, programok egyes ágainak van fenntartva, így pl. itt készíthetünk el egy olyan új funkciót a programunkhoz, ami később beolvasztásra kerülhet. A tags mappában egy-egy pillanatképet szokás tárolni, így ha van egy jól működő változatunk egy programról amit később módosítani szeretnénk, előtte készíthetünk róla egy taget, mintegy extra mentésként.

$ mkdir svnlayout;
$ mkdir svnlayout/{trunk,branches,tags};

Ezzel létrehoztuk azt a könyvtárszerkezetet, amit minden projectnél használni fogunk.

Ezen kívül még két könyvtárra lesz szükségünk, egyik ahol az SVN adatbázisok lesznek tárolva ez lesz az svnrepos könyvtár valamint egy, ahol a projectjeink lesznek, itt fog zajlani a tényleges munkavégzés. A könyvtár neve projects lesz.

$ mkdir {svnrepos,projects};
/devdirs/
|--svnlayout/
|  |--branches/
|  |--tags/
|  |--trunk/
|--svnrepos/
|--projects/

Ettől a szerkezettől természetesen el lehet térni, az SVN-ben erre vonatkozóan semmilyen megkötés nincs.

Tárolók létrehozása (svnadmin create)

Eljött az idő, hogy elkészítsük projectjeink tárolóját. Ehhez először létrehozzuk a projectek könyvtárát az svnrepos könyvtárban majd az svnadmin paranccsal létrehozzuk magát a tárolót, ahol az adott projecthez tartozó összes információ tárolva lesz. Egyúttal hozzuk létre a projects könyvtárba is a megfelelő könyvtárakat.

$ mkdir {svnrepos,projects}/{MyProject1,MyProject2};
$ svnadmin create --fs-type fsfs svnrepos/MyProject1;
$ svnadmin create --fs-type fsfs svnrepos/MyProject2;

Az fs típusa Berkeley DB (bdb) vagy FSFS lehet, ez az opció elhagyható. Röviden annyit róluk, hogy a bdb régebbi ezért lassabb, több helyet foglal a háttértárolón, stb. Az FSFS régebbi verzióval állítólag voltak problémák, viszont a Subversion 1.4-es verziójától az FSFS-t részesítik előnyben.

A parancs a következő könyvtárakat és fájlokat hozza létre:

$ ls -pR svnrepos/MyProject1
svnrepos/MyProject1:
conf/  dav/  db/  format  hooks/  locks/  README.txt

svnrepos/MyProject1/conf:
authz  passwd  svnserve.conf

svnrepos/MyProject1/dav:

svnrepos/MyProject1/db:
current  format  fs-type  revprops/  revs/  transactions/  uuid  write-lock

svnrepos/MyProject1/db/revprops:
0

svnrepos/MyProject1/db/revs:
0

svnrepos/MyProject1/db/transactions:

svnrepos/MyProject1/hooks:
post-commit.tmpl  post-lock.tmpl  post-revprop-change.tmpl  post-unlock.tmpl  pre-commit.tmpl  pre-lock.tmpl  pre-revprop-change.tmpl  pre-unlock.tmpl  start-commit.tmpl

svnrepos/MyProject1/locks:
db.lock  db-logs.lock

Ezekben a fájlokban és könyvtárakban tárolódik a repository minden része, melyek némelyikéről később szó fog esni. FIXME

SVN-nel használható protokollok

A Subversion több féle elérési módot támogat, így egy repositoryt el lehet érni például közvetlen a fájlrendszerről vagy az SVN saját protokolljával. A következő lehetőségek közül lehet választani:

Protokoll Elérési mód
file:/// Elérés közvetlenül a helyi fájlrendszeren keresztül
http:// Elérés WebDAV-on keresztül
https:// Elérés SSL titkosítással ellátott WebDAV-on keresztül
svn:// Elérés az SVN saját protokollján keresztül
svn+ssh:// Elérés az SVN saját protokollján, SSH titkosított csatornán keresztül

A megfelelő protokoll kiválasztása helyzetfüggő. Ha csak egymagunk dolgozunk a projecten, akkor elegendő a helyi fájlrendszeren keresztüli elérést használni, ebben az esetben ugyanis nincs szükség további konfigurálásra, így ez a leggyorsabb. Amennyiben többen dolgozunk a fájlokon, szükségünk lesz egy olyan megoldásra, amin keresztül mások is elérhetik a repositoryt. Ekkor lehetőség szerint használjunk titkosított csatornát az adatok biztonsága érdekében.

Struktúra létrehozása

Hozzuk létre az svn sablon szerkezetét úgy, hogy az előzőleg az svnlayout könyvtárban elkészített szerkezetet beimportáljuk a frissen megcsinált repositoryba (figyeljünk a pontra, ami az aktuális könyvtárat jelöli):

$ cd svnlayout
$ svn import . file:///devdirs/svnrepos/MyProject1 --message 'Struktura letrehozasa';
$ svn import . file:///devdirs/svnrepos/MyProject2 --message 'Struktura letrehozasa';

Ezek után, ha minden igaz, egy a következőhöz hasonló könyvtárszerkezetet kellett kapjunk. Az ábrán csak a MyProject1 és az ahhoz tartozó alkönyvtárakat jelenítem meg, ugyanez érvényes a MyProject2-re is. A fájlnevek végén a "/" jelöli a könyvtárat

/devdirs/
|--svnlayout/
|  |--branches/
|  |--tags/
|  |--trunk/
|--svnrepos/
|  |--MyProject1/
|  |  |--conf/
|  |  |  |--authz
|  |  |  |--passwd
|  |  |  |--svnserve.conf
|  |  |--dav/
|  |  |--db/
|  |  |  |--current
|  |  |  |--format
|  |  |  |--fs-type
|  |  |  |--revprops/
|  |  |  |  |--0
|  |  |  |--revs/
|  |  |  |  |--0
|  |  |  |--transactions/
|  |  |  |--uuid
|  |  |  |--write-lock
|  |  |--format
|  |  |--hooks/
|  |  |  |--post-commit.tmpl
|  |  |  |--post-lock.tmpl
|  |  |  |--post-revprop-change.tmpl
|  |  |  |--post-unlock.tmpl
|  |  |  |--pre-commit.tmpl
|  |  |  |--pre-lock.tmpl
|  |  |  |--pre-revprop-change.tmpl
|  |  |  |--pre-unlock.tmpl
|  |  |  |--start-commit.tmpl
|  |  |--locks/
|  |  |  |--db.lock
|  |  |  |--db-logs.lock
|  |  |--README.txt
|--projects/
|  |--MyProject1/
|  |  |--branches/
|  |  |--tags/
|  |  |--trunk/

Tárolók feltöltése és használatba vétele (svn import)

Mivel a MyProject2 egy létező projectünk, ezért a trunk könyvtárába beimportáljuk a már létező fájlokat.

$ cd /path/to/projectdir;
$ svn import . file:///devdirs/svnrepos/MyProject2/trunk --message 'Mar kesz fajlok beimportalasa';

Visszamegyünk a projects könyvtárba majd az svn checkout paranccsal letöltjük a projectek adatbázisát, hogy utána megkezdhessük velük a munkát.

$ cd /devdirs/projects/MyProject1;
$ svn checkout file:///devdirs/svnrepos/MyProject1 .;
$ cd /devdirs/projects/MyProject2;
$ svn checkout file:///devdirs/svnrepos/MyProject2 .;

Ezután a projectek trunk könyvtárában elkezdhetjük a munkát.


Munka közben

Most, hogy készen vagyunk a repositorykkal, elkezdünk velük dolgozni. Továbbiakban csak a MyProject1-en végzünk műveleteket, ugyanis az ezzel kapcsolatos dolgok használhatóak már a MyProject2-nél is. Minden további műveletet a /devdirs/projects/MyProject1/trunk könyvtárban végzünk.

Esetenként a parancsok kimenetét is feltüntetem a howtoban.

Revision

Svnben a fájlok változatait úgy nevezett revision numberrel azonosíthatjuk. Minden revision egyértelműen meghatározza minden fájl állapotát, tartalmát. A revision number minden commit után eggyel növekszik. Ez a szám az egész repositoryra vonatkozik, ellenben a CVSsel, ami minden fájlt külön-külön számoz. Ez megkönnyíti egy-egy régebbi verzió megtalálását.

A revisionöket minden parancsban egységes formában érhetjük el: svn <cmd> -r <revision number>. Megadhatunk tartományt is, ekkor egy kettősponttal kell elválasztani a revision numbereket: svn <cmd> -r <revison number 1>:<revision number 2.

Néhány revision állapot speciális elnevezést kapott, így azok anélkül is elérhetőek, hogy tudnánk a pontos számot:

  • HEAD A repositoryn lévő legutolsó verzió
  • BASE A working copy alapjául szolgáló legutolsó változat. Gyakorlatilag az utolsó svn update által letöltött vagy svn commit által feltöltött változatot jelenti.
  • COMMITTED Utolsó commitolt változatod. FIXME
  • PREV Az előző változat. Az utolsó commit előtti állapot. Nagyjából egyenlő a COMMITTED-1 verzióval.
  • {dátum} Egy adott dátumkor érvényes változat

Ezeket használva például könnyedén megtudhatjuk, hogy milyen különbségek vannak a helyi változatunk és a repositoryban lévő legutolsó verzió között:

svn diff -r BASE:HEAD

Alapvető munka ciklus

Az svn-bookban olvashatunk egy ajánlást, mely az SVN-nel való munkavégzés ajánlott formáját írja le. Ezzel a munkaciklussal minimálisra csökkenthetjük annak az esélyét, hogy konfliktusok alakuljanak ki, így kevesebb problémát okozhatunk magunknak és a projecten velünk dolgozóknak. Ebben a szakaszban ezt a ciklust tárgyaljuk.

Tehát egy tipikus munka ciklus a következőhöz hasonlóan néz ki:

  • Módosítás előtt mindig frissítsük a helyi változatunk. Így elérhetjük, hogy mindig a legfrissebb változat legyen nálunk.
    • svn update
  • Végezzük el a változtatásainkat
    • svn add
    • svn delete
    • svn copy
    • svn move
  • Vizsgájuk meg a változtatásainkat
    • svn status
    • svn diff
  • Frissítsük a helyi változatunk, hogy tudjuk, nem történt-e azóta frissítés a repositoryban és lássuk, ha valamivel ütköznek a módosításaink
    • svn update
  • Ha szükséges, vonjuk vissza a változtatásaink
    • svn revert
  • Szüntessük meg a konfliktusokat (olvasszuk be mások változtatását)
    • svn update
    • svn resolved
  • Commitoljuk a változtatásainkat
    • svn commit

Munka közben valószínűleg többször használjuk majd némely parancsot, de ha alapvetően követjük (és munkatársaink is követik) ezt a módszert, rengeteg - főleg a konfliktusok által okozott - bosszúságtól kímélhetjük meg egymást.

Working copy létrehozása (svn checkout)

Ha nem most hoztuk létre a repositoryt, hanem pl. éppen csatlakozunk egy projecthez, ami már használja az SVN-t, akkor előbb le kell töltenünk a már meglévő fájlokat. Ezt az svn checkout paranccsal tehetjük meg.

$ svn checkout http://example.org/svn/MyProject1 /home/user/dev/MyProject1

Ekkor az example.org-ról letöltődik a MyProjecthez tartozó összes file és könyvtár /home/user/dev/MyProject1 könyvtárba.

File hozzáadása (svn add)

Mivel a projectünk egyelőre egyetlen fájlt sem tartalmaz, tulajdonképpen haszontalan. Készítsük hát el a hozzá tartozó első fájlunkat, aminek adjuk a first.file nevet. touch first.file;. Az svn status paranccsal lekérdezhetjük a változásokat. Ekkor a következő kimenetet fogjuk kapni:

$ svn status
?      first.file

Láthatjuk, hogy a first.file előtt egy kérdőjel szerepel. Ez azt jelenti, hogy a fájl még nem áll verziókezelés alatt. Ezért az svn add paranccsal adjuk is hozzá a repositoryhoz.

$ svn add first.file;
A         first.file

Ilyenkor a fájl még csak az svn helyi adatbázisához lesz hozzáadva, ahhoz, hogy ez a repositoryban és így a munkatársaknál is megjelenjen, commitolnunk kell.

$ svn commit -m 'first.file file hozzaadása';
Transmitting file data .
Committed revision 2.

A -m a --message rövidítése

A kimeneteken látható Committed revision 2. sor jelzi, hogy az utolsó commit hányadik változtatás repositoryban.

File törlése (svn del)

Előfordulhat, hogy munka közben egy file feleslegessé válik ezért töröljük azt. A törlés demonstrálására hozzunk létre egy ideiglenes fájlt, adjuk hozzá a repositoryhoz, commitoljuk majd töröljük azt az svn delete paranccsal:

$ touch second.file;
$ svn add second.file
A         second.file
 
$ svn commit -m 'second.file hozzaadasa';
Adding         second.file
Transmitting file data ..
Committed revision 3.
 
$ svn del second.file
D         trunk/second.file
 
svn ci -m 'second.file torlese';
Deleting       trunk/second.file
Committed revision 4.

Commitoláskor a file törlődik a merevlemezről, is viszont hála annak, hogy korábban commitoltuk az svn-ből bármikor visszaállíthatjuk azt, ha mégis szükségünk lenne rá. Ezt majd az svn copy paranccsal tehetjük meg - részletesen később.

File áthelyezése, átnevezése (svn mv)

Ha egy olyan file-t szeretnénk áthelyezni vagy átnevezni, amely már verzió kontroll alatt van, mindig használjuk az svn move parancsot, hogy a fájlon végzett változtatásokat könnyen nyomon lehessen kísérni.

$ svn mv first.file first-moved.file;
A         first-moved.file
D         first.file

Ha az áthelyezendő file nem commitolt változásokat tartalmaz, az svn figyelmeztetni fog erre és a commit megszakad. Ezt a --force kapcsolóval felülbírálhatjuk. Az svn move parancs kiadását követően az eredeti file az új néven lesz megtalálható a merevlemezen, commit után a változtatás eltárolódik a repositoryban is.

Helyi változtatások visszavonása (svn revert)

Tegyük fel, hogy változtatásokat végeztünk a repositoryban, azonban commitolás előtt meggondoljuk magunkat. Példának vegyük a fájl törlést.

Commit előtt mindig érdemes és ajánlott megtekinteni, hogy mely fájlokat változtattuk meg:

$ svn st
M      first.file
D      second.file

Az svn status parancs hatására láthatjuk, hogy a first.file nevű file tartalma módosult valamint a second.file nevű fájlt törlésre jelöltük ki. Ekkor azonban eszünkbe jut, hogy szükségünk lehet még a second.file-ra, ezért szeretnénk visszavonni a rá vonatkozó törlést. Ezt az svn revert paranccsal tehetjük meg.

$ svn revert tmp.file
Reverted 'second.file'
$ svn st
M      first.file

A törlést visszavontuk és ha most commitolunk, akkor csak a first.file módosításai fognak felkerülni a repositoryba.

Szerveren lévő változások letöltése (svn update)

Mivel az SVN repositoryt nem egyedül használjuk, hanem a fájlokat mások is szerkesztik, ezért gyakran megesik, hogy munkatársaink is változtatásokat commitolnak. Ebből adódóan a helyi fájlok régebbiek, mint a szerveren lévők. Ahhoz, hogy a local gépen és a szerveren lévő verziók megegyezzenek, le kell töltenünk a változásokat az svn update paranccsal:

$ svn update
U    first.file
Updated to revision 8.

Ahhoz, hogy elkerüljük a konfliktusokat (azok az esetek, amikor a szerveren lévő verzió újabb, mint a helyi, viszont a helyi fájlunkon is módosítottunk már, tehát nem összeegyeztethető különbségek is lehetnek), a lehető leggyakrabban töltsük le a változásokat és módosításokat követően (miután meggyőződtünk, hogy az elvégzett változtatások nem okoznak gondot a programokban) commitoljunk.

Branch létrehozása

Branchet akkor hozunk létre, ha valami olyasmivel szeretnénk kísérletezni, ami nem feltétlenül illik bele az aktuális fejlesztő ágba (trunk). Ilyen lehet például ha mondjuk valamelyik programkönyvtárat le akarjuk cserélni egy másikra. SVN-ben ilyenkor csupán annyit teszünk, hogy a trunkot átmásoljuk a branches könyvtárba.

$ svn cp file:///devdirs/svnrepos/MyProject1/trunk file:///devdirs/svnrepos/MyProject1/branches/branch1 -m 'branch1 letrehozasa'

Committed revision 13.

Ezzel még csak a repositoriban van létrehozva a branch, ahhoz, hogy ez helyben is érvényes legyen, csináljunk egy svn update-et a projectünk gyökérkönyvtárában!

$ cd ..
$ svn up
A    branches/branch1
A    branches/branch1/first.file
A    branches/branch1/second.file
Updated to revision 13.

Összeolvasztás (svn merge)

Miután a branchben elvégeztük a szükséges módosításokat és úgy gondoljuk, hogy alkalmas arra, hogy a módosítások bekerüljenek a fejlesztői ágba, olvasszuk be azokat, majd commitoljunk is egyet. A parancsokat a project trunk mappájában adjuk ki!

$ svn merge -r 13:HEAD file:///devdirs/svnrepos/MyProject1/branches/branch1
U    svn-howto.wiki
 
$ svn ci -m 'branch1 beolvasztasa a fejlesztoi agba'
Sending        trunk/svn-howto.wiki
Transmitting file data .
Committed revision 15.

Előfordul, hogy viszonylag sokat commitoltunk már és nem emlékszünk arra, hogy melyik revisionben történt a branch létrehozása. Ekkor elkezdhetjük az svn log-ot turkálni, greppelni, azonban van egy egyszerűbb módja is. Az SVN-ben van egy funkció arra, hogy könnyen megtalálhassuk, hogy mikor másoltunk valamit, így megtalálhatjuk, hogy mikor hoztuk létre a branchet.

$ svn log --stop-on-copy

A log utolsó eleme a branch létrehozását fogja jelölni, merge-kor ezt a számot kell alapul venni, viszont ki kell vonni belőle egyet.

------------------------------------------------------------------------
r30 | ennevem | 2009-11-19 13:33:34 +0100 (Thu, 19 Nov 2009) | 2 lines

branch1 utolso commitja

------------------------------------------------------------------------
...
------------------------------------------------------------------------
r22 | ennevem | 2009-11-19 10:33:34 +0100 (Thu, 19 Nov 2009) | 2 lines

branch1 letrehozasa

------------------------------------------------------------------------

Tehát mergeljük a branchet a trunkba:

$ svn merge -r 21:30 file:///devdirs/svnrepos/MyProject1/branches/branch1

A HEAD a branch legutolsó változatát jelöli. Beolvasztás történhet fordítva is, tehát a trunkban történt változásokat beírhatjuk a branchbe. A parancsokat a branch mappájában adjuk ki

$ svn merge -r 13:14 file:///devdirs/svnrepos/MyProject1/trunk

Összeolvasztáskor a branchre vonatkozó referencia pontként annak létrehozásának revision numberét használjuk, itt 13!

Merge a trunkból egy branchbe

$ cd /devdirs/projects/MyProject1/branches/branch1/
$ svn merge -r 12:13 file:///devdirs/svnrepos/MyProject1/trunk/file1 file1

Adott revision változásinak beolvasztása:

$ cd /devdirs/projects/MyProject1/branches/branch1/
$ svn merge -c 13 file:///devdirs/svnrepos/MyProject1/trunk/

Törölt branchből való merge-elés

Tegyük fel, hogy dolgozunk egy branchen, majd merge-ölünk belőle, végül töröljük a branchet. Azonban később előfordulhat, hogy még szükségünk van a branchre, például azért mert valamit mégsem merge-öltünk be vagy szeretnénk az egy másik branchbe is bemerge-ölni a változásokat. Ekkor csak úgy, minden nélkül nem használhatjuk a branch korábbi nevét - hiszen töröltük azt -, hanem a branch url-ében meg kell adnunk egy revisiont, ahol a branch utolsó állapota még létezett.

Tegyük fel, hogy a file:///devdirs/svnrepos/MyProject1/branches/branch3-at a 22. revisionben hoztunk létre és egészen 30-ig commiteltünk bele, majd 31-ben töröltük.

$ svn merge -r 21:30 file:///devdirs/svnrepos/MyProject1/branches/branch3@30

Tag létrehozása

A tag létrehozásának módja megegyezik a branch létrehozásával. Ez esetben nem a branches mappába másolunk, hanem a tags mappába.

$ svn cp file:///devdirs/svnrepos/MyProject1/trunk file:///devdirs/svnrepos/MyProject1/tags/tag1 -m 'tag1 letrehozasa'

Visszatérés korábbi verzióhoz

Gyakran megeshet, hogy csak commit után jövünk rá arra, hogy nem jó a változtatás, amit végeztünk. Ekkor ugye szeretnénk visszavonni a commitot, amit svn-ben az svn merge paranccsal tehetünk meg. a példában a trunk mappában történt 17. commitot vonjuk vissza. Az svn merge után egy svn commitra is szükség van, hogy érvényesítsük a változtatásokat

$ svn merge -c -17 file:///devdirs/svnrepos/MyProject1/trunk
U    svn-howto.wiki

A -c kapcsoló a --change rövidítése.

$ svn st
M      first.file
 
$ svn ci -m 'undoing change committed 17'
Sending        trunk/first.file
Transmitting file data .
Committed revision 18.

Másik módszer, ha a --change helyett a revision numbereket adjuk meg:

$ svn merge -r 19:16 file:///devdirs/svnrepos/MyProject1/trunk
U    first.file

A példában a 19-es revisionről a 16-osra tértünk vissza.

Törölt fájl visszaállítása repositoryból

Előfordul, hogy töröltünk egy fájlt, a törlést commitoltuk is, viszont eszünkbe jut, hogy mégis szükségünk lenne rá. Ilyenkor tudnunk kell, hogy melyik volt az a revision, ahol még elérhető volt a fájl, majd onnan visszaállíthatjuk:

$ svn copy -r 14 file:///devdirs/svnrepos/MyProject1/trunk/deleted.file ./deleted.file

Commit után újra a helyén lesz a fájl.

Fájlok, könyvtárak figyelmen kívül hagyása

Előfordul, hogy egy-egy projectben vannak olyan fájlok is, amiket nem szeretnénk verziókezelés alá helyezni. Ilyen fájlok lehetnek pl.: ideiglenes fájlok, konfigurációs fájlok stb. Az svn propset paranccsal meghatározhatjuk ezeknek a fájloknak és könyvtáraknak a listáját:

$ svn propset svn:ignore second.file .
property 'svn:ignore' set on '.'

Ugyanez egy teljes könyvtárra nézve:

svn propset svn:ignore '*' tempfiles/

Ebben az esetben a tempfiles könyvtárban lévő összes file és könyvtár figyelmen kívül lesz hagyva.

Konfliktusok kezelése

Vegyünk egy példát arra, hogy hogyan jöhet létre konfliktus: Van két felhasználó, Joe és John. Joe commitolt utoljára (revision 21), amit John szépen le is tölt az svn up paranccsal. Ekkor Joe szerkeszt valamit a fájlon, amit szépen fel is commitol (revision 22), azonban eközben John is szerkesztette a sajátját. Commit előtt természetesen leellenőrzi, hogy a repositoryban lévő verzió megegyezik-e a sajátjával:

$svn up
C    first.file
Updated to revision 22.

Itt láthatjuk, hogy a first.file előtt egy C karakter szerepel, ami egyenlő azzal, hogy az svn konfliktust érzékelt. Ebben az esetben minden konfliktusban érintett fájl mellé létrehoz az svn létrehoz további 3 fájlt:

  • filename.mine: ez a fájl az aktuálisan szerkesztett fájlunkkal egyenlő. Nem tartalmaz konfliktus jelölőket.
  • filename.rOLDREV: Ebben a fájlban az utolsó checkoutunk szerinti állapot van, mielőtt bármiféle változtatást végeztünk volna rajta.
  • filename.rNEWREV: Ez az a fájl, amit a Subversion kliens a szerverről letöltött, amikor az svn up parancsot futtattuk. Ez a fájl megegyezik a repository HEAD revisionjével.

Az OLDREV a .svn könyvtár, a NEWREV pedig a repoistory HEAD revision numberét jelenti.

 $ ls -1
 first.file
 first.file.mine
 first.file.r21
 first.file.r22

Ha most megpróbálunk commitolni, a Subversion nem fogja engedni:

$ svn ci -m 'try to commit'
svn: Commit failed (details follow):
svn: Aborting commit: '/devdirs/projects/MyProject1/trunk/first.file' remains in conflict

Konfliktus esetén három dolgot tehetünk:

  • Kézzel összeolvasztjuk a konfliktusban érintett fájlokat
  • Az egyik ideiglenes fájllal felülírjuk a munkafájlunkat
  • Futtatjuk az svn revert <filename> parancsot, amivel az összes változtatásunkat visszavonjuk

Ha egy konfliktust megoldottál, ezt tudatnunk kell a Subversionnel is, amire az svn resolved parancsot használhatjuk. Ez törli az ideiglenes fájlokat valamint az svn nem fogja konfliktusban lévőként érzékelni a fájlokat.

$ svn resolved first.file
Resolved conflicted state of 'first.file'

Kézi összeolvasztás

Kézi összeolvasztást akkor szoktuk használni, amikor mindkét verzió tartalmaz olyan módosításokat, amikre szükség van. Konfliktus esetén az svn konfliktus jelölőket használ a munkafájlunkban, melyek arra valók, hogy könnyen azonosítani tudjuk a különbségeket:

$ cat first.file
1. sor
2. sor
3. sor
4. sor
<<<<<<< .mine
John 5. sor
John 6. sor
John 7. sor
=======
Joe 5. sor
Joe 6. sor
Joe 7. sor
>>>>>>> .r25

Látható, hogy a kérdéses rész két szakaszra van osztva: az egyik a saját módosításainkat tartalmazza, a másik a HEAD-ben lévőket. Ha szeretnénk a saját és a HEAD változtatásait is megtartani, egyszerűen töröljük ki a jelölőket, futtassuk le az svn resolved parancsot, majd commitoljunk.

$ cat first.file
1. sor
2. sor
3. sor
4. sor
John 5. sor
John 6. sor
John 7. sor
Joe 5. sor
Joe 6. sor
Joe 7. sor

$ svn resolved first.file
Resolved conflicted state of 'first.file'

$ svn ci -m 'A sajat (John) modositasaink beolvasztasa Joe sorai ele'
Sending        first.file
Transmitting file data .
Committed revision 26.

Saját verziónk kikényszerítése

Ha úgy ítéljük meg, hogy a saját verziónk való inkább a repositoryba, akkor csupán elég felülírni a munkafájlt a módosításainkat tartalmazó ideiglenes fájllal:

$ svn up
C    first.file
Updated to revision 30.

$ ls first.file*
first.file first.file.mine first.file.r29 first.file.r30

$ cp first.file.r30 first.file

$ svn resolved first.file
Resolved conflicted state of 'first.file'

$ ls first.*
first.file

Commit és már kész is.

Szerveren lévő verzió elfogadása

Ha úgy gondoljuk, hogy a szerveren lévő verziónak van nagyobb létjogosultsága, akkor csupán futtassuk le az svn revert <filename> parancsot, ezzel visszavonva saját módosításainkat.

Változások visszakövetése (svn log)

Nem véletlenül ajánlott a message mező kitöltése, sokszor előfordul, hogy nem emlékszünk pontosan, hogy mely módosítást mikor végeztünk, viszont vissza szeretnénk állni az akkori állapotra. Ekkor jön segítségünkre a gondosan kitöltött message mező.

$ svn log
------------------------------------------------------------------------
r34 | ajnasz | 2007-08-16 00:43:21 +0200 (cs, 16 aug 2007) | 1 line

konyvtarstruktura kiegeszitesek
------------------------------------------------------------------------
r33 | ajnasz | 2007-08-12 18:21:12 +0200 (v, 12 aug 2007) | 1 line

typo, formazasok
------------------------------------------------------------------------

... és így tovább. Ebből már greppel könnyen megtalálhatjuk, hogy hol történt a változtatás. Természetesen megadhatunk revision numbereket is, hogy pl csak a 10-től 20-ig történt változtatásokat írja ki.

$ svn log -r 10:20

vagy ha fordított sorrendben akarjuk látni az eredményt:

$ svn log -r 20:10

Ha csak egy commit logját szeretnénk látni, akkor csak egy számot adjunk meg:

$ svn log -r 23

Ha az egyik szám helyett a HEAD kulcsszót adjuk meg, akkor azt a legutólsó commit revision numberének fogja venni. Így megtudhatjuk, hogy mik voltak a módosítások az utolsó commitkor:

$ svn log -r HEAD

A -v kapcsolóval részletesebb információt is kaphatunk, így megtudhatjuk, hogy mely fájlok voltak érintettek:

$ svn log -v -r HEAD
------------------------------------------------------------------------
r42 | ajnasz | 2007-09-02 16:47:31 +0200 (v, 02 szept 2007) | 1 line
Changed paths:
   M /trunk/svn-howto.wiki
 
lorem ipsum dolor sit
------------------------------------------------------------------------

Ha paraméterként egy fájl nevét adjuk meg, akkor csak a fájlt érintő változások lesznek kiírva.

$ svn log trunk/svn-howto.wiki

Ha szeretnénk megtudni, hogy mi változott egy távoli repositoryban, viszont nincs róla helyi másolatunk, mert nem szeretnénk azt letölteni, akkor elegendő megadni a tároló url-jét.

$svn log http://example.com/svn/file.c -r HEAD

Rengeteg módon kombinálhatjuk az opciókat, attól függően, mire van szükségünk.

Módosítások soronkénti áttekintése (svn diff)

Commitolás előtt jó tudni, hogy miket módosítottunk. Ennek megállapítására az svn az svn diff parancsa áll rendelkezésünkre

$ svn diff first.file
Index: first.file
===================================================================
--- first.file      (revision 43)
+++ first.file      (working copy)
@@ -511,7 +511,7 @@
Lorem ipsum dolor


- modositas
+ Módosítás
 
Lipsum
Lorem ipsum

Ha paraméter nélkül hívjuk meg az svn diff parancsot, akkor minden fájl módosítását kiírja vagy megtehetjük, hogy több fájlt is sorolunk fel. Megadhatjuk, hogy bizonyos revisionöket hasonlítson össze

$ svn diff -r 40:42 first.file

Repositoryt használva is elvégezhetjük az összehasonlítást, ekkor egy @ jellel jelölhetjük, hogy mely verziókra vagyunk kíváncsiak:

$ svn di http://example.com/svn/trunk@10 http://example.com/svn/trunk@12

Az svn diffel patchet is generálhatunk, csupán a kimenetet kell egy fájlba irányítani:

$ svn di > patch.file

Az SVN-nel lehetőség van arra, hogy más programot használjunk a diff feldolgozására, így pl színes kimenetet is generálhatunk, melynek könnyebb az áttekinthetősége. Ehhez először telepítsük pl a colordiff programot, majd a --diff-cmd colordiff opció megaádásval adjuk meg az SVN-nek, hogy ezt használja.

$ svn di --diff-cmd colordiff

Working copy helyének megváltoztatása (svn switch)

Néha előfordul, hogy valamilyen okból új elérési úton érhető el a repository, erről tudatni kell a helyi változatunk is:

$ svn switch --relocate http://example.com/regihely http://example.com/ujhely .

Felhasználókezelés

Ha azt szeretnénk, hogy a repositoryhoz többen is hozzáférjenek, érdemes beállítani a tárolóhoz való hozzáférést. Ha megnézzük a repository conf alkönyvtárában található svnserve.conf állományt, megtalálhatjuk a következő bejegyzést:

[general]
anon-access = read
auth-access = write

A Subversion kétféle felhasználó között tesz különbséget, az anon-access (nem kell felhasználónév és jelszó), illetve auth-access (felhasználónév és jelszó kötelező), illetve megadható jogosultságok lehetnek write (írás és olvasás), read (csak olvasás a tárolóból), illetve none (semmilyen művelet nem engedélyezett). A fenti példában az azonosított felhasználók letölthetik és a változtatásaikat közzétehetik a repositoryban, a felhasználónévvel nem rendelkező felhasználók pedig csak olvshatják annak tartalmát. A hozzáféréshez szükséges felhasználóneveket és jelszavakat a következő módon adhatjuk meg. Módosítsuk az svnserve.conf állomány password-db bejegyzését a következőképpen:

password-db = jelszo_allomany

Ezzel jeleztük, hogy a felhasználókat tartalmazó állományunkat jelszo_allomany -nak hívják, és az svnserve.conf fájllal egy könyvtárban található, természetesen más fájlnév és elérési út is megadható. Az állomány felépítése a következő:

[USERS]
felhasznalo = jelszo

Látható, hogy az [USERS] bejegyzés alatt a felhasználókat és a hozzá tartozó jelszavakat kell megadni, egymástól egyenlőségjellel elválasztva. A példában egy felhasznalo nevű felhasználót hoztunk létre, amelynek a jelszava jelszo lesz.

Parancsokról röviden

A helyi és a repositoryban lévő változatok közötti különbségeket az svn status paranccsal kérdezhetjük le. Ha a szerveren lévő verzió újabb, mint a nálunk lévő, az svn update paranccsal letölthetjük azt. Az általunk elvégzett módosításokat az svn commit tölthetjük fel a szerverre. Commitkor erősen ajánlott a message mező kitöltése, ezzel jelezhetjük magunknak és a projecten dolgozó munkatársainknak, hogy milyen változtatásokat eszközöltünk.

svn commit --message 'code tisztitas'

Ha hosszabb szöveget szeretnénk egy commmithoz megjegyzésként, esetleg kényelmetlen lehet a -m (vagy --message) használata. Ilyenkor az SVN_EDITOR környezeti változó megadásával lehetséges egy szövegszerkesztő program használata:

export SVN_EDITOR=vim

Új fájlokat az svn add paranccsal adhatunk a projecthez, fájlokat törölni az svn delete, régebbi verziót visszaállítani az svn revert paranccsal lehet. További gyakran használt parancsok: svn diff, svn update, svn help, stb. Az svn parancsok egy részét rövidíthetjük:

  • svn commit = svn ci
  • svn checkout = svn co
  • svn status = svn st
  • svn update = svn up
  • svn diff = svn di
  • svn delete = svn del vagy svn rm
  • svn move = svn mv
  • svn copy = svn cp
  • svn list = svn ls
  • svn switch = svn sw
  • stb.

Az SVN-bookot legalább egyszer mindenképpen olvassuk el, hogy megértsd az itt felsorolt megoldásokat és parancsokat, megismerjük az SVN a működését, filozófiáját és a benne rejlő lehetőségeket. Ez a rövid dokumentáció csak arra szolgál, hogy gyorsan létre tudj hozni egy működő svn repository-t, ezért nem helyettesíti a kézikönyv egyik szakaszát sem.

Függelék: Subversion szerver létrehozása

Miután telepítettük a rendszerünkre a Subversion csomagot, lehetőség nyílik az svn használatára hálózaton keresztül. Ehhez az svn három módszert állít rendelkezésünkre: Apache + mod_dav_svn, svnserve, svenserve over SSH.

Svnserve használata

Az svnserve programot több módon is futtathatjuk:

  • Egyszerű daemonként
  • (X)inetd-n keresztül
  • SSH tunnelen keresztül

A telepítés után elérhető svnserve program a tényleges szerverprogram, amely lehetővé teszi a repo -hoz való hozzáférést. Xinetd mód használata esetén szükség lesz még az xinetd programra, amely elindítja a szervert kapcsolódáskor.

SVN szerver daemonként

A legegyszerűbb módja, hogy egy SVN szervert elindítsunk az, ha az svnserve programot -d opcióval futtatjuk:

$ svnserve -d

Ha így futtatjuk az svnserve daemont, paraméterekkel szabályozhatjuk annak működését. Így például a --listen-port= és --listen-host= paraméterekkel szabályozhatjuk, hogy a daemon melyik porton és hoston figyeljen. A -r opcióval beállíthatjuk, hogy melyik az a könyvtár, ahol az SVN repositoryk vannak.

$ svnserve -d -r /devdirs/svnrepos

Ekkor az eddig használt file:// helyett az svn:// módon érhetjük el a repositoryt:

$ svn checkout svn://host.example.com/MyProject1

SVN szerver üzembe helyezése xinetd-n keresztül

Először hozzunk létre egy felhasználót, akinek a nevében a szerver futni fog, és állítsuk be a tulajdonosi jogokat a könyvtárra. A példában a felhasználó neve svnuser lesz.

$ /usr/sbin/useradd svnuser -d /devdirs
$ chown svnuser /devdirs

Alapértelmezetten a felhasználó home-könyvtára a /devdirs lesz, amely a repository -t tartalmazni fogja. Ezek után hozzunk létre a következő fájt: /etc/xinetd.d/svn Ez tartalmazza a szerver indításához szükséges adatokat, a tartalma legyen a következő:

service svn
{
    disable = no
    flags = REUSE
    socket_type = stream
    wait = no
    user = svnuser
    server = /usr/bin/svnserve
    server_args = -i -r /devdirs
}

A konfigurációs állományban az user résznél azt a felhasználónevet kell megadni, amelyet előzőleg létrehoztunk a tároló kezelésére. A létrehozott tároló helyét a szervernek átadott paramétereknél, a -r után kell megadni. Természetesen figyeljünk arra, hogy az svnuser felhasználónak legyen jogosultsága a könyvtár eléréséhez (olvasás, írás, hozzáférés). Ha ez megtörtént, indítsuk újra az xinetd -t, hogy a változtatások életbe lépjenek:

$ /etc/init.d/xinetd restart

SVN szerver futtatása Apache és webdav segítségével

Az Apache-on keresztüli futtatás elég sok eszközt ad a kezünkbe, így könyvtáranként vagy akár fájlonként is más hozzáférési jogosultságokat adhatunk meg, vagy akár egy böngészőből is böngészhetjük a repositorykat.

Mindenek előtt szükségünk lesz egy működő Apache(2) webszerverre valamint a hozzá tartozó mod_dav, mod_dav_svn, mod_authz_svn apache modulokra. Ezeket Debianon az apt-get install libapache2-svn paranccsal fel is telepíthetjük, majd az a2enmod dav_svn paranccsal engedélyezhetjük is.

A /etc/apache2/mods-enabled könyvtárban találhatjuk a konfigurációs fájlokat.

A következő konfiguráció anonymous felhasználók számára engedélyezi a repository olvasását, viszont commitolni csak a bejelentkezett felhasználók tudnak. A felhasználókat és a hozzá tartozó jelszavakat tároló fájlt a /etc/apache2/dav_svn.passwd fájlba tesszük, amit a htpasswd paranccsal hozhatunk létre.

<Location /svn>
  # repository engedélyezese
  DAV svn
 
  # repository elérési útja
  # ezt használjuk, ha csak egyetlen repositoryt akarunk engedélyezni
  # SVNPath /devdirs/svnrepos/MyProject1
 
  # repository root elérési útja
  #  ezt használjuk, ha az összes repositoryt elérhetővé akarjuk tenni
  SVNParentPath /devdirs/svnrepos
 
  # authentikació
  AuthType Basic
  AuthName "Subversion Repository"
  # auth file
  AuthUserFile /etc/apache2/dav_svn.passwd
 
  # engedélyezzük, hogy anonymous user tudja olvasni a repositoryt
  # de csak authentikalt user tudjon commitolni
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Require valid-user
  </LimitExcept>
</Location>

Fontos, hogy az Apache DocumentRootjában nem létezhet olyan könyvtár, aminek a neve megegyezik a Location értékével, pl. ez a példa esetén létezhet az svn könyvtár!

Apache konfiguráció újratöltés után az svn checkout http://example.domain.com/svn/MyProject1 paranccsal tudjuk letölteni a MyProject repositoryban található fájlokat.

SVN + SSH Windows alatt

Hozzávalók:

Töltsük le a két hozzávalót, csomagoljuk ki ahova tetszik, és jegyezzük meg nagyon az útvonalukat. A példában én a D:\progs\putty és a D:\svn útvonalakra csomagoltam ki a zipeket.

Készítsünk egy parancsikont, mely a PuTTY telepítési mappájában levő PAGEANT.EXE-re mutat. Ezt nevezzük el akárminek, a lényeg, hogy miután megvan, húzzuk be az Start menü Indítópult menüjébe (bátrabbak Registryből is betöltetthetik, akkor nem kell parancsikon).

Következő lépésben nyissuk meg a Sajátgép -> Jobb gomb -> Tulajdonságok ablakát, majd a Speciális fülön alul környezeti változók gombra kattintsunk.

A rendszer változói küzül keressük ki a PATH-et majd tegyük be a két program telepítési helyét. Én például az alábbi sztringet fűztem a végére:

D:\progs\putty;D:\svn\bin

OK

Most a felhasználó változói közé hozzunk létre egy SVN_SSH nevű változót, a tartalma valami ilyesmi legyen:

plink -ssh -agent -l hron -load Peter -P 30222

Kifejtve:

  • plink: SSH program neve
  • -ssh: SSH kapcsolatot akarunk
  • -agent: Használd a PAGEANT-ot.
  • -l: Felhasználónév
  • -load: A paraméterül kapott nevű munkamenet adatait fogja használni a csatlakozáshoz. Értelemszerűen a PAGEANT-ba léteznie kell ilyen munkamenetnek.
  • -P: Port, ha nem standard porton figyel az SSH. Úgy néz ki, a session-ból ezt az egy opciót nem veszi át.

A PAGEANT-ba konfiguráljuk fel az SSH kapcsolatunkat az SVN serverrel, és mentsük el olyan néven, amit a -load paraméter után adtunk meg (esetemben Peter). Ne használjunk ékezetet illetve szóközt a névben.

Az SSH-ba kétféleképp lehet authentikálni, tehát a következő két szakaszból csak a releváns beállításokat kell megtenni.

1) SSH authentikáció jelszóval
Ha nem kulccsal, hanem jelszóval authentikálunk a szerveren, akkor a SVN_SSH végére oda kell bökni egy ilyet:
-pw ezegynagyontitkosjelszo
2) SSH authentikáció jelszó nélkül, kulccsal.
Kulcsos authentikációnál a helyzet kissé bonyolultabb, de távolról sem vészes.
Először is, győződjünk meg arról, hogy a munkamenetünkbe bekerült a SSH kulcsunk holléte is (Pageant: Connection - SSH - Auth), ha nem, ezt pótoljuk, és mentsük el ismét a munkamenetet.
Ezután a PAGEANT óra melletti ikonján jobbat kattintva válasszuk az Add Key menüt és tallózzuk ki a privát kulcsunkat. Ha jelszavas a kulcs, akkor ennél a pontnál rá fog kérdezni a PAGEANT a kulcshoz tartozó jelszóra.
Ezt a műveletet sajnos minden rendszerindítás után egyszer meg kell tenni, mert a PAGEANT elfelejti a hozzáadott kulcsokat, enélkül pedig lehetetlen authentikálni a szerverrel.

Nagyjából kész is vagyunk. Nyissunk egy teljesen új konzolt (figyelem! Ne használjunk már megnyitott konzolt most, mert az még nem ismeri az új beállításainkat!), és máris használhatjuk az svn szervert.

Ami nagyon fontos: mivel egy kicsit másképpen működnek a dolgok ezzel az összeállítással, így a SVN URL-ekben ne adjunk meg gépnevet vagy felhasználónevet. Ami tehát alapból így néz ki:

svn+ssh://hron@svn.mittudomain.hu/srv/svn/repos/hron/

Abból jelenleg csak ennyi marad:

svn+ssh:///srv/svn/repos/hron/

Lévén a PAGEANT-ban már megadtuk a szervert, a SVN_SSH változóban pedig a nevünket.

Ráadás: Hogyan tudom használni a linux alatt generált privát kulcsomat windows alatt? Nagyon egyszerűen. Fell kell másolni a privát kulcsot valami ideiglenes helyre, majd a putty telepítési mappájából el kell indítani a puttygen.exe-t. File -> Load private key, elkéri a jelszót, majd File -> Save private key, és .ppk kiterjesztéssel tetszőleges névvel el lehet menteni. Az így kapott kulcsot aztán meg lehet adni a PAGEANT-nak.

Annyit érdemes hozzátenni még, hogy bár többféle SSH-s SVN szerverrel lehet használni, mindegyiken ugyanannak kell lenni a felhasználónévnek, és ugyanott kell figyelni az SSH-nak is. Ellenkező esetben a SVN_SSH környezeti változó okszerűen módosítandó a set parancs használatával.

Az SVN_SSH-ból nyugodtan kiugorhat a '-l username' és a '-load session' rész, amennyiben nem a sessionhöz mentjük el a kulcsunkat, hanem globálisan. Ennek menete a következő:

Az Indítópultba behúzott PageAnt ikonon jobbklikkel kérjük le a tulajdonságait, és a cél megadásánál a parancsot egészítsük ki a PuttyGennel bekonvertált kulcsunk teljes elérési útjával (D:\progs\putty\id_dsa.ppk). (Elvben ha több kulcsunk van, akkor itt sorban meg lehet adni a kulcsok elérési útját, a pageant lesz szíves azokat betölteni, FIXME).

Ekkor a PageAnt minden loginkor megkérdezi a kulcshoz való jelszavunkat (idegesítő, de le is lehet altatni a gépet - akkor nem kéri). Ezzel a módszerrel a régi stílusú svn+ssh://user@svn.mittudomain.com/srv/svn/repos/userke címek érvényesek lesznek megint. Az új svn és putty képes egymás közt lekomunikálni, hogy milyen szerver és milyen user. A kulcsot pedig intelligensen kiválasztja.

Arra figyeljünk, hogy ha az SSH nem standard porton figyel, mindenképpen legyen -P argumentum az SVN_SSH környezeti változóban. További körültekintést igényelnek a szóközt tartalmazó elérési utak - ezeket idészőjelek közé kell tenni.

Jelszavas SSH esetén pedig nem kötelező a jelszót megadni - viszont jelszó cache nincs, minden commitkor meg kell adni a jelszót -> érdemes megfontolni a publikus kulccsal való authentikációt.

Forrás

A leírás alapjául az ajnasz.hu oldalon megjelent cikk szolgált. Ez annak egy lényegesen kibővített változata. A leírás pár fejezete a lacix.hu oldalon szereplő dokumentáció alapján lett kiegészítve, vagy kidolgozva. Az SVN + SSH Windows alatt szekciót hrgy84 készítette és tette közzé hupos blogjában.