Subversion

Fra Wikipedia, den frie encyklopedi.

Subversion er et versjonskontrollsystem som er ment å erstatte CVS. Et versjonskontrollsystem har som oppgave å holde styr på endringene en ressurs gjennomgår. Ressursen kan være et sett med kildekodefiler, men kan like gjerne være helt andre ting – som skolearbeid, websider etc. Subversion kan sees på som en avansert filtjener som holder rede på alle endringer som blir gjort, og lar deg gå tilbake og se på alle tidligere tilstander av filsystemet. Om det oppstår problemer, kan man på denne måten gå tilbake til tidligere versjoner, undersøke hvem som har gjort endringer, og hente ut informasjon om hvorfor endringene er gjort ved hjelp av kommentarer som legges inn ved hver revisjon.

Innholdsfortegnelse

Hvorfor versjonskontroll?

Forenklet kan man si at Subversion overvåker et sett med filer over tid. Filene er lagret i et såkalt arbeidslager (eng. repository), som på mange måter kan sammenliknes med et filsystem – bortsett fra at det inneholder alle endringene som gjøres over tid. Subversion lagrer fildataene i en database. På sett og vis kan man se på Subversion som en tidsmaskin som lar deg reise tilbake og se på tilstanden til ulike ressurser på ulike tidspunkter.

Kanskje minst like viktig er muligheten for å la flere personer jobbe på samme prosjekt samtidig. Man kan se for seg forvirringen som kan oppstå dersom flere personer jobber på samme fil på samme tid. Endringene kan lett komme i konflikt med hverandre ettersom folk arbeider med hver sin lokale kopi, eller dersom man «låser» ressurser, kan det tenkes at enkelte ikke får gjort det de skal. Å manuelt hindre slike konflikter er ikke enkelt; selv i små grupper kan det fort bli tull. I tillegg er arbeidsflyten enklere når personene i et prosjekt oppdaterer sin lokale kopi framfor å drive med «.DOC-fil-stafett».

En annen fordel er muligheten til å jobbe på samme prosjekt fra flere steder, og hele tiden kunne holde ressurser synkroniserte.

Ulike versjonskontrollmetoder

Lås – modifiser – lås opp

Man kan tenke seg en løsning der en ressurs blir «låst» så lenge noen arbeider på den. Problemet med denne løsningen er at det kan hindre produktivitet (personer kan bli sittende og vente på at ressursen skal bli ledig). I tillegg, men ikke fullt så alvorlig, kan det føre til administrativt merarbeid dersom noen glemmer, eller av andre grunner ikke kan «låse opp», en ressurs når de er ferdige med å modifisere den.

Kopier – modifiser – slå sammen

Subversion bruker en annen modell, der man først henter ut en arbeidskopi av en ressurs fra det sentrale lageret. Man gjør så de endringene man skal gjøre, og sender så ressursen tilbake til lageret. Dersom noen andre har gjort endringer på ressursen i mellomtiden, vil dette bli flagget slik at man kan vurdere om endringene har konsekvens for hverandre, og eventuelt rette på dette. Når man har sikret at ting ikke er i konflikt, kan man slå sammen endringene. Som oftest er faktisk dette ikke et problem; folk arbeider normalt på ulike deler av filer, og versjonskontrollsystemet er ofte i stand til å slå sammen filene uten problemer på egen hånd. Ved konflikter vil Subversion påpeke hvor problemet ligger, så man manuelt kan fikse problemet.

Viktige begreper

Import

Når et prosjekt opprettes, importeres innholdet i prosjektet over til et lager.

Arbeidskopier (checkout)

Når man først begynner å arbeide på et prosjekt, sjekker man ut en arbeidskopi av ressursene man trenger. Man får da en kopi av et filtre på sitt eget filsystem. I tillegg lagrer Subversion en del metadata om filene, som legges i en katalog som heter «.svn» i hver katalog som hentes. Det er svært viktig at denne katalogen ikke slettes, ellers vil muligheten til å sende tilbake endringer forsvinne.

Sende tilbake endringer (commit)

Når man har gjort de endringene man ønsker, kan man sende filene tilbake til lageret. Det kan da tenkes at andre har gjort endringer på samme ressurser i mellomtiden. Subversion vil flagge dette og la brukeren slå sammen endringene, og deretter sende den sammenslåtte versjonen tilbake.

En slik innsending av endringer kan berøre både en enkelt fil, flere filer eller kataloger i arbeidskopien. Subversion lar brukeren gjøre alt dette i en enkelt atomisk transaksjon. Med atomisk menes det at transaksjonen enten blir gjennomført 100 % eller ikke i det hele tatt.

Revisjoner

