Subversion i aksjon

Det er på tide å gå fra det abstrakte til det konkrete. I denne seksjonen vil vi vise reelle eksempler på bruk av Subversion.

Depot-URLer i Subversion

Gjennom denne boka bruker Subversion URLer for å identifisere versjonerte ressurser i Subversion-depoter. For det meste bruker disse URLene standard syntaks, som tillater servernavn og portnummer å bli spesifisert som en del av URLen:

$ svn checkout http://svn.example.com:9834/repos
…

Men det er noen nyanser i Subversions behandling av URLer som legges merke til. For eksempel, URLer som bruker file:-aksesseringsmetoden (brukt for lokale depoter) må, i henhold til konvensjonene, enten ha servernavnet localhost eller ikke noe servernavn:

$ svn checkout file:///sti/til/depot
…
$ svn checkout file://localhost/sti/til/depot
…

I tillegg må brukere av file:-skjemaet på MS Windows-plattformer bruke en uoffisiell “standard” syntaks for å aksessere depoter smo er på den samme maskinen, men på en annen disk enn klientens nåværende arbeidsdisk. En av de to følgende URL-syntaksene vil virke der X er disken hvor depotet ligger:

C:\> svn checkout file:///X:/sti/til/depot
…
C:\> svn checkout "file:///X|/sti/til/depot"
…

I den andre syntaksen må du sette URLen i hermetegn ("") så det vertikale stolpetegnet ikke blir tolket som et rør. Legg også merke til at en URL bruker vanlige skråstreker (/) selv om den lokale (ikke-URL) formen i en sti i MS Windows bruker omvendte skråstreker (\).

Til slutt må det legges merke til at Subversionklienten automatisk vil kode URLer etter behov, akkurat som en nettleser gjør det. For eksempel, hvis en URL inneholder et mellomrom eller store bokstaver:

$ svn checkout "http://server/sti med mellomrom/prosjekt/españa"

… så vil Subversion beskytte de usikre tegnene og oppføre seg som om du hadde skrevet:

$ svn checkout http://server/sti%20med%20mellomrom/prosjekt/espa%C3%B1a

Hvis URLen inneholder mellomrom, pass på å plassere den innenfor hermetegn, så skallet behandler hele URLen som et enkelt argument til svn-programmet.

Arbeidskopier

Du har allerede lest om arbeidskopier; nå skal vi demonstrere hvordan Subversionklienten lager og bruker dem.

En arbeidskopi i Subversion er et vanlig katalogtre på ditt lokale system, og inneholder en samling filer. Du kan redigere disse filene sånn som du vil, og hvis det er kildekode, kan du kompilere programmet på den vanlige måten. Arbeidskopien din er ditt eget private arbeidsområde: Subversion vil aldri legge inn andre folks forandringer, heller ikke gjøre dine egne forandringer tilgjengelig for andre før du eksplisitt ber programmet om å gjøre det. Du kan til og med ha flere arbeidskopier av det samme prosjektet.

Etter at du har gjort noen forandringer i filene i arbeidskopien og sjekket at de virker skikkelig, gir Subversion deg kommandoer så du kan “publisere” forandringene dine til de andre som arbeider med deg på prosjektet ditt (ved å skrive til depotet). Hvis andre personer publiserer deres egne forandringer, gir Subversion deg kommandoer for å flette disse forandringene inn i din arbeidskopi (ved å lese fra depotet).

En arbeidskopi inneholder også noen ekstra filer, opprettet og vedlikeholdt av Subversion, for å hjelpe seg med å utføre disse kommandoene. Hver katalog i arbeidskopien inneholder en underkatalog kalt .svn, også kjent som arbeidskopiens administrative katalog. Filene i hver administrative katalog hjelper Subversion til å se hvilke filer som inneholder upubliserte forandringer, og hvilke filer som er utdaterte i forhold til andres arbeid.

Et typisk Subversiondepot inneholder ofte filene (eller kildekoden) for flere prosjekter; vanligvis har hvert prosjekt sin egen underkatalog i depotets filsystemtre. Med dette arrangementet vil en brukers arbeidskopi samsvare med et spesielt deltre av depotet.

For eksempel, tenk deg at du har et depot som består av to programprosjekter, paint og calc. Hvert prosjekt bor i hver sin toppkatalog, som vist i Figur 1.6, “Depotets filsystem”.

Figur 1.6. Depotets filsystem

Depotets filsystem

