Subversion

De la Wiki.lug.ro
Salt la: navigare, căutare

Introducere

Procesul de "revision control" este folosit pentru managementul multiplelor versiuni ale unei unităţi de informaţii. Este întâlnit în software development pentru a trasa evoluţia documentelor cum ar fi codurile sursa, etc. Acest proces apare în literatura englezească sub diverse denumiri cum ar fi "version control", "revision control" sau "source control".

Direct asociat cu acesta este conceptul de "repository". În lipsa unui termen românesc consacrat îl voi referi în acest document drept "repositoriu". Din punct de vedere practic, un repozitoriu este un sistem de fişiere care acumulează temporal toate modificările care se fac şi asigură reproducerea stării sistemului de fişiere pentru orice moment specific din trecut.

Să presupunem că lucrăm intens la un fişier sursă, îl modificăm iar două luni mai târziu decidem că am luat-o razna şi dorim să revenim la versiunea iniţială. Evident, un sistem de fişiere precum ext2 sau ext3 nu ne premite să facem acest lucru. De aici şi necesitatea unui repozitoriu temporal.

Subversion este un program open source care implementează un sistem de fişiere temporal/repozitoriu. În decursul timpului multe asemenea programe au fost dezvoltate, subversion se vrea în principal un înlocuitor modern al lor, şi în special al repozitoriului CVS. Comparat cu CVS, pe lângă o serie întreagă de optimizări, subversion foloseşte atomic commits, tagging şi branching în timp constant şi suportă şi fişiere binare.

Descriu în continuare modul de utilizare a unui repozitoriu subversion pentru proiecte mici precum cele cu care ne jucăm acasă. În acest repozitoriu obişnuiesc să-mi salvez pe lângă fişiere surse şi informaţie de configurare a sistemului, poze şi tot felul de alte fişiere. Repozitorul se găseşte fizic pe maşina pe care lucrez şi este arhivat periodic. Opţiunile de setare a repozitorului ca server în reţea nu sunt discutate.


Instalare