For hver enkelt innsending som blir gjort, vil Subversion lage en revisjon av filsystemet. En revisjon er en tilstand som kan hentes fram senere. Merk at revisjoner er globale for alle prosjekter i lageret – om det er flere prosjekter der, vil revisjonen gjelde for alle prosjektene (selv om det ikke er endringer i de andre prosjektene).

Grener

I løpet av levetiden kan det tenkes at et prosjekt splittes opp i flere grener. Subversion har innebygget funksjonalitet for å lettere holde styr på ulike grener i et prosjekt.

En gren lages ved å lage en kopi av en revisjon, og definere hvor denne skal legges.

Arbeidsflyt med versjonskontrollsystemer

Den første hendelsen i versjonskontrollhistorien til et prosjekt er import av prosjektet til et lager. Etter at det er gjort, kan ulike brukere sjekke ut koden og dermed lage sin egen arbeidskopi.

Hver bruker følger så ett sett med forhåndsdefinerte trinn for hver gang de starter på arbeidet:

  1. Oppdater arbeidskopien
  2. Utfør endringer
  3. Sjekk status på egne endringer
  4. Løs ev. konflikter med andres endringer i lageret
  5. Dersom noen av endringene viser seg å være feil, kan man revertere enkeltfiler eller hele arbeidskopien
  6. Send inn endringer til lageret

Administrasjon av lager

Før man begynner

Bruk av selve Subversion-lageret krever at det kjører en tjener som tar seg av innkommende henvendelser. Denne tjeneren bør ikke kjøre som root, men som en egen systembruker. Det anbefales derfor at man oppretter en bruker, f.eks. «svn», som skal brukes til å kjøre tjeneren. I tillegg bør svn-lagrene legges i en egen katalog, for eksempel /var/svn. Eksempel:

# useradd svn
# groupadd svn
# mkdir /var/svn; chown svn:svn /var/svn
# passwd svn

I de følgende avsnittene, som omhandler administrasjon, bør man gjøre operasjoner som brukeren svn for å opprettholde konsistens på filrettigheter i lageret:

# su - svn
# <skriv inn passord>

Opprettelse av lager

Før man kan importere prosjekter og begynne å arbeide med arbeidskopier, må lageret opprettes. Vi skal i artikkelen forutsette at man har installert Subversion og har de nødvendige verktøyene på plass.

Verktøyet som brukes for å administrere lokale lager kalles svnadmin. svnadmin har mange ulike oppgaver, som man kan se ved å skrive svnadmin help. Merk at svnadmin forventer å få lokale filstier som parameter, i motsetning til andre verktøy som vi skal se på om en kort stund. For å opprette et lager på lokal disk (lager må ikke plasseres på nettverksressurser på grunn av potensielle problemer med filsystemoperasjoner), kan man kjøre denne kommandoen:

# svnadmin create /var/svn/repository

Dette vil opprette katalogen «repository» under /var/svn, og i tillegg opprette en database og noen grunninnstillinger i denne katalogen.

Innstillinger for lageret

Inne i lagerets katalog vil det befinne seg noen underkataloger. De to viktigste er «conf» og «db». Førstnevnte inneholder konfigurasjonsinnstillingene for lageret. Den andre inneholder en database som inneholder hele lageret – denne katalogen må ikke endres på manuelt.

Vi skal opprette en lokal bruker av lageret og sette et passord for denne brukeren. I «conf»-katalogen er det en fil som heter «svnserve.conf», og som ser omtrent slik ut:

Fil: svnserve.conf
# General er den eneste delen av denne fila vi går inn på.
[general]

# Brukere uten tilgang skal kun ha lesetilgang
anon-access = read

# Autentifiserte brukere kan sende inn endringer
auth-access = write

# Et filnavn som passordene skal legges i
password-db = passwd

# Beskrivelse av «området» som passordfila skal gjelde for. Lager med samme område må ha samme passordfil.
realm = svn.domene.com


Tilsvarende må vi opprette fila «passwd» i samme katalog. Den skal se slik ut:


Fil: passwd
# En seksjon
[users]
# En bruker med passord
svnuser = passord

Oppstart av tjener

For å starte tjeneren, kjøres kommandoen:

# svnserve -r /var/svn/repository -d

-r angir at tjeneren skal ha rot-nivå i angitt katalog. -d angir at man skal kjøre som tjener i bakgrunnen. svnserve lytter som standard på TCP-port 3690.

Arbeidsflyt i praksis med Subversion

Import av et prosjekt

Det første trinnet i versjonskontrollhistorien til et prosjekt er import av prosjektet til lageret. Vi skal i eksemplene her benytte oss av et prosjekt som heter «testprosjekt», og som har følgende katalog/filstruktur:

--testprosjekt
    |-- branches
    |-- tags
    `-- trunk
        |-- Makefile
        `-- test.c

Vi har en overordnet prosjektkatalog, «testprosjekt», som inneholder tre underkataloger: «branches», «tags», og «trunk». «trunk» inneholder kildekoden som vi jobber med (vi kommer tilbake til årsakene for denne strukturen).

For å legge denne strukturen inn i lageret vårt, kjører vi kommandoen svn med korrekte parametre (vi står nå i katalogen som inneholder «testprosjekt»):

# svn import testprosjekt svn://localhost/testprosjekt --message 'Import av testprosjekt'

Den første parameteren til svn-kommandoen angir at vi skal gjøre en import. Deretter kommer navnet på katalogen som skal importeres til lageret. Den tredje parameteren er en URL som viser hvor prosjektet skal legges i lageret. Siden det er et lokalt lager, bruker vi «localhost» som vertsnavn. Til slutt angir vi en beskjed som skal annoteres til denne innsendingen av data. All innsending av data krever at man legger ved en slik beskjed (som dog kan være tom, men dette anbefales ikke).

For å se om ting gikk som de skulle, kan vi teste en annen mulighet som svn-kommandoen gir: Listing av lager.

# svn list svn://localhost/
testprosjekt

Utsjekk

Når prosjektet nå er lagt inn på lageret, kan brukerene begynne å sjekke det ut. Dette gjøres med kommandoen svn checkout:

# svn checkout svn://localhost/testprosjekt/
A  testprosjekt/trunk
A  testprosjekt/trunk/test.c
A  testprosjekt/trunk/Makefile
A  testprosjekt/branches
A  testprosjekt/tags
Sjekket ut revisjon 89.

Her svarer svn med en rekke linjer, en for hver fil eller katalog som finnes i URL-en som angis i kommandoen. Legg merke til revisjonsnummeret, som økes for hver eneste innsending av data som gjøres mot lageret (uavhengig av prosjekt). Med andre ord har dette lageret vært i bruk før. Bokstaven A står for «Added», som betyr at en fil ble hentet fra lageret og lagt til i den lokale kopien. Den lokale kopien vil, i tillegg til de opplistede ressursene, også inneholde en katalog som heter «.svn» i hver underkatalog til den lokale kopien. Denne må ikke endres på, siden den inneholder metadata som Subversion skal benytte til å holde kontroll på revisjoner og endringer.

Oppdatering

Hver gang man starter å jobbe på en ressurs, er det svært viktig å oppdatere den lokale arbeidskopien i tilfelle andre brukere har gjort endringer siden sist man sendte inn sine egne data. La oss se på hva som skjer med en oppdatering når en annen bruker har lagt til en fil i lageret (vi skal se på hvordan dette gjøres senere). Merk at vi nå står inne i «testprosjekt»-katalogen (den lokale arbeidskopien starter i første underkatalog):

# svn update
A  trunk/header.h
Oppdatert til revisjon 90.

Her ser vi at en fil «trunk/header.h» har blitt lagt til (A betyr som nevnt «added»). Hvordan ser det ut dersom en ressurs har blitt endret?

# svn update
U  trunk/Makefile
Oppdatert til revisjon 91.

Nå kommer det en annen bokstavkode – U – som står for «updated». Det betyr at noen har endret på denne fila siden sist vi oppdaterte eller sendte inn data. Vi ser også av revisjonsnummeret at det er den eneste endringen siden sist. Vi kan se på nøyaktig hva som er endret med svn diff-kommandoen:

# svn diff -r PREV:COMMITTED
Index: trunk/Makefile
===================================================================
--- trunk/Makefile      (revisjon 90)
+++ trunk/Makefile      (revisjon 91)
@@ -0,0 +1 @@
+jaljalll

Vi får da en utputt som er ganske likt det den normale diff-kommandoen lager. Med -r angir vi at vi skal se på diff mellom revisjoner. Legg også merke til nøkkelordene «PREV» og «COMMITTED». Førstnevnte betyr «forrige» innsending, og sistnevnte betyr «ferskeste» innsending. Vi ser altså på den siste endringen som er gjort i lageret. Vi kommer tilbake til andre nøkkelord etter hvert.

Flytting og endringer i filstrukturen

I mange tilfeller er det ønskelig å flytte eller endre navn på filer eller kataloger. Da er det viktig at vi forteller lageret om disse endringene før vi prøver å sende inn data. La oss anta at vi etter siste update ønsker å flytte «Makefile» til «makefile.linux». Vi kan da gå inn i «trunk»-katalogen (eller gjøre det med full eller relativ sti fra andre steder) og bruke kommandoen svn rename:

# svn rename Makefile makefile.linux
A         makefile.linux
D         Makefile

Subversion svarer da med å kvittere for at «makefile.linux» er opprettet og «Makefile» er slettet. En ting er det veldig viktig å være klar over: Endringen ble ikke sendt inn til lageret i dette tilfellet, det er kun arbeidskopien som ble oppdatert. Vi foregriper begivenhetene litt, og sender inn denne endringen til lageret med svn commit:

# svn commit -m 'rename Makefile -> makefile.linux'
Autentiseringsområde: <svn://localhost:3690> svn.domene.com
Passord for 'brukernavn':
Sletter          trunk/Makefile
Legger til       trunk/makefile.linux  
La inn revisjon 92.

Kvitteringen er selvbeskrivende – «trunk/Makefile» er slettet, og «trunk/makefile.linux» er lagt til. En viktig ting er at Subversion her spør etter passord. Dette passordet vil bli bufret slik at man ikke trenger oppgi det for hver innsending. Vi har også oppgitt en kommentar til innsendingen med opsjonen «-m». Unnlater man dette, vil i stedet en tekstredigerer åpnes for å angi en kommentar. Hensikten er at det skal oppfordres til å legge inn gode kommentarer for hver revisjon (innsending).

I andre tilfeller ønsker man å legge til en fil som er ny. La oss si at vi har laget en ny headerfil, «nyheader.h». Vi kan legge til denne med kommandoen svn add:

# svn add nyheader.h
A         nyheader.h

Nok en gang: Legg merke til at dette kun er en endring i arbeidskopien. Dataene må sendes inn før de er tilgjengelige i lageret. Dette overlates som en øvelse for leseren!

Sjekke status

Når vi er ferdige med å gjøre endringer, kan vi sjekke status på den lokale arbeidskopien med kommandoen svn status:

# svn status
A      nyheader.h
M      makefile.linux

Som vi kjenner igjen fra forrige avsnitt, har det blitt lagt til en fil, «nyheader.h» I tillegg har det blitt gjort en modifikasjon på fila «makefile.linux». Vanligvis tar man seg nå tid til å verifisere at dette er korrekte endringer for det man har gjort.

Med svn diff kan vi også se på forskjellene på arbeidskopien og lageret:

# svn diff -r COMMITTED:BASE
Index: makefile.linux
===================================================================
--- makefile.linux      (revisjon 0)
+++ makefile.linux      (revisjon 92)
@@ -0,0 +1 @@
+all: test
Index: Makefile
===================================================================
--- Makefile    (revisjon 91)
+++ Makefile    (arbeidskopi)
@@ -1 +0,0 @@
-jaljalll

«BASE» refererer her til den nåværende tilstanden til arbeidskopien, mens «COMMITTED» som nevnt refererer til siste versjon i lageret.

Det sier seg selv at når man jobber på større prosjekter, kan det bli til dels store endringer som skal gjennomgås. Et tips kan være å sende inn data ofte og gjøre mindre endringer om gangen. Dette kan imidlertid fort bli litt irriterende for andre, som da må oppdatere oftere. I et team må man finne en balansegang på dette.

Lage patchefil

Som nevnt lager svn diff data som er i diff-format. Dersom man vil lage en patch som andre utviklere kan bruke i sin testing uten å sende inn til lageret, kan man omdirigere utputt til en fil:

# svn diff -r COMMITTED:BASE > patch.diff

Denne fila kan man så sende til en annen utvikler for testing.

Konflikter

Av og til hender det at noen andre har oppdatert en fil i lageret mens man selv har jobbet på samme fil. Dersom vi da prøver å kjøre en innsending, vil det oppstå en konflikt. La oss se på hva som skjer dersom noen har endret «makefile.linux» i lageret siden sist vi oppdaterte:

# svn commit -m 'Her får vi en konflikt siden andre har oppdatert en av de samme filene som oss i lageret'
Sender           trunk/makefile.linux
Sender fildata .svn: Innsending feilet (detaljer følger):
svn: Out of date: '/testprosjekt/trunk/makefile.linux' in transaction '32'

Her får vi en feilmelding som i utgangspunktet ser ganske stygg ut. Men fortvil ikke, «Out of date» betyr ganske enkelt at arbeidskopien er for gammel; det finnes en nyere versjon i lageret. La oss først se på hva som er forskjellen med svn diff:

# svn diff -r BASE:HEAD
Index: makefile.linux
===================================================================
--- makefile.linux      (revisjon 94)
+++ makefile.linux      (arbeidskopi)
@@ -1 +1,4 @@
 all: test
+
+test: test.c
+       gcc -O2 -o test.o test.c