For å få deg en arbeidskopi, må du først hente ut (check out) et del av et katalogtre fra depotet. (Det engelske uttrykket “check out” kan høres ut som det har noe å gjøre med låsing eller reservering av ressurser, men det har ikke det; det lager bare en privat kopi av prosjektet for deg.) For eksempel, hvis du henter ut /calc, vil du få en arbeidskopi som dette:

$ svn checkout http://svn.example.com/repos/calc
A    calc/Makefile
A    calc/integer.c
A    calc/button.c
Checked out revision 56.

$ ls -A calc
Makefile  integer.c  button.c  .svn/

Listen med bokstaven A indikerer at Subversion legger til et antall elementer i arbeidskopien din. Du har nå en personlig kopi av depotets /calc-katalog, med en ekstra komponent – .svn – som tidligere nevnt inneholder den ekstra informasjonen som Subversion trenger.

Tenk at du gjør forandringer til button.c. Siden .svn-katalogen husker filens modifiseringsdato og originale innhold, kan Subversion se at du har forandret fila. Men Subversion offentliggjør ikke dine forandringer før du eksplisitt ber programmet om å gjøre det. Prosessen når du publiserer dine forandringer blir vanligvis omtalt som å legge inn (eller sende/sjekke inn) forandringer til depotet.

For å publisere dine forandringer til andre, kan du bruke Subversions commit-kommando.

$ svn commit button.c -m "Ordnet en skrivefeil i button.c ."
Sender        button.c
Sender fildata .
La inn revisjon 57.

Nå er dine forandringer til button.c lagt inn i depotet, med en melding som beskriver forandringen din (at du ordnet en skrivefeil). Hvis en annen bruker henter ut en arbeidskopi av /calc, vil de se dine forandringer i den seneste versjonen av fila.

Tenk deg at du har en samarbeidspartner, Sally, som hentet ut en arbeidskopi av /calc samtidig med deg. Når du legger inn din forandring til button.c, er Sallys arbeidskopi uforandret; Subversion modifiserer bare arbeidskopier etter brukerens ønske.

For å få sitt prosjekt oppdatert, kan Sally be Subversion om å oppdatere hennes arbeidskopi, ved å bruke Subversionkommandoen update. Dette vil legge inn dine forandringer inn i hennes arbeidskopi, så vel som alle andre forandringer som er blitt lagt inn i depotet siden hun sist hentet det ut.

$ pwd
/home/sally/calc

$ ls -A 
.svn/ Makefile integer.c button.c

$ svn update
U    button.c
Updated to revision 57.

Utdataene fra kommandoen svn update indikerer at Subversion oppdaterte innholdet av button.c. Legg merke til at Sally ikke trengte å spesifisere hvilke filer som skulle oppdateres, Subversion bruker informasjonen i .svn-katalogen sammen med annen informasjon i depotet for å bestemme hvilke filer som trenger en oppdatering.

Revisjoner

En svn commit-operasjon publiserer forandringer til et vilkårlig antall filer og kataloger som en enkeltstående atomisk transaksjon. I arbeidskopien din kan du forandre filenes innhold, opprette, slette, skifte navn og kopiere filer og kataloger, og så legge inn et komplett sett med forandringer som en atomisk transaksjon.

Med “atomisk transaksjon” mener vi rett og slett dette: Enten skjer alle forandringene i depotet, eller ingen av dem skjer. Subversion prøver å beholde denne atomiteten stilt opp mot programkræsj, systemkræsj, nettverksproblemer og andre brukeres aktiviteter.

Hver gang depotet aksepterer en innlegging, opprettes det en ny tilstand i filsystemtreet, kalt en revisjon. Hver revisjon blir tildelt et unikt naturlig tall, ett større enn nummeret på den forrige revisjonen. Den første revisjonen i et nyopprettet depot har nummeret null, og inneholder ingenting annet enn en tom rotkatalog.

Figur 1.7, “Depotet” illustrerer en fin måte å visualisere depotet på. Tenk deg en rekke av revisjonsnumre som starter på 0 og strekker seg fra venstre mot høyre. Hvert revisjonsnummer har et filsystemtre hengende under seg, og hvert tre er et “øyeblikksbilde” av hvordan depotet så ut etter en innlegging.

Figur 1.7. Depotet

Depotet

Det er viktig å notere seg at arbeidskopier ikke bestandig samsvarer med en enkelt revisjon i depotet; de kan inneholde filer fra flere forskjellige revisjoner. For eksempel, tenk at du henter ut en arbeidskopi fra et depot der den siste revisjonen er 4:

calc/Makefile:4
     integer.c:4
     button.c:4

For øyeblikket samsvarer arbeidskopien nøyaktig med revisjon 4 i depotet. Men tenk deg så at du gjør en forandring i button.c, og legger inn denne forandringen. Forutsatt at ingen andre innlegginger har forekommet, vil din innlegging opprette revisjon 5 i depotet, og arbeidskopien din vil se ut som dette:

calc/Makefile:4
     integer.c:4
     button.c:5

Så sier vi at Sally på dette tidspunktet legger inn en forandring til integer.c, som lager revisjon 6. Hvis du bruker svn update for å oppdatere arbeidskopien, vil den se ut som dette:

calc/Makefile:6
     integer.c:6
     button.c:6

Sallys forandring i integer.c vil komme til syne i arbeidskopien din, og din forandring vil fortsatt være til stede i button.c. I dette eksempelet er teksten i Makefile identisk i revisjon 4, 5 og 6, men Subversion vil merke kopien av Makefile med revisjon 6 for å indikere at den fortsatt gjelder. Så, etter at du gjør en ren oppdatering fra toppen av arbeidskopien din, vil den vanligvis samsvare med en eksakt revisjon i depotet.

Hvordan arbeidskopier holder følge med depotet

For hver fil i en arbeidskatalog, lagrer Subversion to essensielle deler informasjon i det administrative .svn-området:

  • Hvilken revisjon arbeidsfila di er basert på (dette kalles filas arbeidsrevisjon), og

  • et tidsmerke fra da den lokale kopien sist ble oppdatert av depotet.

Ved hjelp av denne informasjonen kan Subversion ved å kommunisere med depotet se hvilke fire tilstander en arbeidsfil er i:

Uforandret og oppdatert

Fila er uforandret i arbeidskopien, og ingen forandringer til denne fila er blitt lagt inn i depotet siden arbeidskopien ble lagt inn. En svn commit på fila vil ikke gjøre noe som helst, og en svn update på fila vil heller ikke gjøre noe.

Forandret lokalt og samtidig gjeldende

Fila er blitt forandret i arbeidskopien, og ingen forandringer i denne fila er blitt lagt inn i depotet siden sist du oppdaterte. Det er lokale forandringer som ikke er blitt lagt inn i depotet, så en svn commit av fila vil lykkes i å publisere forandringene dine, og en svn update på fila vil ikke gjøre noen ting.

Uforandret og utdatert

Fila er ikke blitt forandret i arbeidskopien, men har forandret seg i depotet. Fila må etterhvert bli oppdatert, for å få den til å samsvare med den nyeste offentlige revisjonen. En svn commit på fila vil ikke gjøre noe, og en svn update på fila vil legge de seneste forandringene inn i arbeidskopien din.

Forandret lokalt og samtidig utdatert

Fila er blitt forandret både i arbeidskopien og depotet. En svn commit av fila vil feile med en “out-of-date”-feilmelding. Fila må oppdateres først; en svn update-kommando vil prøve å flette inn de offentlige forandringene med de lokale forandringene. Hvis Subversion ikke kan fullføre flettingen automatisk på en skikkelig måte, blir det opp til brukeren å løse konflikten.

Dette kan høres ut som mye å holde greie på, men svn status-kommandoen vil vise deg tilstanden til ethvert element i arbeidskopien din. For mer informasjon om denne kommandoen, se svn status.

Arbeidskopier med blandede revisjonsnumre

Som et generelt prinsipp prøver Subversion å være så fleksibel som mulig. En spesiell type fleksibilitet er muligheten til å ha en arbeidskopi som inneholder filer og kataloger med en blanding av forskjellige arbeidsrevisjonsnumre. Uheldigvis har denne fleksibiliteten en tendens til å forvirre en del nye brukere. Hvis det tidligere eksempelet som viser blandede revisjoner gjør deg litt perpleks, her kommer en forklaring på hvorfor denne funksjonaliteten finnes og hvordan du gjør bruk av den.

Oppdateringer og innlegginger er separate

En av de fundamentale reglene til Subversion er at en “skyveoperasjon” ikke forårsaker en “trekkoperasjon”, heller ikke den andre veien. Det at du er klar til å legge inn nye forandringer i depotet betyr ikke at du er klar til å motta forandringer fra andre folk. Og hvis du har nye forandringer som fortsatt er under utvikling, bør svn update flette forandringer i depotet fint og forsiktig inn i dine egne istedenfor å tvinge deg til å publisere dem.