Pentru storage-ul intern al datelor în repozitoriu aveţi de ales între două tipuri de baze de date. Primul se numeşte fsfs şi a fost implementat special pentru acest proiect, al doilea este Berkeley DB. Comparat cu Berkeley DB, fsfs aduce o serie de îmbunătăţiri (vezi http://web.mit.edu/ghudson/info/fsfs), este însă relativ imatur şi în continuă dezvoltare.

În Gentoo Linux alegerea unuia sau altuia se face folosind useflag-ul berkdb:

# emerge --pretend --verbose subversion

These are the packages that I would merge, in order:

Calculating dependencies ...done!
[ebuild   R   ] dev-util/subversion-1.2.3-r2  -apache2 -bash-completion\
+berkdb -emacs   -java -nls -nowebdav +perl +python +zlib 0 kB

Instalez implicit folosind berkdb:

# emerge subversion

Diverse distribuţii binare de Linux folosesc pentru subversion un tip de bază de date sau altul, însă în general Berkeley DB este preferat. În cazul distribuţiilor bazate pe RPM, o posibilitate de instalare ar începe cu downloadarea şi instalarea următoarelor pachetelor binare de la http://summersoft.fay.ar.us/pub/subversion

apache*.i386.rpm (Version 2.0.49 or greater)
db*.i386.rpm     (Version 4.0.14 or greater; version 4.2.52 is preferred however)
expat            (Comes with RedHat)
neon             (Version 0.24.7)

Evident, aveţi de downloadat şi subversion*.i386.rpm. Instalarea se face ca user root în felul următor:

# rpm -ivh subversion*.386.rpm

Iniţializarea repozitorului

Primul pas după instalare este să iniţializăm repozitorul şi să-l populăm. Iniţializarea se face folosind comanda svnadmin:


# svnadmin create /home/myself/repository
# ls /home/myself/repository
conf/  dav/  db/  format  hooks/  locks/  README.txt
#

Aceasta crează un director /home/myself/repository unde vor fi stocate datele. Apoi începem să-l populăm. Pentru aceasta creem un director intitulat mai jos "tree" unde copiem toate fişierele de care avem nevoie. Directorul îl structurăm în subdirectoare, fiecare dintre ele conţinând un proiect diferit:

# ls -R tree
tree:
project1  project2  tags

tree/project1:
Makefile  file1.c  file2.c

tree/project2:
Makefile  file.c

tree/tags:

#

project1 şi project2 sunt două proiecte la care lucrez în mod curent. Scopul directorului tags va fi descris mai târziu. Pentru a salva această structură de fişiere în repozitor folosim comanda svn import:

# svn import tree file:///home/myself/repository --message "initial import"
Adding         tree/project1
Adding         tree/project1/file1.c
Adding         tree/project1/file2.c
Adding         tree/project1/Makefile
Adding         tree/project2
Adding         tree/project2/file.c
Adding         tree/project2/Makefile
Adding         tree/tags

Committed revision 1.
#

Cu aceasta repozitorul este creat. Nu mai rămâne decât să-l arhivăm şi să-l salvăm undeva, de exemplu:

# tar -czvf repository.tgz /home/myself/repository
# cp repository.tgz /mnt/backup/.


Ciclu simplu de muncă

Prima operaţie este operaţia de checkout. În directorul de lucru folosim comanda svn checkout:

# svn checkout file:///home/myself/repository
A    repository/project1
A    repository/project1/file1.c
A    repository/project1/file2.c
A    repository/project1/Makefile
A    repository/project2
A    repository/project2/file.c
A    repository/project2/Makefile
A    repository/tags
Checked out revision 1.
#

Avem în acest moment un nou director intitulat repository care conţine toată structura de fişiere introdusă anterior în subversion.

# ls -R repository
repository:
project1  project2  tags

repository/project1:
Makefile  file1.c  file2.c

repository/project2:
Makefile  file.c

repository/tags:
#

repository devine noul nostru director de lucru, deci "cd repository". Putem să mergem acum în directorul project1 şi să modificăm fişierele file1.c şi file2.c. În orice moment putem reveni în directorul de lucru (repository) şi putem lista modificările folosind comanda svn status:

# svn status
M      project1/file1.c
M      project1/file2.c
#

Când am terminat, din directorul de lucru introducem comanda svn commit:

# svn commit --message "modified project1"
Sending        project1/file1.c
Sending        project1/file2.c
Transmitting file data ..
Committed revision 2.
#

Cu aceasta, toate modificările au fost salvate în repozitorul subversion, putem chiar şterge directorul de lucru - îl vom recrea din nou într-un nou ciclu de muncă.


Adăugarea unui nou proiect

Ne-am ales să ne mapăm fiecare proiect în repositorul subversion ca un director separat. Adăugarea unui nou proiect constă în crearea noului director de proiect şi popularea lui. Ostilităţile decurg similar cu "ciclul simplu de muncă" descris mai sus:

# svn checkout file:///home/myself/repository
A    repository/project1
A    repository/project1/file1.c
A    repository/project1/file2.c
A    repository/project1/Makefile
A    repository/project2
A    repository/project2/file.c
A    repository/project2/Makefile
A    repository/tags
Checked out revision 2.
# cd repository
# mv ../boxconfig .   <--- aici aducem noul proiect in directorul de lucru -->
# svn add boxconfig
A         boxconfig
A         boxconfig/etc
A         boxconfig/etc/cron.hourly
A         boxconfig/etc/cron.hourly/newpkgs.cron
A         boxconfig/etc/cron.hourly/ntpdate.cron
A         boxconfig/etc/cron.daily
A         boxconfig/etc/cron.daily/inuse
A         boxconfig/etc/cron.daily/logrotate.cron
A         boxconfig/etc/cron.daily/rkhunter
# svn status
A      boxconfig
A      boxconfig/etc
A      boxconfig/etc/cron.hourly
A      boxconfig/etc/cron.hourly/ntpdate.cron
A      boxconfig/etc/cron.hourly/newpkgs.cron
A      boxconfig/etc/cron.daily
A      boxconfig/etc/cron.daily/logrotate.cron
A      boxconfig/etc/cron.daily/rkhunter
A      boxconfig/etc/cron.daily/inuse
# svn commit --message "add boxconfig project"
Adding         boxconfig
Adding         boxconfig/etc
Adding         boxconfig/etc/cron.daily
Adding         boxconfig/etc/cron.daily/inuse
Adding         boxconfig/etc/cron.daily/logrotate.cron
Adding         boxconfig/etc/cron.daily/rkhunter
Adding         boxconfig/etc/cron.hourly
Adding         boxconfig/etc/cron.hourly/newpkgs.cron
Adding         boxconfig/etc/cron.hourly/ntpdate.cron
Transmitting file data .....
Committed revision 3.
# cd ..
# rm -fr repository

Am început în mod clasic cu un svn checkout, apoi în directorul de lucru am creat un director nou intitulat boxconfig care conţine o parte din configuraţia /etc a maşinii Linux pe care lucrez. Apoi am informat subversion de apariţia noului director folosind comanda svn add. Un simplu svn status ca să vedem dacă totul este în ordine, urmat de un svn commit şi cu asta totul este gata, putem şterge directorul de lucru.


Comenzi subversion

După un svn checkout putem modifica oricare din fişierele existente. Pentru orice modificare a structurii de fişiere, trebuie însă să informăm subversion. Astfel, folosim comenzile svn în locul comenzilor uzuale furnizate de sistemul de fişiere pe care lucrăm (în loc de rm folosim svn delete de exemplu).

Am văzut mai sus cum svn add ne permite să adăugăm un director nou cu toate fişierele pe care acesta le conţine. Aceeaşi comandă poate fi folosită pentru adăugarea de fişiere noi:

#svn add filename

Toate comenzile pe care un sistem de fişiere le suportă sunt suportate în subversion. Astfel, dacă dorim să mutăm un fişiere dintr-o parte în alta folosim comanda

#svn move filename1 filename2

iar dacă dorim să ştergem un fişier folosim comanda

#svn delete filename

Pentru copiere folosim

# svn copy source destination

Acestea sunt comenzile cele mai folosite, o listă completă poate fi obţinută folosind

# svn --help
usage: svn <subcommand> [options] [args]
Subversion command-line client, version 1.2.3.
Type 'svn help <subcommand>' for help on a specific subcommand.

Most subcommands take file and/or directory arguments, recursing
on the directories.  If no arguments are supplied to such a
command, it recurses on the current directory (inclusive) by default.

Available subcommands:
   add
   blame (praise, annotate, ann)
   cat
   checkout (co)
   cleanup
   commit (ci)
   copy (cp)
   delete (del, remove, rm)
   diff (di)
   export
   help (?, h)
   import
   info
   list (ls)
   lock
   log
   merge
   mkdir
   move (mv, rename, ren)
   propdel (pdel, pd)
   propedit (pedit, pe)
   propget (pget, pg)
   proplist (plist, pl)
   propset (pset, ps)
   resolved
   revert
   status (stat, st)
   switch (sw)
   unlock
   update (up)

Subversion is a tool for version control.
For additional information, see http://subversion.tigris.org/
#

Pentru a afla mai multe despre o comandă specifică folosim

#svn --help comandă

Versiuni

Când un repositoriu subversion este creat acesta apare ca având versiunea 0. Versiunea este globală, şi se aplică tuturor fişierelor din repozitoriu. Fiecare svn commit va incrementa această versiune. Versiunea curentă a directorului de lucru poate fi verificată în mod simplu folosind comanda svs log:

# svn log
------------------------------------------------------------------------
r3 | myself | 2006-02-16 10:16:43 -0500 (Thu, 16 Feb 2006) | 1 line

add boxconfig project
------------------------------------------------------------------------
r2 | myself | 2006-02-16 10:08:29 -0500 (Thu, 16 Feb 2006) | 1 line

modified project1
------------------------------------------------------------------------
r1 | myself | 2006-02-16 09:57:43 -0500 (Thu, 16 Feb 2006) | 1 line

initial import
------------------------------------------------------------------------
#

Aceasta ne listează toate versiunile (r1, r2, r3) şi mesajele specificate de opţiunea --message când commit a avut loc. r3 este versiunea curentă a directorului de lucru. Mai multe informaţii despre versiunea curentă sunt furnizate de comanda svn info:

# svn info
Path: .
URL: file:///home/myself/repository
Repository UUID: 6f3a1ea4-ea0c-0410-b67b-ebc3b21bbff1
Revision: 3
Node Kind: directory
Schedule: normal
Last Changed Author: myself
Last Changed Rev: 3
Last Changed Date: 2006-02-16 10:16:43 -0500 (Thu, 16 Feb 2006)
#

Dacă este necesar, mutarea directorului de lucru curent la ultima versiune se face folosind comanda svn update:

# svn update
At revision 3.
#

(nimic nu s-a schimbat, ne aflam deja la versiunea 3).

Dacă dorim să ne întoarcem în timp, putem crea un director de lucru specific pentru o versiune anterioară:

# svn checkout file:///home/myself/repository --revision 2
A    repository/project1
A    repository/project1/file1.c
A    repository/project1/file2.c
A    repository/project1/Makefile
A    repository/project2
A    repository/project2/file.c
A    repository/project2/Makefile
A    repository/tags
Checked out revision 2.
#

Un update în acest moment ne-ar muta la versiunea 3:

# svn update
A    boxconfig
A    boxconfig/etc
A    boxconfig/etc/cron.daily
A    boxconfig/etc/cron.daily/logrotate.cron
A    boxconfig/etc/cron.daily/rkhunter
A    boxconfig/etc/cron.daily/inuse
A    boxconfig/etc/cron.hourly
A    boxconfig/etc/cron.hourly/ntpdate.cron
A    boxconfig/etc/cron.hourly/newpkgs.cron
Updated to revision 3.
#

Tot pentru versiuni anterioare putem să folosim şi ziua şi/sau ora:

# svn checkout --revision {2006-02-16}
# svn checkout --revision {15:30}
# svn checkout --revision {"2006-02-16 15:30"}

Dacă nu specificăm opţiunea --revision, obţinem ultima versiune în mod implicit.


Tag-uri

Modul de denumire a versiunilor de software pe canalele uzuale de distribuţie variază de la proiect la proiect. Întâlnim în mod uzual denumiri precum Kernel-2.6.15 sau hardened-sources-2.6.14-r3. Subversion nu încearcă să impună un mod de denumire sau altul, şi oferă un mecanism simplu de a rezolva această problemă folosind tag-uri.

Să zicem că amicul meu Gigel ar vrea să folosească configurarea mea de calculator pe care o am în boxconfig. Problema este boxconfig se modifică periodic, pe măsură ce îmi modific configurarea la mine pe maşină. Ar fi interesant de ştiut ce i-am trimis în cazul în care Gigel revine cu probleme şi vrea mai mult.

Am creat iniţial un director tags în care folosind un simplu svn copy pot salva configuraţia lui Gigel.

# svn copy boxconfig tags/boxconfig-Gigel-r1
A         tags/boxconfig-Gigel-r1
# ls -R tags
tags:
boxconfig-Gigel-r1

tags/boxconfig-Gigel-r1:
etc

tags/boxconfig-Gigel-r1/etc:
cron.daily  cron.hourly

tags/boxconfig-Gigel-r1/etc/cron.daily:
inuse  logrotate.cron  rkhunter

tags/boxconfig-Gigel-r1/etc/cron.hourly:
newpkgs.cron  ntpdate.cron
#

Copierea este o operaţie foarte ieftină în subversion. Intern, este practic echivalentă cu crearea unor link-uri hard pe un sistem de fişiere normal. Astfel, baza de date nu creşte în dimensiuni. După un svn commit am salvat astfel versiunea lui Gigel fără să-mi bat capul cu modul de denumire folosit intern de subversion descris în capitolul precedent. În cazul în care Gigel vine din nou cu alte cereri, nu am decât să creez un nou tag intitulat boxconfig-Gigel-r2.

În mod similar (svn copy) sunt suportate de către subversion şi branch-urile. Comenzi precum svn diff/merge/revert uşurează maintenanţa acestor branch-uri.

Încheiere

Pe scurt, ce ne place la subversion:

- operarea repozitorului folosind o serie mică şi uşor de învăţat de comenzi;

- atomic commit: toate fişierele modificate într-o sesiune de lucru sunt introduse în acelaşi timp în baza de date; aceasta este deosebit de utilă în cazul în care mai mulţi developeri lucrează pe acelaşi repozitoriu;

- branching şi tagging foarte ieftin, baza de date nu creşte în mod semnificativ;

- repozitorul poate fi folosit local şi arhivat cu uşurinţă

- suport pentru fişiere binare în repozitoriu, ceea ce ne permite să folosim subversion şi pentru alte scopuri decât software development.

Nu s-a discutat despre setarea unui repozitoriu ca un server pe reţea, ne-am limitat în mod exclusiv la folosirea lui locală. Subversion permite un astfel de setup, fie direct ca un server subversion sau prin intermediul server-ului Apache. De asemenea există în momentul de faţa o serie întreagă de pachete client subversion grafice care pot înlocui linia de comandă.

Have fun!


Mai multe resurse

Homepage: http://subversion.tigris.org/

Carte online (O'Reilly Media): http://svnbook.red-bean.com/

Articol despre subversion pe RedHat site: http://www.redhat.com/magazine/010aug05/features/subversion/

Discuţie Slashdot: http://ask.slashdot.org/askslashdot/06/02/16/2344228.shtml