svn status kan også gi en pekepinn på at konflikten vil dukke opp (-u betyr at man skal sjekke hva en update ville ført til av endringer).

# svn status -u
M      *       93   makefile.linux
Status mot revisjon:     94

Noen har tydeligvis lagt til tre linjer (et nytt mål) i «makefile.linux». Hva kan vi gjøre for å rette på dette? Vi kan prøve å kjøre en update, og se om det gjøres noen endringer i vår lokale kopi ("up" er en kortform for "update") :

# svn up
C  makefile.linux
Oppdatert til revisjon 94.

Her fikk vi plutselig angitt en ny status på en fil – «C». Dette betyr «Conflict» – det er oppstått en konflikt mellom endringene i lageret og de endringene vi selv har gjort. I mange tilfeller vil Subversion klare å slå sammen endringene uten konflikt, og flagge dette med enten en «U», som vi kjenner, eller en «G», som betyr at endringene har blitt flettet inn i vår lokale arbeidskopi uten problemer. Her har det imidlertid oppstått en konflikt. Subversion gjør da to ting: Den markerer konflikten tekstlig i den aktuelle fila i arbeidskopien, og «markerer» fila som konfliktberørt.

La oss ta en titt på fila, og se hva som er markert der:


Fil: makefile.linux med konflikt
<<<<<<< .mine
all: testing, but different from repository
=======
all: test

test: test.c
        gcc -O2 -o test.o test.c
>>>>>>> .r94

Her har Subversion lagt inn markører for endringene som er gjort. Først ser vi at det står «<<<<<<< .mine». Dette er en markør for at «her starter mine data som er i konflikt». Deretter kommer de linjene som er i konflikt i arbeidskopien. Så kommer det et skillemerke, «=======», fulgt av endringene som er i konflikt i lageret. Disse endringene avsluttes med «>>>>>>> .r94». Revisjonsnummeret til dataene fra lageret er angitt like bak markøren.

I tillegg til denne endringen i fila selv legger Subversion nå inn et sett andre filer i den aktuelle katalogen:

# ls -1 makefile.linux*
makefile.linux
makefile.linux.mine
makefile.linux.r93
makefile.linux.r94

«makefile.linux.mine» inneholder den lokale kopien av fila som den var før svn update. De to andre er kopier av fila i revisjon før man gjorde de lokale endringene, og kopier av fila som den var i lageret ved svn update. Ved å undersøke disse kan man se hva som må gjøres for å løse konflikten. Man må nå gjøre en av tre ting:

  • Redigere fila som er i konflikt for hånd ved hjelp av markørene
  • Benytte en av de andre revisjonene og kopiere den inn over den originale fila
  • Kjøre svn revert for å forkaste egne endringer

I forhold til resten av teamet som du jobber med er nesten alltid det første alternativet det beste. De andre blir i alle fall mektig overrasket om det viser seg at du forkaster endringene de har gjort. Vi skal forsøke oss på det første punktet, og redigerer fila «makefile.linux» så den ser slik ut, ved å se på markørene og endringene de andre har gjort:


Fil: makefile.linux etter redigering
all: test

testing: test

test: test.c
        gcc -O2 -o test.o test.c

Nå er vi klare til å fortelle Subversion at problemet er løst, med svn resolved <filnavn>:

# svn resolved makefile.linux
Konflikten med 'makefile.linux' er løst

Innsending av endringer

Når dette er gjort, kan vi igjen prøve å sende inn (merk at de midlertidige filene er fjernet av Subversion):

# svn commit -m 'konflikt løst'
Sender           trunk/makefile.linux
Sender fildata .
La inn revisjon 95.

Merk igjen at man alltid legger ved en beskrivende kommentar om hva som er gjort med svn commit. Det er god praksis å være konsekvent og nøye på dette, så hele teamet vet hva som gjøres med en ressurs.

Mer om grener

Av og til oppstår det i livsløpet til et prosjekt et behov for å opprette en egen gren av et prosjekt. Man kan tenke på en gren som konsekvensen av at man på et gitt tidspunkt lar prosjektet dele seg i to like deler (kopier), som deretter får sine egne utviklingsløp. Subversion har ikke noe internt begrep av hva en gren er for noe, men den har alle verktøyene som trengs for å jobbe med det konseptuelle begrepet.

Leseren husker nok fra tidligere at vi opprettet tre kataloger i prosjektet vårt: «tags», «branches» og «trunk». Sistnevnte er den katalogen der hoveddelen av utviklingen finner sted. De aller fleste som jobber med et prosjekt, vil jobbe mot denne delen av lageret. Som sagt oppstår det av og til behov for å ta en kopi av treet i «trunk», legge denne kopien på et eget sted og fortsette arbeidet på den separat. Til dette behovet har man «branches»-katalogen.