Den største sideeffekten av denne regelen er at det betyr at arbeidskopien må holde ekstra regnskap for å følge med på miksede revisjoner, og også være tolerant i forhold til blandingen. Det blir ytterligere komplisert fordi også selve katalogene er versjonerte.

For eksempel, tenk deg at du har en arbeidskopi der alle elementene er på revisjon 10. Du redigerer fila foo.html og utfører deretter en svn commit som lager revisjon 15 i depotet. Etter at innleggingen er fullført, vil mange brukere forvente at hele arbeidskopien er på revisjon 15, men det er ikke tilfellet! Alle mulige forandringer kan ha skjedd mellom revisjonene 10 og 15. Klienten vet ingenting om disse forandringene i depotet, siden du ikke har kjørt svn update enda, og svn commit ikke henter nye forandringer. Hvis svn commit på den annen side hentet de nyeste forandringene automatisk, ville det være mulig å sette hele arbeidskopien til revisjon 15 – men da ville vi brutt den fundamentale regelen om at “skyveoperasjoner” og “trekkoperasjoner” skal være separate handlinger. Derfor, den eneste trygge tingen som Subversionklienten kan gjøre er å kun merke den ene fila – foo.html – som å være på revisjon 15. Resten av arbeidskopien forblir på revisjon 10. Bare ved å kjøre svn update kan de nyeste forandringene bli hentet, og hele arbeidskopien kan merkes som revisjon 15.

Blandede revisjoner er normalt

Faktum er at hver gang du kjører svn commit ender arbeidskopien din opp med en form for blanding av revisjoner. Det elementet som du akkurat la inn er merket som om det har et større revisjonsnummer enn alt det andre. Etter flere innlegginger (uten noen oppdateringer i mellomtiden) vil arbeidskopien din inneholde en stor blanding av revisjoner. Selv om du er den eneste personen som bruker depotet vil du fortsatt se dette fenomenet. For å utforske blandingen din av blandede revisjoner kan du bruke kommandoen svn status --verbose (se svn status for mer informasjon).

Ofte er nye brukere ikke klar over at arbeidskopiene deres inneholder miksede revisjoner. Dette kan være forvirrende, fordi mange klientkommandoer er sensitiv ovenfor arbeidsrevisjonen til elementet som de utforsker. For eksempel, svn log-kommandoen blir brukt til å vise historien til ei fil eller en katalog (se svn log). Når brukeren kjører denne kommandoen på et objekt i arbeidskopien, forventer de å se hele historien til objektet. Men hvis objektets arbeidsrevisjon er ganske gammel (ofte fordi svn update ikke har vært kjørt på aldri så lenge), blir historien til den eldre versjonen av objektet vist.

Blandede revisjoner er nyttige

Forutsatt at prosjektet ditt er komplekst nok, vil du oppdage at det noen ganger er fint å kunne “tilbakedatere” deler av arbeidskopien til en tidligere revisjon; du vil lære hvordan du gjør dette i Kapittel 2, Grunnleggende bruk. Kanskje vil du prøve en tidligere versjon av en delmodul som ligger i en underkatalog lagret i en underkatalog, eller kanskje du vil finne ut når en feil først oppsto i en spesiell fil. Dette er “tidsmaskin-aspektet” i et versjonskontrollsystem – funksjonaliteten som lar deg flytte alle deler av arbeidskopien din framover og bakover i historien.

Blandede revisjoner har begrensninger

Hvordan du enn gjør bruk av blandede revisjoner i arbeidskopien din, er det begrensninger i denne fleksibiliteten.

For det første kan du ikke legge inn sletting av ei fil eller en katalog som ikke er fullstendig oppdatert. Hvis en nyere versjon av elementet eksisterer i depotet, vil forsøket ditt på å slette bli avslått, for å forhindre deg å feilaktig ødelegge forandringer som du enda ikke har sett.

For det andre kan du ikke legge inn en metadata-forandring til en katalog hvis den ikke er fullstendig oppdatert. Du vil få lære om å legge til “egenskaper” til elementer i Kapittel 3, Avanserte emner. En katalogs arbeidsrevisjon definerer et spesifikt sett med poster og egenskaper, og en innlegging av forandringer i en egenskap for en utdatert katalog kan ødelegge egenskaper som du enda ikke har sett.