Opprette en gren

For å opprette en gren lager man rett og slett en kopi av trunk i branches-katalogen til prosjektet. Et viktig poeng i denne sammenhengen er at man ikke trenger gjøre dette med arbeidskopien, men kan gjøre det direkte mot lageret med svn copy:

# svn copy svn://localhost/testprosjekt/trunk svn://localhost/testprosjekt/branches/gren-av-testprosjekt
La inn revisjon 111.

Dersom vi nå kjører en oppdatering av arbeidskopien, vil vi se at den nye grenen blir lagt inn i «branches»:

# svn update
A  branches/gren-av-testprosjekt
A  branches/gren-av-testprosjekt/test.c
A  branches/gren-av-testprosjekt/nyheader.h
A  branches/gren-av-testprosjekt/makefile.linux
A  branches/gren-av-testprosjekt/header.h
Oppdatert til revisjon 111.

Vi har nå opprettet en gren av prosjektet. Som en interessant liten detalj kan det nevnes at dette egentlig ikke er en reell kopi i databasen. Subversion vil beholde alle som referanser i «trunk» – helt til noen gjør endringer som gjør de to grenene forskjellige. Først da opprettes det en egen versjon av den enkelte fila eller katalogen.

Kopiering av endringer mellom grener

Etter en viss tid (som kan være veldig kort) vil en gren og hovedkatalogen «trunk» utvikle seg forskjellig. Men ofte kan det være aktuelt å hente endringer fra en gren inn igjen i hovedlageret i «trunk». Dette kan man selvsagt ordne! La oss anta at det i grenen vi nettopp lagde er gjort en endring i «test.c», som vi vil ha inn i «trunk». Vi kan sjekke forskjellene på de to grenene først med svn diff:

# svn diff -r 111:112 svn://localhost/testprosjekt/branches/gren-av-testprosjekt/test.c
Index: test.c
===================================================================
--- test.c      (revisjon 111)
+++ test.c      (revisjon 112)
@@ -0,0 +1 @@
+//Her legger vi inn en endring som gjør at grenen og trunk blir forskjellige

Det har altså blitt lagt til en linje i denne fila. Legg spesielt merke til at det her oppgis en diff mellom to numeriske revisjoner, 111 og 112. 111 referer til siste revisjon av «trunk», mens 112 inneholder endringen til «test.c», som ble gjort i grenen.

Etter å ha sett gjennom forskjellene, bestemmer vi oss for å ta denne endringen inn i «trunk». Dette kan vi gjøre med svn merge. Merge fungerer omtrent på samme måte som når man patcher en fil med patch – den tar en diff og påfører fila den. La oss se det i praksis:

# svn merge -r 111:112 svn://localhost/testprosjekt/branches/gren-av-testprosjekt/test.c
U  test.c

Syntaksen er omtrent identisk, men vi bytter diff med merge. Merk at merge ga beskjed om «U», det vil si at fila ble oppdatert. La oss sjekke status på endringer før vi sender inn til lageret:

# svn status
M      test.c

OK, det er ikke andre endringer som er gjort. La oss sende inn som før:

# svn commit -m 'merged changes from branch in test.c'
Sender           trunk/test.c
Sender fildata .
La inn revisjon 113.

Sånn! Det var jo enkelt. Men det er ikke alltid så lett som dette på større prosjekter. I mange tilfeller vil det oppstå konflikter mellom endringer; i så fall vil Subversion flagge dette på samme måte som vi så i avsnittet om oppdatering. Man må da evaluere endringene på samme måte og si fra til Subversion (med svn resolved) når dette er løst opp i.

Subversion kan selvsagt ikke sjekke om separate endringer i samme kode vil ødelegge logikken i et program – dette må utvikleren selv sørge for. Subversion er et fantastisk redskap, men det er ikke magisk!

Versjonsmerking

Som et ekstra hjelpeverktøy i utviklingen av et prosjekt, opererer man ofte med versjonsmerkelapper. Årsaken er at man til tider ønsker å la en gitt revisjon av et uviklingstre fungere som en bestemt versjon. I praksis er det ingen forskjell på dette og det å lage en gren – man opererer med dette konseptet utelukkende av menneskelige årsaker. Det var av denne grunnen at vi laget «tags»-katalogen litt tidligere. For å lage en versjonsmerkelapp kan vi lage en kopi i denne katalogen akkurat slik vi gjorde med en gren:

# svn copy svn://localhost/testprosjekt/trunk svn://localhost/testprosjekt/tags/release-1.0 -m 'opprettet 1.0 release ut fra r113'
La inn revisjon 114.

Nok en gang gjør vi dette direkte på lageret. Vi kjører en update på den lokale kopien for å gjenspeile endringen:

# svn up
A  tags/release-1.0
A  tags/release-1.0/test.c
A  tags/release-1.0/nyheader.h
A  tags/release-1.0/makefile.linux
A  tags/release-1.0/header.h
Oppdatert til revisjon 114.

Da har vi lest endringen inn igjen i arbeidskopien. Nok en gang kan man merke seg at dette ikke innebærer en fysisk kopi i databasen – men «linker» til de relevante dokumentenes tilstand slik de var i «trunk» på tidspunktet.

Mer om navngiving på kataloger

Når det her i artikkelen opprettes kataloger som heter «branches», «tags» og «trunk», er det fordi dette er vanlig konvensjon. Det er ingenting i veien for å velge andre navn eller andre metoder for å versjonsmerke og forgrene prosjektet, så lenge hele teamet kjenner til rutinene. Husk at Subversion ikke har noe begrep om versjonering og merking – den sørger rett og slett bare for å holde de ulike revisjonen under kontroll og lage de kopiene man ber om på bestemte tider.

Subversion og Apache 2

Subversion kan også brukes sammen med den populære Apache-vevtjeneren for å aksessere lagre over http-protokollen. Merk at dette kun gjelder Apache 2 – Apache 1.3 er ikke støttet. Dette krever at man installerer to ekstramoduler til Apache, mod_dav og mod_dav_svn. De fleste distribusjoner har pakker for dette i sine respektive pakkestyrere. Vi anbefaler at du bruker disse systemene for å installere disse pakkene. I tillegg må selvsagt Subversion være installert.

Når man har sørget for at mod_dav_svn er installert, må man laste modulene i Apache sin konfigurasjon, samt definere hvor mod_dav skal se etter ressursen. De aktuelle linjene i Apache sin konfigurasjonsfil er:


Fil: /etc/httpd/conf/httpd2.conf (utdrag)
LoadModule dav_module           modules/mod_dav.so
LoadModule dav_svn_module       modules/mod_dav_svn.so

# ... øvrige direktiver her, legg til Location nederst:

<Location /repos>
        DAV svn
        SVNPath /var/svn
</Location>

Når dette er lagt til, må man huske å legge til apache-brukeren i svn-gruppa, og restarte Apache:

# usermod -G svn apache
# service httpd restart

Det er ikke alle distribusjoner som har service-kommandoen – bruk den som tilhører ditt system. Sjekk også hvilken bruker det er Apache kjører som på ditt system.

Sikkerhet med Subversion, Apache 2 og mod_dav_svn

Triksene som er gjort i forrige avsnitt etterlater seg et gapende sikkerhetshull. Siden det er apache-brukeren som aksesserer lageret, kan *alle* som bruker denne tilgangsmetoden ha skrivetilgang til lageret!

Vi kan løse dette ved å legge til enkel http-autentisering. Da må vi legge til noen nye konfigurasjonsvalg i konfigurasjonsfila til Apache, slik at Subversion-seksjonen blir seende omtrent slik ut:


Fil: /etc/httpd/conf/httpd2.conf (utdrag)
<Location /repos>
        DAV svn
        SVNPath /var/svn
        AuthType Basic
        AuthName "Subversion repository authentication"
        AuthUserFile /etc/subv_dav.auth
        <LimitExcept GET PROPFIND OPTIONS REPORT>
                Require valid-user
        </LimitExcept>
</Location>

Dette fører til at alle som vil utføre andre operasjoner enn å lese fra /repos, blir nødt til å angi et brukernavn og passord. For å sette passordet (som vi har angitt skal ligge i fila /etc/subv_dav.auth), kjører vi htpasswd:

# htpasswd -cbm /etc/subv_dav.auth <brukernavn> <passord>

Husk å starte Apache på nytt etter at konfigurasjonen er endret. Vi kan nå teste en checkout for å se hvordan dette virker:

# svn checkout http://localhost/repos/testprosjekt

Siden vi ikke skriver noe informasjon i denne omgangen, får vi direkte tilgang til å lese lageret.

La oss prøve å gjøre en endring og deretter sende inn:

# svn commit -m "testcommit over http"
Autentiseringsområde: &amp;lt;http://svn.domene.com:80> Subversion repository authentication
Passord for 'kenneth':
Sender           trunk/Makefile
Sender fildata .
La inn revisjon 105.

Legg merke til at brukernavnet antas ut fra systembrukeren som kjører svn-kommandoen. Husk at dette passordet må være satt med htpasswd-kommandoen. Også i dette tilfellet vil svn bufre passordet og bruke det ved neste gangs innsending.

Løsningen er imidlertid fortsatt ikke helt optimal, siden passordet går over HTTP i klartekst.

SSL-kryptering ved bruk av Apache og mod_dav_svn

For å bøte på dette kan Apache-tjeneren settes opp til å automatisk omdirigere forespørsler som kommer inn til katalogen som lagrene ligger i til en annen URL, ved bruk av HTTPS. Dersom du bruker Apaches virtuelle tjenere, må dette legges i den relevante virtuelle tjeneren, som vist under. Det er linjene som starter med «Rewrite» som er lagt til:


Fil: /etc/httpd/conf/vhosts/Vhost.conf (utdrag)
<VirtualHost 129.241.152.115:80>
        ServerName darkeuphoria.sytes.net
        DocumentRoot /var/www/html
        RewriteEngine On
        RewriteCond %{REQUEST_URI} ^/repos.*$
        RewriteCond %{SERVER_PORT} ^80$
        RewriteRule ^.*$ https://%{SERVER_NAME}%{REQUEST_URI} [R]
</VirtualHost>

Etter en start av Apache-tjeneren vil alle henvendelser til /repos på port 80 på denne tjeneren automatisk bli sendt videre til samme katalog, men med HTTPS-protokollen.

Sikkerhet

Sikkerhet er alltid en stor bekymring for systemer der man kommuniserer over nettverk. Subversion har støtte for ulike måter å kommunisere over et nettverk. I artikkelen over har vi antatt at man kjører en tjener som kommuniserer med svn-protokollentcp port 3690. Selv om brukernavn og passord i et slikt system sendes kryptert over nett, kan man tenke seg at noen snapper opp informasjonen og klarer å benytte denne til å misbruke tjenesten. En mulig måte å hindre dette på kan være å kjøre Subversion over en ssh-tunnel.

Subversion over ssh-tunnel

I stedet for å ha en dedisert tjener som kjører i bakgrunnen, kan man kjøre Subversion gjennom en ssh-tunnel. Bruken er ikke veldig forskjellig fra det som er gjennomgått over. På klientsiden er den eneste forskjellen at man identifiserer lagrene ved hjelp av en annen type URL, som starter med «svn+ssh://». På tjenersiden er forskjellene litt større. For det første trenger man ingen egen tjener som kjører i bakgrunnen. I stedet blir svnserve kalt for hver gang en bruker skal benytte lageret. Dette fører til en ekstra liten utfordring: svnserve vil da kjøre som brukeren til ssh-tunnellen. Vi satte tidligere opp systemet slik at systembrukere som skal bruke lageret ligger i gruppa til svn. Det er viktig å påse at nye brukere som skal bruke Subversion legges til i denne gruppa. Brukere som ikke ligger i denne gruppa vil kun ha lesetilgang til lageret.

Men i tillegg til dette må svnserve kjøre med en spesiell umask 002. Vi ordner dette ved å lage et script som kapsler inn svnserve-kommandoen. Først flytter vi svnserve til et annet navn (gjøres som root):

# mv /usr/bin/svnserve /usr/bin/svnserve.real

Så lager vi et script /usr/bin/svnserve som ser slik ut:


Fil: /usr/bin/svnserve
#!/bin/sh
umask 002
svnserve.real -r /var/svn/repository "$@"

Vi angir her stien til lageret for å unngå at de som bruker tjenesten må angi full filsystemsti til lageret når de kobler til.

Pass på at du ikke lager en sirkulær referanse ved å la dette scriptet kalle seg selv, men den korrekte svnserve.real!

Husk at du må sette scriptet som nettopp er laget til kjørbart:

# chmod 755 /usr/bin/svnserve

Med dette oppsettet kan du koble til en tjener med Subversion-lager ved hjelp av følgende syntaks:

# svn checkout svn+ssh://svn.domene.com/testprosjekt

Man kan så fortsette med å bruke arbeidskopien som før; svn husker tilkoblingstypen som ble brukt ved checkout, og sørger for å bruke den videre.

Subversion autentiserer nå ved hjelp av systemkontoer. Husk at brukere som skal ha skrivetilgang til lageret, må legges til i svn-gruppa. Passordene som er definert i configen til lageret vil ikke bli brukt, men man må fortsatt ha lesetilgang til fila.

Oppsummering

Subversion forenkler hverdagen betydelig når man jobber flere sammen på et prosjekt. Det er også et nyttig verktøy om du jobber alene – selv om konflikter da er mye sjeldnere, har man gode verktøy for å spore de endringene man selv har gjort, og eventuelt rulle tilbake til tidligere versjoner dersom man havner på villspor.

Eksterne ressurser


Personal tools