Oversatt til norsk av: Øyvind A. Holm
Copyright © 2002, 2003, 2004, 2005 Ben Collins-Sussman, Brian W. Fitzpatrick, C. Michael Pilato
Dette verket er lisensiert under Creative Commons Attribution License. For å se en kopi av denne lisensen, gå til http://creativecommons.org/licenses/by/2.0/ eller send et brev til Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
(TBA)
Innholdsfortegnelse
Figuroversikt
Tabelloversikt
Eksempeloversikt
En dårlig FAQ (Ofte stilte spørsmål) er en som ikke er satt sammen av spørsmål brukere faktisk stiller, men det som forfatterne ønsker at de ville spørre om. Du har kanskje sett noe lignende som dette før:
Spm.: Hvordan kan jeg bruke Glorbosoft XYZ til å øke lagproduktiviteten?
Sv.: Mange av våre kunder vil vite hvordan de kan maksimere produktiviteten gjennom våre patenterte kontor-gruppevareløsninger. Svaret er enkelt: Først, klikk på “Fil”-menyen, rull ned til “Øk produktiviteten”, deretter…
Problemet med slike FAQ-er er at de er, bokstavelig talt, ikke FAQ-er i det hele tatt. Ingen ringer teknisk support og spør: “Hvordan kan vi øke produktiviteten?” Istedenfor spør folk høyst spesifikke spørsmål som for eksempel: “Hvordan kan vi forandre kalendersystemet til å sende påminnelser to dager i forveien istedenfor en?” og så videre. Men det er er mye lettere å lage spørsmål istedenfor å oppdage de spørsmålene som egentlig blir stilt. Det å sette sammen en FAQ må være en sammenhengende og organisert prosess som varer hele levetiden til programmet, innkommende spørsmål må bli lagret, responsen fulgt, og alt dette må samles i en fyldig, søkbar helhet som avspeiler den kollektive opplevelsen for brukerne ute i det fri. Det krever en tålmodig og observant innstilling hos en som befinner seg i felten. Ingen store hypoteser, ingen visjonære uttalelser her – åpne øyne og nøyaktige notater er det som trengs mest.
Det jeg liker med denne boken er at den vokste fram nettopp gjennom en slik prosess, og dette vises på hver eneste side. Den er det direkte resultatet av forfatterens møter med brukerne. Det begynte med Ben Collins-Sussmans observasjon av at folk spurte de samme enkle spørsmålene om og om igjen på mailinglisten til Subversion: Hva er standard fremgangsmåte for å bruke Subversion? Virker forgreninger og merker på samme måte som i andre versjonskontrollsystemer? Hvordan kan jeg finne ut hvem som gjorde en spesiell forandring?
Frustrert over å se de samme spørsmålene dag etter dag, jobbet Ben intenst over en måned sommeren 2002 for å skrive The Subversion Handbook, en 60-siders håndbok som tok for seg all grunnleggende bruk av Subversion. Håndboken tok ikke mål av seg å være komplett, men den ble distribuert med Subversion og hjalp brukere over den innledende humpen i lærekurven. Da O’Reilly and Associates bestemte seg for å publisere en fullversjon av Subversion-boken, var minste motstands vei opplagt: Å utvide håndboken.
De tre medforfatterne av den nye boken ble dermed presentert for en uvanlig mulighet. Offisielt var deres oppgave å skrive en bok ovenfra og ned, å starte med innholdet og et innledende utkast. Men de hadde også tilgang til en jevn strøm – faktisk en ukontrollerbar geysir – av materiale som kom fra gressrotplan. Subversion var allerede i hendene på tusenvis av tidlige brukere, og disse brukerne ga tonnevis med tilbakemeldinger, ikke bare om Subversion, men om den eksisterende dokumentasjonen.
Gjennom hele tiden de skrev denne boken, trålte Ben, Mike og Brian konstant malilinglisten til Subversion og fulgte med i snakkerom mens de nøye skrev ned problemer brukere hadde i virkelige situasjoner. Å holde øye med slike tilbakemeldinger er en del av deres arbeidsoppgaver hos CollabNet uansett, og det ga dem en stor fordel når de satte i gang med å dokumentere Subversion. Boken de produserte er fast forankret i erfaringens grunnfjell, ikke i den skiftende sanden av ønsketenkning; den kombinerer de beste aspektene av en brukerhåndbok og en liste over ofte spurte spørsmål. Denne tosidigheten er kanskje ikke så merkbar ved første gjennomlesing. Tatt i rekkefølge, forside til bakside, er boken rett og slett en likefrem beskrivelse av et stykke programvare. Oversikten finnes der, den obligatoriske guidede gjennomgangen, kapittelet om administrativ konfigurasjon, noen avanserte tema, og selvfølgelig en referanse over kommandoer og problemer som kan dukke opp. Bare når du kommer tilbake til den senere, når du vil lete opp løsningen på et spesifikt problem, da viser det seg: Detaljene i fortellingen kan bare være resultatet av møter med det uventede, eksemplene finslipt av virkelige bruksmåter, og mest av alt forståelsen av brukerens behov og hvordan situasjonen tar seg ut fra brukerens ståsted.
Selvfølgelig, ingen kan love at denne boken vil kunne svare på hvert eneste spørsmål du har om Subversion. Noen ganger vil presisjonen i hva den forventer av spørsmål være uhyggelig telepatisk; andre ganger kan du finne et hull i fellesskapets kunnskap, og du står tomhendt tilbake. Når dette skjer, er den beste tingen du kan gjøre å sende en epost til <users@subversion.tigris.org> og legge fram problemet ditt. Forfatterne er fortsatt der, de følger fortsatt med, og disse består ikke bare av de tre på forsiden, men også mange andre som har bidratt med korreksjoner og originalt materiale. Fra fellesskapets synspunkt er det å løse problemet ditt bare en behagelig bieffekt av et mye større prosjekt – det å sakte tilpasse boken, og aller helst Subversion selv, til å samsvare mer med hvordan den faktisk brukes. De er ivrige etter å høre fra deg, ikke bare fordi de kan hjelpe deg, men fordi du kan hjelpe dem. Det er med Subversion som med alle aktive prosjekter innen fri programvare, du er ikke alene.
La denne boken bli din første ledsager.
– , Chicago, 14. mars 2004
Innholdsfortegnelse
“Hvis C gir deg nok tau til å henge deg selv, tenk på Subversion som en slags lagringsplass for tau.” –Brian W. Fitzpatrick
I opensource-verdenen har Concurrent Versions System (CVS) lenge vært førstevalget innen versjonskontroll. Og det er fortjent. CVS er selv fri programvare, og dens ikke-restriktive virkemåte og støtte for nettverksbaserte operasjoner – som tillater dusinvis av programmerere spredt over et geografisk område å dele arbeidet – passer veldig bra sammen med samarbeidsånden i opensource-verdenen. CVS med sin halvkaotiske utviklingsmodell har blitt hjørnesteiner i kulturen av fri programvare.
Men som mange verktøy, begynner CVS å vise aldringstegn. Subversion er et relativt nytt versjonskontrollsystem konstruert for å bli etterfølgeren til CVS. Designerne gikk inn for å vinne hjertene til CVS-brukerne på to måter: Ved å lage et opensource-system med en design (og “look and feel”) som ligner på CVS, og samtidig prøve å fikse mesteparten av de åpenbare feilene i CVS. Selv om resultatet nødvendigvis ikke er det neste store steget innen versjonskontrolldesign, er Subversion meget kraftig, brukbart og veldig fleksibelt.
Denne boken er skrevet for å dokumentere 1.0-serien av versjonskontrollsystemet Subversion. Vi har gjort ethvert forsøk på å være grundig i dekningen av systemet. Subversion har imidlertid et livlig og energisk utviklermiljø, så det er allerede et antall funksjoner og forbedringer planlagt i fremtidige versjoner av Subversion som kan forandre noen av kommandoene og de spesifikke notatene i denne boken.
Denne boken er skrevet for datamaskinkyndige personer som vil bruke Subversion til å behandle sine data. Selv om Subversion kjører under flere forskjellige operativsystemer, er det primære brukergrensesnittet kommandolinjebasert. Det er dette kommandolinjeverktøyet (svn) som blir diskutert og brukt i denne boken. For å være konsekvent går vi ut i fra at leseren bruker et Unix-lignende operativsystem og er relativt komfortabel med Unix og kommandolinjemiljø.
Når det er sagt, kjører svn-programmet også på andre plattformer enn Unix, for eksempel Microsoft Windows. Med noen få unntak, som bruken av omvendte skråstreker (\) istedenfor vanlige skråstreker (/) som stiseparatorer, er inndataene og utdataene til og fra dette verktøyet når det kjøres under Windows identisk med den tilsvarende versjonen på Unix. Windowsbrukere kan imidlertid få større suksess ved å kjøre eksemplene i Unix-emuleringsmiljøet Cygwin.
De fleste leserne er sannsynligvis programmerere eller systemadministratorer som har behov for å følge forandringer i kildekode. Dette er den vanligste bruken av Subversion, og derfor legges dette scenariet til grunn for alle bokens eksempler. Men Subversion kan også brukes til å holde rede på forandringer i alle typer informasjon: Bilder, musikk, databaser, dokumentasjon og så videre. For Subversion sin del er alle data bare data.
Selv om denne boken er skrevet med antakelsen om at brukeren aldri har brukt versjonskontroll tidligere, har vi også forsøkt å gjøre det lett for CVS-brukere å foreta en smertefri overgang til Subversion. Spesielle sidenotater kan diskutere CVS nå og da, og et spesielt tillegg summerer opp mesteparten av forskjellene mellom CVS og Subversion.
Denne boken tar sikte på å være nyttig for folk med varierende bakgrunn – fra folk med ingen tidligere erfaring fra versjonskontroll til erfarne systemadministratorer. Avhengig av bakgrunnen din kan enkelte kapitler være mer eller mindre nyttig for deg. Det som kommer nå kan sees på som en “anbefalt leseliste” for forskjellige typer lesere:
Antakelsen her er at du sannsynligvis har brukt CVS før, og klør i fingrene etter å få en Subversiontjener opp og gå så fort som mulig. Kapittel 5 og 6 vil vise deg hvordan du lager ditt første depot og gjør det tilgjengelig over nettverket. Etter at det er gjort, er kapittel 3 og tillegg A den raskeste veien for å bli kjent med Subversionklienten mens du drar nytte av erfaringen du har fra CVS.
Administratoren din har muligens satt opp Subversion allerede, og du trenger å lære hvordan klienten skal brukes. Hvis du aldri har brukt et versjonskontrollsystem før (som CVS), er kapitlene 2 og 3 en vital introduksjon. Hvis du allerede er en gammel rev innen CVS, er kapittel 3 og tillegg A de beste plassene å starte.
Enten du er en bruker eller administrator, vil prosjektet ditt etterhvert vokse seg større. Du vil ønske å lære hvordan man gjør mer avanserte ting med Subversion, som å bruke forgreninger og utføre flettinger (kapittel 4), hvordan bruke Subversions støtte for egenskaper, hvordan konfigurere valg for kjøring (kapittel 7) og andre ting. Kapitlene 4 og 7 er ikke absolutt nødvendige til å begynne med, men pass på å lese dem når du er komfortabel med det grunnleggende.
Antagelig er du allerede kjent med Subversion, og vil nå enten utvide den eller lage ny programvare på toppen av dens mange programmeringsgrensesnitt. Kapittel 8 er bare for deg.
Boken slutter med referansemateriale – kapittel 9 er en referanseguide for alle Subversionkommandoer, og tilleggene dekker en rekke nyttige emner. Dette er kapitlene du mest sannsynlig vil komme tilbake til når du er ferdig med boken.
Denne seksjonen tar for seg de forskjellige konvensjonene brukt i denne boken.
Brukt om kommandoer, utdata fra kommandoer og om valg.
Brukt til utbyttbare elementer i kode og tekst
Brukes for fil- og katalognavn
Dette ikonet indikerer et notat som har sammenheng med den omkringliggende teksten.
Dette ikonet indikerer et nyttig tips som har sammenheng med den omkringliggende teksten.
Dette ikonet indikerer en advarsel som har sammenheng med den omkringliggende teksten.
Merk at kildekodeeksemplene er bare det – eksempler. Selv om de vil kompilere med den riktige kompilatorbesvergelsen, er de beregnet på å illustrere problemet der og da, ikke nødvendigvis være eksempler på god programmeringsstil.
Kapitlene som følger og innholdet deres er listet her:
Dekker historien til Subversion så vel som funksjoner, arkitektur, komponenter og installasjonsmetoder. Inkluderer også en guide for å komme fort i gang.
Forklarer det grunnleggende om versjonskontroll og forskjellige versjoneringsmodeller, sammen med Subversions depot, arbeidskopier og revisjoner.
Tar deg med gjennom en dag i livet til en Subversionbruker. Det demonstrerer hvordan Subversion brukes til å hente, modifisere og sende inn data.
Diskuterer forgreninger, fletting og merking, inkludert beste praksiser for å lage forgreninger og fletting, vanlige bruksmåter, hvordan angre på forandringer og hvordan man lett kan svinge seg fra en gren til en annen.
Beskriver det grunnleggende ved et Subversiondepot, hvordan man lager, konfigurerer og vedlikeholder et depot, og verktøyene du kan bruke for å gjøre alt dette.
Forklarer hvordan du konfigurerer Subversiontjeneren din og de tre måtene å få tilgang til depotet ditt: HTTP, svn-protokollen og lokal tilgang. Det dekker også detaljer omkring autentisering, tilgangskontroll og anonym tilgang.
Utforsker Subversion-klientens konfigurasjonsfiler, fil- og katalogegenskaper, hvordan ignorere filer i arbeidskopien din og til sist, hvordan behandle tredjeparts forgreninger.
Beskriver den interne funksjonaliteten til Subversion, Subversions filsystem, og de administrative områdene i arbeidskopien sett fra en programmerers synspunkt. Demonstrerer hvordan man bruker de offentlige programmeringsgrensesnittene til å skrive et program som bruker Subversion, og viktigst av alt, hvordan bidra til utviklingen av Subversion.
Forklarer i stor detalj hver eneste delkommando i svn, svnadmin og svnlook med nok eksempler for hele familien!
Dekker likheter og forskjeller mellom Subversion og CVS, med flere forslag om hvordan du kan bryte alle de dårlige vanene du har plukket opp etter år med bruk av CVS. Inkludert her er beskrivelser av Subversions revisjonsnumre, versjonerte kataloger, frakoblede operasjoner, update versus status, forgreninger, merker, metadata, konfliktløsing og autentisering.
Adresserer vanlige problemer og vanskeligheter ved bruk og kompilering av Subversion.
Beskriver detaljene om WebDAV og DeltaV, og hvordan du kan konfigurere Subversiondepotet til å være montert som et delt DAV-område.
Diskuterer verktøy som støtter eller bruker Subversion, inkludert alternative klientprogrammer, verktøy for å utforske depotet og så videre.
Denne boken startet som småbiter av dokumentasjon skrevet av Subversions prosjektutviklere, og deretter satt sammen til et enkeltstående verk og omskrevet. Som sådan har den alltid hatt en fri lisens. (Se Tillegg E, Copyright.) Boken ble faktisk skrevet under offentlig oppsyn, som en del av Subversion. Dette betyr to ting:
Du vil alltid finne den seneste versjonen av denne boken i bokens eget Subversiondepot.
Du kan distribuere og gjøre forandringer til denne boken så mye du ønsker – den er under en fri lisens. Selvfølgelig, istedenfor at du distribuerer din egen private versjon av boken, vil vi heller at du sender respons og patcher til utviklermiljøet for Subversion. Se “Contributing to Subversion” for å lære hvordan du deltar i dette fellesskapet.
Du kan sende publiseringskommentarer og spørsmål til O’Reilly her: ###insert boilerplate.
En relativt fersk versjon av boken finner du online på http://svnbook.red-bean.com.
Denne boken ville ikke vært mulig (og heller ikke særlig nyttig) hvis Subversion ikke eksisterte. Derfor vil forfatterne takke Brian Behlendorf og CollabNet for visjonen om å støtte et slikt risikabelt og ambisiøst nytt Open Source-prosjekt; Jim Blandy for det originale Subversion-navnet og designen – vi elsker deg, Jim; Karl Fogel for at han er slik en god venn og stor leder av fellesskapet, i den rekkefølgen.[1]
Takk til O’Reilly og våre redaktører, Linda Mui og Tatiana Diaz for deres tålmodighet og støtte.
Til slutt vil vi takke alle de utallige folkene som har bidratt til denne boken med informative anmeldelser, forslag og korreksjoner: Selv om dette uten tvil ikke er en komplett liste, ville denne boken være ukomplett og ukorrekt uten hjelp fra: Jani Averbach, Ryan Barrett, François Beausoleil, Jennifer Bevan, Matt Blais, Zack Brown, Martin Buchholz, Brane Čibej, John R. Daily, Peter Davis, Olivier Davy, Robert P. J. Day, Mo DeJong, Brian Denny, Joe Drew, Nick Duffek, Ben Elliston, Justin Erenkrantz, Shlomi Fish, Julian Foad, Chris Foote, Martin Furter, Dave Gilbert, Eric Gillespie, Matthew Gregan, Art Haas, Greg Hudson, Alexis Huxley, Jens B. Jorgensen, Tez Kamihira, David Kimdon, Mark Benedetto King, Andreas J. König, Nuutti Kotivuori, Matt Kraai, Scott Lamb, Vincent Lefevre, Morten Ludvigsen, Paul Lussier, Bruce A. Mah, Philip Martin, Féliciano Matias, Patrick Mayweg, Gareth McCaughan, Jon Middleton, Tim Moloney, Mats Nilsson, Joe Orton, Amy Lyn Pilato, Kevin Pilch-Bisson, Dmitriy Popkov, Michael Price, Mark Proctor, Steffen Prohaska, Daniel Rall, Tobias Ringström, Garrett Rooney, Joel Rosdahl, Christian Sauer, Larry Shatzer, Russell Steicke, Sander Striker, Erik Sjölund, Johan Sundström, John Szakmeister, Mason Thomas, Eric Wadsworth, Colin Watson, Alex Waugh, Chad Whitacre, Josef Wolf, Blair Zajac, og hele Subversion-fellesskapet.
Thanks to my wife Frances, who, for many months, got to hear, “But honey, I’m still working on the book”, rather than the usual, “But honey, I’m still doing email.” I don’t know where she gets all that patience! She’s my perfect counterbalance.
Thanks to my extended family for their sincere encouragement, despite having no actual interest in the subject. (You know, the ones who say, “Ooh, you’re writing a book?”, and then when you tell them it’s a computer book, sort of glaze over.)
Thanks to all my close friends, who make me a rich, rich man. Don’t look at me that way—you know who you are.
Takk til min kone Frances, som i mange måneder måtte høre “Men kjære, jeg jobber fortsatt på boken”, istedenfor den vanlige “Men kjære, jeg holder fortsatt på med eposten”. Jeg vet ikke hvor hun får all tålmodigheten fra! Hun er min perfekte motvekt.
Takk til min storfamilie for deres oppriktige oppmuntring, til tross for at de ikke har noen egentlig interesse for emnet. (Du vet, de som sier “Åååja, du skriver en bok?”, og når du forteller at det er en bok om datamaskiner, forsvinner stjerneglansen i øynene deres.)
Takk til alle mine nære venner, som gjør meg til en rik, rik mann. Ikke se på meg på den måten – dere vet hvem dere er.
Huge thanks to my wife Marie for being incredibly understanding, supportive, and most of all, patient. Thank you to my brother Eric who first introduced me to UNIX programming way back when. Thanks to my Mom and Grandmother for all their support, not to mention enduring a Christmas holiday where I came home and promptly buried my head in my laptop to work on the book.
To Mike and Ben: It was a pleasure working with you on the book. Heck, it’s a pleasure working with you at work!
To everyone in the Subversion community and the Apache Software Foundation, thanks for having me. Not a day goes by where I don’t learn something from at least one of you.
Lastly, thanks to my Grandfather who always told me that “freedom equals responsibility.” I couldn’t agree more.
Kjempestor takk til min kone Marie for å være utrolig forståelsesfull, støttende og mest av alt, tålmodig. Takk til min bror Eric som først introduserte meg til UNIX-programmering langt tilbake i tiden. Takk til min mor og bestemor for all deres støtte, for ikke å nevne å holde ut en juleferie hvor jeg kom hjem og med en gang begravde hodet i laptopen for å arbeide på boken.
Til Mike og Ben: Det var en glede å jobbe med dere på boken. Søren heller, det er en glede å arbeide med dere på jobben!
Til alle i Subversion-miljøet og Apache Software Foundation, takk for at dere har meg. Det går ikke en dag uten at jeg lærer noe fra ihvertfall en av dere.
Til sist, takk til min bestefar som alltid fortalte meg at “frihet er lik ansvarlighet”. Jeg kunne ikke være mer enig.
Special thanks to my wife, Amy, for her love and patient support, for putting up with late nights, and for even reviewing entire sections of this book—you always go the extra mile, and do so with incredible grace. Gavin, when you’re old enough to read, I hope you’re as proud of your Daddy as he is of you. Mom and Dad (and the rest of the family), thanks for your constant support and enthusiasm.
Hats off to Shep Kendall, through whom the world of computers was first opened to me; Ben Collins-Sussman, my tour-guide through the open-source world; Karl Fogel—you are my .emacs; Greg Stein, for oozing practical programming know-how; Brian Fitzpatrick—for sharing this writing experience with me. To the many folks from whom I am constantly picking up new knowledge—keep dropping it!
Finally, to the One who perfectly demonstrates creative excellence—thank you.
Spesiell takk til min kone Amy for hennes kjærlighet og tålmodige støtte, for å holde ut med sene kvelder, og for å til og med se over hele seksjoner av denne boken – du går alltid den ekstra milen, og gjør det med utrolig ynde. Gavin, når du er gammel nok til å lese, håper jeg du er like stolt av faren din som han er av deg. Mor og Far (og resten av familien), takk for deres konstante støtte og entusiasme.
Hatten av for Shep Kendall, gjennom ham ble verdenen av datamaskiner først åpnet for meg; Ben Collins-Sussman, min turguide gjennom open source-verdenen; Karl Fogel – du er min .emacs; Greg Stein, for å utstråle praktisk know-how innen programmering; Brian Fitz – for å dele denne skriveopplevelsen med meg. Til de mange folkene som jeg til stadighet plukker opp ny kunnskap etter – fortsett med å strø den omkring!
Til sist, til den Ene som så perfekt demonstrerer kreativ fortreffelighet – takk.
Denne utgaven av boken er blitt oppdatert for å dekke nye funksjoner og forandringer i oppførselen for Subversion 1.1. Her er en rask oversikt over pekere til store forandringer i 1.1.
Det er nå mulig å opprette depot som ikke bruker en Berkeley DB database. Istedenfor lagrer disse nye depotene data i det ordinære filsystemet ved å bruke et tilpasset format. Disse depotene er ikke sårbare for “wedging”, men er heller ikke så gjennomtestet som Berkeley DB-depot. Se “Datalagring i depotet”.
Unixbrukere kan nå opprette symbolske lenker og plassere dem under versjonskontroll med svn add-kommandoen. Se svn add og “svn:special”.
Forgreninger (kopier) av filer og kataloger vedlikeholder historiske forbindelser til kilden deres, men i Subversion 1.0 fulgte bare svn log denne kopierings/navneskiftehistorien, ikke andre kommandoer som svn diff, svn merge, svn list eller svn cat. I Subversion 1.1 tråler alle delkommandoene seg bakover gjennom kopieringer og navneskifter ved undersøkelser av eldre versjoner av filer og kataloger.
I kommandolinjeklienten for 1.0 måtte brukere beskytte (“escape”) URL-er manuelt. Klienten aksepterte bare “lovlige og korrekte” URL-er, som http://tjener/sti%20med%20mellomrom/prosjekt/espa%F1a. Kommandolinjeklienten for 1.1 vet nå hvordan den skal gjøre det som nettlesere har gjort i lang tid: Den autobeskytter tegn som mellomrom og bokstaver med aksent, så lenge brukeren plasserer URL-en i hermetegn for å beskytte tegn fra skallet: "http://tjener/sti med mellomrom/prosjekt/españa"
Subversion 1.1 bruker nå gettext() for å vise oversatte feil-, informasjons- og hjelpemeldinger til brukeren. Det er foreløpige oversettelser for tysk, spansk, polsk, svensk, tradisjonell kinesisk, japansk, portugisisk (Brasil) og norsk (bokmål). For å få Subversion til å bruke en av oversettelsene, sett skallets LANG-variabel til en støttet verdi (for eksempel nb_NO).
Det har vært historiske problemer med tilgangsrettigheter når flere brukere deler en arbeidskopi, det ser ut til å være ordnet nå.
Dette er en ny variabel for bruk under kjøring som bare kobler ut lagring av passord, så tjenersertifikater fortsatt kan lagres. Se “Config”.
Kommandoene svn checkout, svn update, svn status og svn blame er raskere. Mer enn femti små feil er blitt fikset, alle beskrevet i Subversionprosjektets CHANGES-fil (på http://svn.collab.net/repos/svn/trunk/CHANGES ).
svn blame --verbose: Se svn blame.
svn export --native-eol EOL: Se svn export.
svn add --force: Se svn add.
svnadmin dump --deltas: Se “Flytte et depot”.
svnadmin create --fs-type TYPE: Se svnadmin create.
svnadmin recover --wait: Se svnadmin recover.
svnserve --tunnel-user=NAME: Se “svnserve Switches”.
Innholdsfortegnelse
Versjonskontroll er kunsten å behandle forandringer i informasjon. Det har lenge vært et kritisk verktøy for programmerere, som typisk nok bruker tiden sin på å gjøre små forandringer i programvare og deretter forandrer det tilbake dagen etterpå. Men nyttigheten av versjonskontroll rekker langt forbi grensene for programutviklingsverdenen. Alle steder du finner folk som bruker datamaskiner til å behandle informasjon som forandrer seg ofte, der er det plass for versjonskontroll. Og det er her Subversion kommer inn i bildet.
Dette kapitlet inneholder en høytsvevende introduksjon til Subversion – hva det er, hva det gjør og hvordan få tak i det.
Subversion er et fritt/opensource versjonskontrollsystem. Det betyr: Subversion behandler filer og kataloger over tid. Et tre av filer er plassert i et sentralt depot. Depotet ligner mye på en vanlig filtjener, med det unntaket at det husker hver eneste forandring noensinne gjort på filene og katalogene. Dette tillater deg å hente fram eldre versjoner av dataene dine, eller studere historien for hvordan dataene dine har forandret seg. På grunn av dette tenker mange på versjonskontroll som en slags “tidsmaskin”.
Subversion kan aksessere depotet sitt via datanettverk, som tillater det å bli brukt av personer på forskjellige maskiner. På et visst nivå gir muligheten for forskjellige personer til å modifisere og behandle den samme datamengden seg utslag i samarbeid. Fremgangen kan gå fortere uten en trang flaskehals som alle forandringene må gå gjennom. Og fordi arbeidet er versjonert, trenger du ikke frykte at kvaliteten er noe du må gi avkall på når du mister denne flaskehalsen – hvis en feil forandring er gjort med dataene, bare omgjør denne forandringen.
Noen versjonskontrollsystemer er også software configuration management-systemer (SCM). Disse systemene er spesielt beregnet på å vedlikeholde trær av kildekode, og har mange funksjoner som er spesielt tilpasset programutvikling – de kan ha en viss forståelse av programmeringsspråk, eller de tilbyr verktøy for å bygge programvare. Subversion, derimot, er ikke et av disse systemene. Det er et generelt system som kan bli brukt til å vedlikeholde en hvilken som helst samling av filer. For ditt vedkommende kan det være kildekode – for andre, alt fra huskelister til butikken til redigeringsfiler for digital video og annet.
Tidlig i år 2000 startet CollabNet, Inc. (http://www.collab.net) letingen etter utviklere for å lage en erstatning for CVS. CollabNet tilbyr programvare for å muliggjøre samarbeid – SourceCast – der en komponent er versjonskontroll. Selv om SourceCast brukte CVS som sitt første versjonskontrollsystem, var begrensningene i CVS helt fra begynnelsen veldig tydelige, og CollabNet visste at noe bedre måtte finnes på et eller annet tidspunkt. Uheldigvis hadde CVS blitt de facto-standarden i opensource-verdenen fordi det ikke fantes noe bedre, ihvertfall ikke under en fri lisens. Så CollabNet gikk inn for å skrive et nytt versjonskontrollsystem fra bunnen av, basert på de grunnleggende idéene fra CVS, men uten feilene og manglende funksjoner.
I februar 2000 kontaktet de Karl Fogel, forfatteren av Open Source Development with CVS (Coriolis, 1999), og spurte om han ville arbeide på dette nye prosjektet. Tilfeldigvis diskuterte Karl på dette tidspunktet et design for et nytt versjonskontrollsystem med sin venn Jim Blandy. I 1995 startet de to Cyclic Software, et firma som tilbød kontrakter for CVS-støtte, og selv om de senere solgte forretningen, brukte de fortsatt CVS hver dag på jobben. Frustrasjonen deres over CVS hadde fått Jim til å tenke nøye over bedre veier til å behandle versjonerte data, og han hadde allerede kommet opp med ikke bare navnet “Subversion”, men også med den grunnleggende designen av depotet for Subversion. Da forespørselen kom fra CollabNet, gikk Karl øyeblikket med på å arbeide med prosjektet, og Jim fikk sin arbeidsgiver, RedHat Software, til å hovedsaklig donere ham til prosjektet for en udefinert tidsperiode. CollabNet ansatte Karl og Ben Collins-Sussman, og detaljert designarbeid startet i mai. Med hjelp av noen velplasserte nålestikk fra Brian Behlendorf, Jason Robbins fra CollabNet og Greg Stein (på den tiden en uavhengig utvikler aktiv innen spesifiseringsprosessen for WebDAV/DeltaV), fikk Subversion raskt trukket til seg en samling aktive utviklere. Det viste seg at mange hadde hatt de samme frustrerende opplevelsene med CVS, og ønsket sjansen til å endelig få gjort noe med dette velkommen.
Den originale designgruppen satte seg enkle mål. De ville ikke bryte nytt land innen versjonskontrollteknikken, de ville bare forbedre CVS. De bestemte seg for at Subversion skulle ha de samme funksjonene som CVS og beholde den samme utviklingsmodellen, men uten de mest åpenbare feilene i CVS. Og selv om det nødvendigvis ikke skulle være en fullstendig erstatning for CVS, skulle det være likt nok til at enhver CVS-bruker kunne gjennomføre overgangen med små anstrengelser.
Etter fjorten måneder med programmering ble Subversion “selvlagrende” den 31. august 2001. Det betydde at Subversionutviklerne avsluttet bruken av CVS til å vedlikeholde Subversions kildekode, og gikk over til å bruke Subversion istedenfor.
Selv om CollabNet startet prosjektet, og fortsatt finansierer en stor del av arbeidet (de betaler lønningene for noen få fulltidsansatte Subversionutviklere), drives Subversion som de fleste opensource-prosjekter, styrt av et løst sammensatt og gjennomsiktig regelverk som oppmuntrer til elitestyre. CollabNets copyrightlisens er fullstendig i samsvar med Debians retningslinjer for fri programvare – Debian Free Software Guidelines. Med andre ord, alle kan hente, modifisere og redistribuere Subversion som de selv ønsker; ingen tillatelse fra CollabNet eller andre er nødvendig.
Når vi diskuterer funksjonalitetene som Subversion bringer til versjonskontrollbordet, hjelper det ofte å snakke om dem i vendinger som beskriver hvordan de er forbedringer i forhold til måten CVS er konstruert. Hvis du ikke er vant med CVS, er det ikke sikkert du forstår alle disse funksjonene. Og hvis du ikke er kjent med versjonskontroll i det hele tatt, kan nok blikket sløves såfremt du ikke har lest Kapittel 2, Grunnleggende konsepter, hvor vi foretar en forsiktig introduksjon til versjonskontroll generelt.
Subversion tilbyr:
CVS holder bare rede på historien til individuelle filer, men Subversion implementerer et “virtuelt” versjonert filsystem som følger forandringer til hele katalogtrær over tid. Filer og kataloger er versjonert.
Siden CVS er begrenset til versjonering av filer, er ikke operasjoner som kopiering og navneskifter – som kan hende med filer, men som egentlig er forandringer i innholdet av katalogen de ligger i – støttet i CVS. I tillegg kan du ikke i CVS erstatte en versjonert fil med en ny ting med det samme navnet uten at det nye elementet overtar historien til den gamle – kanskje helt urelaterte – filen. Med Subversion kan du legge til, slette, kopiere og skifte navn på både filer og kataloger. Og hver fil som er nylig lagt til begynner med en frisk, ren historie helt for seg selv.
En samling av forandringer går enten fullstendig inn i depotet, eller ikke i det hele tatt. Dette tillater utviklerne å konstruere og legge inn forandringer som logiske porsjoner, og forhindrer problemer som kan oppstå når bare en del av forandringene ble lagt inn i depotet.
Hver fil og katalog har et sett med egenskaper – egenskapsnavn og deres verdier – assossiert med seg. Du kan opprette og lagre ethvert vilkårlig egenskapsnavn/verdi-par som du ønsker. Egenskaper er versjonert over tid, akkurat som filinnhold.
Subversion har et løst definert begrep om depottilgang, noe som gjør det enkelt for brukere å implementere nye nettverksmekanismer. Subversion kan plugges inn i Apache HTTP-serveren som en tilleggsmodul. Dette gir Subversion en stor fordel innen stabilitet og kommunikasjon med brukere og prosesser, og øyeblikkelig tilgang til eksisterende funksjoner som denne serveren tilbyr – autentisering, autorisasjon, “wire compression” og så videre. En lettere egenstående Subversiontjener-prosess er også tilgjengelig. Denne tjeneren snakker en tilpasset protokoll som lett kan bli kjørt gjennom en SSH-tunnel.
Subversion uttrykker filforskjeller ved en binær forskjellsalgoritme som fungerer likt både på tekst (lesbar for det menneskelige øye) og binære (uleselige for mennesker) filer. Begge filtypene pakkes på samme måte i depotet, og forskjeller blir overført i begge retninger over nettverket.
Belastningen ved å lage en gren eller merke trenger ikke å være proporsjonal med prosjektstørrelsen. Subversion lager forgreninger og merker ved å rett og slett kopiere prosjektet, ved hjelp av en mekanisme lik en hard lenke. Dermed tar disse operasjonene bare en liten, konstant mengde tid.
Subversion har ingen historisk bagasje; programmet er implementert som en samling av delte C-biblioteker med veldefinerte programmeringsgrensesnitt. Dette gjør Subversion ekstremt lett å vedlikeholde og lett å bruke av andre applikasjoner og språk.
Figur 1.1, “Subversions arkitektur” illustrerer hva man kan kalle en “milehøy” oversikt over Subversions design.
På den ene kanten er et Subversiondepot som inneholder alle dine versjonerte data. I den andre enden er Subversionklienten din, som holder rede på lokale avspeilinger av deler av disse versjonerte dataene (kalt “arbeidskopier”). Mellom disse yttergrensene er det flere ruter gjennom diverse tilgangslag – Repository Access (RA). Noen av disse rutene går over datanettverk og gjennom datatjenere som deretter aksesserer depotet. Andre dropper hele nettverket og bruker direkte tilgang til depotet.
Subversion er bygget på et portabilitetslag kalt APR (biblioteket Apache Portable Runtime). Dette betyr at Subversion skal kunne virke på alle operativsystemer som Apache httpd-serveren kjører på: Windows, Linux, alle varianter av BSD, Mac OS X, Netware og andre.
Den letteste måten å få tak i Subversion er å hente en binær pakke bygget for ditt operativsystem. Subversions hjemmeside (http://subversion.tigris.org) har ofte disse pakkene tilgjengelig for nedlasting, lagt ut av frivillige. Denne plassen inneholder vanligvis grafiske installasjonspakker for brukere av operativsystemer fra Microsoft. Hvis du kjører et Unix-lignende operativsystem, kan du bruke systemets innebygde distribusjonssystem (RPM-filer, DEB-filer, “ports”-treet, osv.) for å hente Subversion.
Du kan også bygge Subversion direkte fra kildekode. Siste versjon av programmet kan hentes fra Subversions hjemmeside. Etter at du har pakket det ut, følg instruksjonene i filen INSTALL for å kompilere den. Merk at en offentliggjort kildekodepakke inneholder alt du trenger for å bygge en kommandolinjeklient i stand til å kommunisere med et fjerntliggende depot (mer spesifikt, bibliotekene apr, apr-util og neon). Men valgfrie deler av Subversion har mange andre avhengigheter, som Berkeley DB og muligens Apache httpd. Hvis du vil foreta en komplett kompilering, vær sikker på at du har alle pakkene dokumentert i INSTALL-filen. Hvis du planlegger å arbeide på selve Subversion, kan du bruke svn-klienten din for å hente den siste rykende ferske kildekoden. Dette er dokumentert i “Get the Source Code”.
Subversion, installasjonen er ferdig, består av flere deler. Det følgende er en rask oversikt over hva du får. Ikke bli skremt hvis den snaue beskrivelsen etterlater deg med å klø deg i hodet – det er mange flere sider i denne boken som er beregnet på å fjerne denne forvirringen.
Kommandolinjeklienten.
Et program for å rapportere tilstanden (i betydningen av revisjoner for de elementene som finnes) for en arbeidskopi.
Et verktøy for å inspisere et Subversiondepot.
Et verktøy for å lage, tilpasse eller reparere et Subversiondepot.
Et program for å filtrere strømmer i dumpfil-format for et Subversiondepot.
En programtilleggsmodul for Apache HTTP-serveren, som brukes til å gjøre depotet ditt tilgjengelig for andre over et nettverk.
Et tilpasset selvstendig tjenerprogram, kjørbar som en daemon-prosess eller startbar av SSH; en annen måte å gjøre depotet ditt tilgjengelig for andre over et nettverk.
Forutsatt at du har Subversion korrekt installert, er du klar til å starte. De neste to kapitlene vil vise deg bruken av svn, Subversions klient for kommandolinjebruk.
Noen personer har problemer med å absorbere en ny teknologi ved å lese dokumentasjon lagt ut etter et “ovenfra og ned”-prinsipp som i denne boken. Denne seksjonen er en veldig kort introduksjon til Subversion., og er ment å gi personer som lærer “nedenfra og opp” en sjanse. Hvis du er en av dem som liker å lære gjennom eksperimentering, vil den følgende demonstrasjonen få deg i gang. Under gjennomgangen vil vi gi deg lenker til de relevante kapitlene i denne boken.
Hvis du er ny innen hele konseptet med versjonskontroll eller til “kopier-rediger-flett”-modellen brukt av både CVS og Subversion, bør du lese Kapittel 2, Grunnleggende konsepter før du går videre.
Det følgende eksempelet forutsetter at du har svn, kommandolinjeklienten, og svnadmin, det administrative verktøyet, klar til kjøring. Det forutsetter også at svn-klienten er kompilert med støtte for Berkeley DB. For å kontrollere dette, kjør svn --version og sjekk at ra_local-modulen er tilgjengelig. Uten denne modulen kan ikke klienten få adgang til URLer av typen file://.
Subversion lagrer alle versjonerte data i et sentralt depot. For å begynne, lag et nytt depot:
$ svnadmin create /sti/til/depot $ ls /sti/til/depot conf/ dav/ db/ format hooks/ locks/ README.txt
Denne kommandoen lager en ny katalog /sti/til/depot som inneholder et Subversiondepot. Vær sikker på at denne katalogen er på en lokal disk, ikke en disk på nettverket. Denne nye katalogen inneholder inneholder hovedsaklig en samling med Berkeley DB databasefiler. Du vil ikke se dine versjonerte filer hvis du tar en kikk innenfor. For mer informasjon om opprettelse og vedlikehold av depot, se Kapittel 5, Depotadministrasjon.
Deretter, lag et tre av filer og kataloger som skal importeres inn i depotet. For grunner som vil bli forklart senere (se Kapittel 4, Forgrening og fletting), bør strukturen inneholde tre toppkataloger kalt branches, tags og trunk:
/tmp/project/branches/
/tmp/project/tags/
/tmp/project/trunk/
foo.c
bar.c
Makefile
…
Når du har klargjort et tre med data, importer dataene inn i depotet med kommandoen svn import (se “svn import”):
$ svn import /tmp/project file:///sti/til/depot -m "Grunnleggende import" Adding /tmp/project/branches Adding /tmp/project/tags Adding /tmp/project/trunk Adding /tmp/project/trunk/foo.c Adding /tmp/project/trunk/bar.c Adding /tmp/project/trunk/Makefile … Committed revision 1. $
Nå inneholder depotet dette treet med data. Legg merke til at den originale /tmp/project-katalogen er uforandret, Subversion kjenner ikke til den. (Faktisk kan du til og med slette denne katalogen hvis du ønsker det.) For å starte med å manipulere dataene i depotet, må du lage en ny “arbeidskopi” av dataene, et slags personlig arbeidsområde. Be Subversion om å “hente ut” en arbeidskopi fra katalogen trunk i depotet:
$ svn checkout file:///sti/til/depot/trunk project A project/foo.c A project/bar.c A project/Makefile … Checked out revision 1.
Nå har du en personlig kopi av en del av depotet i en ny katalog kalt project. Du kan redigere filene i arbeidskopien og deretter legge disse forandringene inn i depotet.
Gå inn i arbeidskopien og rediger innholdet av en fil.
Kjør kommandoen svn diff for å se en unified diff (forskjellsfil) over forandringene dine.
Kjør svn commit for å legge inn den nye versjonen av filen din til depotet.
Kjør svn update for å oppdatere arbeidskopien din i forhold til depotet.
For en full gjennomgang av tingene som du kan gjøre med arbeidskopien din, les Kapittel 3, Guidet tur.
Ved dette punktet har du valget å gjøre depotet ditt tilgjengelig over et nettverk. Se Kapittel 6, Server Configuration for å lære om de forskjellige tjenerprosessene tilgjengelig og hvordan du setter dem opp.
Innholdsfortegnelse
Dette kapittelet er en kort, lettvint introduksjon til Subversion. Hvis du er ny innen versjonskontroll, er dette kapittelet definitivt for deg. Vi begynner med en diskusjon om generelle konsepter innen versjonskontroll, jobber oss gjennom de spesifikke idéene bak Subversion, og viser noen enkle eksempler på bruk av Subversion.
Selv om eksemplene i dette kapittelet viser personer som deler samlinger av kildekode til programmer, husk at Subversion kan behandle alle typer filsamlinger – det er ikke begrenset til å hjelpe dataprogrammerere.
Subversion er et sentralisert system for å dele informasjon. Dens kjerne er et depot, som er et sentralt lager av data. Depotet lagrer informasjon i form av et filsystemtre – et typisk hierarki av filer og kataloger. Ethvert antall klienter kobler seg til depotet, og leser eller skriver deretter til disse filene. Ved å skrive data, gjør klienten informasjonen tilgjengelig for andre; ved å lese data henter klienten informasjon fra andre. Figur 2.1, “Et typisk klient/tjener-system” illustrerer dette.
Så hvorfor er dette interessant? Så langt høres dette ut som definisjonen av en typisk filtjener. Og det stemmer, depotet er en slags filtjener, men ikke den typen du vanligvis kommer ut for. Det som gjør Subversiondepotet spesielt er at det husker hver eneste forandring noensinne skrevet til det: Hver forandring til hver eneste fil, og til og med forandringer i selve katalogtreet, som opprettelser, slettinger og ommøbleringer i filer og kataloger.
Når en klient leser data fra depotet, ser den vanligvis bare den siste versjonen av filsystemtreet. Men klienten har også muligheten til å se tidligere tilstander av filsystemet. For eksempel, en klient kan spørre historiske spørsmål som: “Hva inneholdt denne katalogen forrige onsdag?” eller “Hvem var den siste personen som forandret denne filen, og hvilke forandringer gjorde vedkommende?” Dette er typen spørsmål som er hjertet av ethvert versjonskontrollsystem: Systemer som er designet for å lagre og følge forandringer i data over tid.
Hovedmålet for et versjonskontrollsystem er å muliggjøre samarbeidsredigering og deling av data. Men forskjellige systemer bruker forskjellige strategier for å oppnå dette.
Alle versjonskontrollsystemer må løse det samme fundamentale problemet: Hvordan vil systemet tillate brukere å dele informasjon, men forhindre dem fra å tråkke hverandre på tærne? Det er alt for lett for brukere å overskrive hverandres forandringer i depotet ved en ulykke.
Tenk over scenariet vist i Figur 2.2, “Problemet som må unngås”. Sett at vi har to arbeidskolleger, Harry og Sally. De bestemmer seg begge for å redigere den samme filen i depotet samtidig. Hvis Harry lagrer sine forandringer til depotet først, er det (noen øyeblikk senere) mulig at Sally feilaktig overskriver dem med hennes egen nye versjon av filen. Selv om Harrys versjon av filen ikke vil være tapt for alltid (fordi systemet husker hver eneste forandring), vil alle forandringene Harry gjorde ikke være med i Sallys nyere versjon av filen, fordi hun så aldri Harrys forandringer til å begynne med. Harrys arbeid er fortsatt borte – eller i det minste borte fra den siste versjonen av filen – og sannsynligvis ved en ulykke. Dette er definitivt en situasjon vi vil unngå!
Mange versjonskontrollsystemer bruker en modell av typen lås-rediger-lås opp når de tar for seg dette problemet. I et sånt system tillater depotet bare en person å forandre en fil om gangen. Først må Harry “låse” filen før han kan begynne med å gjøre forandringer i den. Låsing av en fil er mye likt det å låne en bok på biblioteket; hvis Harry har låst en fil kan ikke Sally gjøre forandringer i den. Hvis hun prøver å låse filen, vil ikke depotet tillate dette. Alt hun kan gjøre er å lese filen, og vente på at Harry gjør seg ferdig med sine forandringer og så slipper låsen han har satt opp. Etter at Harry låser opp filen, er hans tur over, og nå kan Sally ta sin runde med låsing og redigering. Figur 2.3, “Lås-rediger-lås opp-løsningen” demonstrerer denne enkle løsningen.
Problemet med “lås-rediger-lås opp”-metoden er at den er ganske restriktiv, og blir ofte en hindring for brukerne:
Låsing kan medføre administrative problemer. Noen ganger hender det at Harry låser en fil og glemmer den. I mellomtiden, fordi Sally fortsatt venter på å få redigere filen, har hun hendene bundet. Og så drar Harry på ferie. Nå må Sally få en administrator til å fjerne Harrys lås. Situasjonen ender opp med mange forsinkelser og mye bortkastet tid.
Låsing kan forårsake unødvendig serialisering. Hva hvis Harry redigerer begynnelsen av en tekstfil, og Sally rett og slett bare vil redigere slutten av den samme filen? Disse forandringene overlapper ikke i det hele tatt. De kan enkelt redigere filen samtidig, og ingen stor skade vil skje, såfremt forandringene ble flettet fint sammen. Det er ingen vits i at de må vente på tur i denne situasjonen.
Låsing kan skape en falsk følelse av trygghet. Tenk deg at Harry låser og redigerer fil A, mens Sally samtidig redigerer fil B. Tenk deg også at A og B er avhengig av hverandre, og forandringene er hver for seg inkompatible med hverandre. Plutselig virker ikke A og B sammen mer. Låsesystemet var ikke i stand til å forhindre problemet – men skapte likevel en falsk følelse av trygghet. Det er lett for Harry og Sally å tenke seg at ved å låse filer, starter hver av dem en trygg, isolert oppgave, og de bryr seg dermed ikke med å diskutere deres inkompatible forandringer på et tidligere tidspunkt.
Subversion, CVS og andre versjonskontrollsystemer bruker en modell av typen kopier-rediger-flett som et alternativ til låsing. I denne modellen kontakter klienten til hver bruker prosjektdepotet og lager en personlig arbeidskopi – et lokalt speil av depotets filer og kataloger. Brukere arbeider så parallelt ved å modifisere deres private kopier. Til slutt blir de private kopiene flettet inn i en ny, endelig versjon. Versjonskontrollsystemet hjelper ofte til med flettingen, men til syvende og sist er det et menneske som er ansvarlig for å la det skje skikkelig.
Her er et eksempel. La oss si at Harry og Sally hver for seg lager arbeidskopier av det samme prosjektet, kopiert fra depotet. De arbeider samtidig, og gjør forandringer i den samme filen A innenfor sine kopier. Sally lagrer sine forandringer til depotet først. Når Harry prøver å lagre sine forandringer senere, informerer depotet ham om at hans fil A er utdatert. Med andre ord, filen A i depotet har på en eller annen måte forandret seg siden han kopierte den sist. Så Harry ber klienten sin om å flette alle nye forandringer fra depotet inn i hans arbeidskopi av fil A. Sjansene for at Sallys forandringer ikke overlapper med hans egne er store; så når begges forandringer er lagt inn i filen, lagrer han sin egen arbeidskopi til depotet. Figur 2.4, “Kopier-rediger-flett-løsningen” og Figur 2.5, “Kopier-rediger-flett-løsningen (forts.)” viser denne prosessen.
Men hva hvis Sallys forandringer likevel overlapper med Harrys forandringer? Hva da? Denne situasjonen kalles en konflikt, og er vanligvis ikke mye til problem. Når Harry ber klienten sin om å flette sammen de nyeste forandringene i depotet inn i hans arbeidskopi, blir det vist at hans kopi av fil A er i konflikt: Han vil være i stand til å se begge settene av konfliktskapende forandringer, og velge mellom dem manuelt. Legg merke til at programvare ikke kan løse konflikter automatisk; bare mennesker er i stand til å forstå og gjøre de nødvendige intelligente valgene. Når Harry har løst de overlappende forandringene manuelt – kanskje etter en diskusjon med Sally – kan han trygt lagre den flettede filen tilbake til depotet.
“Kopier-rediger-flett”-modellen kan høres litt kaotisk ut, men i praksis går det ekstremt glatt. Brukere kan jobbe parallelt, og aldri vente på hverandre. Når de arbeider på de samme filene, viser det seg at mesteparten av de samtidige forandringene ikke overlapper i det hele tatt; konflikter er sjeldne. Og tiden det tar å løse konflikter er langt mindre enn tiden tapt med et låsesystem.
Til sist koker det hele ned til en kritisk faktor: Brukerkommunikasjon. Når brukerne kommuniserer dårlig, øker antallet av både programmessige og språkmessige konflikter. Ingen systemer kan tvinge brukerne til å kommunisere perfekt, og ingen systemer kan oppdage språkmessige konflikter. Så, det er ikke mye poeng i å bli lurt av et falskt løfte om at et låsesystem på en eller annen måte vil forhindre konflikter; i praksis ser låsing ut til å hemme produktiviteten mer enn noe annet.
Det er på tide å gå fra det abstrakte til det konkrete. I denne seksjonen vil vi vise reelle eksempler på bruk av Subversion.
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.
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 2.6, “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 A calc/Makefile A calc/integer.c A calc/button.c $ 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 filen. 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 Sending button.c Transmitting file data . Committed revision 57.
Nå er dine forandringer til button.c lagt inn i depotet; hvis en annen bruker henter ut en arbeidskopi av /calc, vil de se dine forandringer i den seneste versjonen av filen.
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
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.
En svn commit-operasjon kan publisere forandringer til ethvert 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 det komplette settet med forandringer som en samlet enhet.
I depotet blir hver innlegging behandlet som en atomisk transaksjon: Enten blir alle forandringene lagt inn, eller så blir ingen lagt inn. 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 2.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.
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.
For hver fil i en arbeidskatalog, lagrer Subversion to essensielle deler informasjon i det administrative .svn-området:
Hvilken revisjon arbeidsfilen din er basert på (dette kalles filens 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:
Filen er uforandret i arbeidskopien, og ingen forandringer til denne filen er blitt lagt inn i depotet siden arbeidskopien ble lagt inn. En svn commit på filen vil ikke gjøre noe som helst, og en svn update på filen vil heller ikke gjøre noe.
Filen er blitt forandret i arbeidskopien, og ingen forandringer i denne filen er blitt lagt inn i depotet siden stamrevisjonen. Det er lokale forandringer som ikke er blitt lagt inn i depotet, så en svn commit av filen vil lykkes i å publisere forandringene dine, og en svn update på filen vil ikke gjøre noen ting.
Filen er ikke blitt forandret i arbeidskopien, men har forandret seg i depotet. Filen må etterhvert bli oppdatert, for å få den til å samsvare med den offentlige revisjonen. En svn commit på filen vil ikke gjøre noe, og en svn update på filen vil legge de seneste forandringene inn i arbeidskopien din.
Filen er blitt forandret både i arbeidskopien og depotet. En svn commit av filen vil feile med en “out-of-date”-feilmelding. Filen 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”.
Som et generelt prinsipp prøver Subversion å være så fleksibel som mulig. En spesiell type av fleksibilitet er muligheten til å ha en arbeidskopi som inneholder blandede revisjonsnumre.
Til å begynne med er det ikke sikkert det er helt klart hvorfor denne typen fleksibilitet er å betrakte som en fordel, og ikke en svakhet. Etter å ha fullført en innlegging i depotet, er de nylig innlagte filene og katalogene i en nyere arbeidsrevisjon enn resten av kopien. Det ser ut som litt av et rot. Som tidligere demonstrert, kan arbeidskopien alltid bli satt til en enkelt arbeidsrevisjon ved å kjøre svn update. Hvorfor vil noen med vilje ønske seg en blanding av arbeidsrevisjoner?
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 3. Kanskje vil du prøve en tidligere versjon av en delmodul, lagret i en underkatalog, eller kanskje du vil utforske et antall tidligere versjoner av en fil sett i forhold til det seneste treet.
Hvordan du enn gjør bruk av blandede revisjoner i arbeidskopien, er det begrensninger i denne fleksibiliteten.
For det første kan du ikke legge inn sletting av en fil eller 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 6. 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.
Vi har dekket flere fundamentale konsepter for Subversion i dette kapitlet:
Vi har introdusert begrepene om et sentralt depot, arbeidskopien til klienten, og rekken av revisjonstrær i depotet.
Vi har sett noen enkle eksempler på hvordan to arbeidskolleger kan bruke Subversion til å publisere og motta forandringer til og fra hverandre, ved å bruke “kopier-rediger-flett”-modellen.
Vi har snakket litt om måten Subversion følger og behandler informasjon i en arbeidskopi.
Så langt skal du ha en god oversikt om hvordan Subversion arbeider generelt sett. Bevæpnet med kunnskap er du nå klar til å hoppe inn i neste kapittel, som er en detaljert gjennomgang av Subversions kommandoer og funksjoner.
Innholdsfortegnelse
Nå vil vi gå inn i detaljene omkring bruken av Subversion. Når du har nådd slutten av dette kapittelet, vil du være i stand til å utføre omtrent alle de oppgavene du trenger for å bruke Subversion i en vanlig dags arbeid. Du starter med en innledende uthenting av koden din, og går gjennom å gjøre forandringer og studere disse forandringene. Du vil også få se hvordan du legger inn forandringer laget av andre inn i arbeidskopien din, studere dem, og jobbe deg gjennom eventuelle konflikter som måtte oppstå.
Merk at dette kapittelet ikke er ment å være en fullstendig liste over Subversions kommandoer – det er heller en uformell introduksjon til de mest vanlige Subversionoppgavene du vil komme ut for. Dette kapittelet forutsetter at du har lest og forstått Kapittel 2, Grunnleggende konsepter og er kjent med den generelle modellen til Subversion. For en komplett referanse over alle kommandoene, se Kapittel 9, Subversion Complete Reference.
Før du leser videre, her er den viktigste kommandoen du noen gang vil trenge når du bruker Subversion: svn help. Kommandolinjeklienten til Subversion er selvdokumenterende – til enhver tid vil en rask svn help <delkommando> beskrive syntaksen, valg, og oppførselen til delkommando.
Du bruker svn import for å importere et nytt prosjekt inn i et Subversiondepot. Selv om dette sannsynligvis er det aller første du vil gjøre når du setter opp Subversiontjeneren din, er det ikke noe som skjer veldig ofte. For en detaljert beskrivelse av import, se “svn import” senere i dette kapitlet.
Før vi går videre, bør du vite litt om hvordan du kan identifisere en spesiell revisjon i depotet. Som du lærte i “Revisjoner”, er en revisjon et “øyeblikksbilde” av depotet på et visst tidspunkt. Når du fortsetter med å legge inn revisjoner og øker størrelsen på depotet ditt, trenger du en mekanisme for å identifisere disse øyeblikksbildene.
Du spesifiserer disse revisjonene ved å bruke --revision (-r)-valget pluss revisjonen du ønsker (svn --revision REV eller du kan spesifisere et område ved å separere to revisjoner med et kolon (svn --revision REV1:REV2). Og Subversion lar deg referere til disse revisjonene via nummer, nøkkelord eller dato.
Når du lager et nytt Subversiondepot, begynner det livet sitt på revisjon null og hver etterfølgende innlegging øker revisjonsnummeret med en. Etter at innleggingen din er fullført, informerer Subversionklienten deg om det nye revisjonsnummeret:
$ svn commit --message "Corrected number of cheese slices." Sending sandwich.txt Transmitting file data . Committed revision 3.
Hvis du på et punkt i fremtiden vil referere til den revisjonen (vi vil se hvordan og hvorfor vi kanskje skulle ønske å gjøre det senere i dette kapitlet), kan du referere til den som “3”.
Subversionklienten forstår noen revisjonsnøkkelord. Disse nøkkelordene kan bli brukt istedenfor heltallsargumenter til --revision-valget, og blir oversatt til spesifikke revisjonsnumre av Subversion:
Hver katalog i arbeidskopien din inneholder en administrativ underkatalog kalt .svn. For hver eneste fil i en katalog lagrer Subversion en kopi av hver fil i det administrative området. Denne kopien er en en umodifisert (ingen nøkkelordutvidelser, ingen linjesluttkonvertering, ingen noesomhelst) kopi av filen som da den eksisterte i den siste revisjonen (kalt “BASE”-revisjonen) som du oppdaterte den til i arbeidskopien din. Vi refererer til denne filen som den uberørte kopien eller text-base-versjonen av filen, og det er alltid en eksakt byte-for-byte-kopi av filen som den eksisterer i depotet.
Den seneste revisjonen i depotet.
Den “uberørte” revisjonen for et element i en arbeidskopi.
Den siste revisjonen som et element forandret seg i før (eller på) BASE.
Revisjonen akkurat før den siste revisjonen der et element forandret seg. (Teknisk sett, COMMITTED - 1.)
PREV, BASE og COMMTTED kan bli brukt til å referere til lokale stier, men ikke til URLer.
Her er noen eksempler på revisjonsnøkkelord i aksjon. Ikke bli urolig om du ikke forstår den fulle betydningen av kommandoene enda; vi vil forklare disse kommandoene mens vi går gjennom kapittelet:
$ svn diff --revision PREV:COMMITTED foo.c
# viser den siste forandringen lagt inn til foo.c
$ svn log --revision HEAD
# viser loggmelding for den seneste innleggingen i depotet
$ svn diff --revision HEAD
# sammenligner arbeidsfilen din (med lokale forandringer) med den
# seneste versjonen i depotet.
$ svn diff --revision BASE:HEAD foo.c
# sammenligner din “uberørte” foo.c (ingen lokale
# modifikasjoner) mot den seneste versjonen i depotet.
$ svn log --revision BASE:HEAD
# viser alle loggmeldinger siden du sist oppdaterte
$ svn update --revision PREV foo.c
# setter tilbake til den forrige forandringen på foo.c .
# (Arbeidsrevisjonen til foo.c minsker.)
Disse nøkkelordene tillater deg å utføre mange vanlige (og hjelpsomme) operasjoner uten å måtte lete opp spesielle revisjonsnumre eller huske den eksakte revisjonen til arbeidskopien din.
Alle plasser du spesifiserer et revisjonsnummer eller revisjonsnøkkelord, kan du også spesifisere en dato innenfor krøllparenteser “{}”. Du kan til og med aksessere et område av forandringer i depotet ved å bruke både datoer og revisjoner sammen!
Her er eksempler på dataformater som Subversion aksepterer. Husk å bruke anførselstegn rundt datoer som inneholder mellomrom.
$ svn checkout --revision {2002-02-17}
$ svn checkout --revision {15:30}
$ svn checkout --revision {15:30:00.200000}
$ svn checkout --revision {"2002-02-17 15:30"}
$ svn checkout --revision {"2002-02-17 15:30 +0230"}
$ svn checkout --revision {2002-02-17T15:30}
$ svn checkout --revision {2002-02-17T15:30Z}
$ svn checkout --revision {2002-02-17T15:30-04:00}
$ svn checkout --revision {20020217T1530}
$ svn checkout --revision {20020217T1530Z}
$ svn checkout --revision {20020217T1530-0500}
…
Når du spesifiserer en dato som en revisjon, finner Subversion den nyeste revisjonen på denne datoen:
$ svn log --revision {2002-11-28}
------------------------------------------------------------------------
r12 | ira | 2002-11-27 12:31:51 -0600 (Wed, 27 Nov 2002) | 6 lines
…
Du kan også bruke et område av datoer. Subversion vil finne alle revisjoner mellom to datoer, inkludert:
$ svn log --revision {2002-11-20}:{2002-11-29}
…
Som vi har påpekt, kan du også blande datoer og revisjoner:
$ svn log --revision {2002-11-20}:4040
Brukere bør være klar over en spissfindighet som kan bli litt av en snublestein ved bruk av datoer i Subversion. Siden tidspunktet på en revisjon er lagret som en egenskap for revisjonen – en uversjonert, modifiserbar egenskap – kan revisjonsegenskaper bli forandret til å representere helt feil kronologi, eller kan til og med bli fjernet helt. Dette vil rote til den interne “dato-til-revisjon”-konverteringen som Subversion utfører.
Mesteparten av tiden vil du starte bruken av et Subversiondepot ved å utføre en uthenting av prosjektet ditt. Ved å hente ut en revisjon fra et depot lages det en kopi av den lokalt på maskinen. Denne kopien inneholder HEAD (siste revisjon) av Subversiondepotet som du spesifiserer på kommandolinja:
$ svn checkout http://svn.collab.net/repos/svn/trunk A trunk/subversion.dsw A trunk/svn_check.dsp A trunk/COMMITTERS A trunk/configure.in A trunk/IDEAS … Checked out revision 2499.
Selv om eksempelet ovenfor henter ut trunk-katalogen, kan du like lettvint hente ut en hvilken som helst dyp underkatalog fra et depot ved å spesifisere underkatalogen i adressen:
$ svn checkout http://svn.collab.net/repos/svn/trunk/doc/book/tools A tools/readme-dblite.html A tools/fo-stylesheet.xsl A tools/svnbook.el A tools/dtd A tools/dtd/dblite.dtd … Checked out revision 2499.
Siden Subversion bruker en “kopier-rediger-flett”-modell istedenfor “lås-rediger-lås opp” (se Kapittel 2, Grunnleggende konsepter), er du allerede i stand til å gjøre forandringer i arbeidskopien din. Den er som enhver annen samling av filer og kataloger på systemet. Du kan redigere og forandre dem, flytte dem rundt, du kan til og med slette hele arbeidskopien og ikke tenke mer på den.
Selv om arbeidskopien er “som enhver annen samling av filer og kataloger på systemet”, må du la Subversion få vite om du er på vei til å rearrangere noe i arbeidskopien. Hvis du vil kopiere eller flytte et element i en arbeidskopi, skal du bruke svn copy eller svn move istedenfor kopierings- og flyttekommandoene i operativsystemet. Vi vil snakke mer om dem senere i dette kapitlet.
Såfremt du ikke er klar til å legge inn en ny fil eller katalog, eller forandringer til eksisterende, er det ikke nødvendig å fortelle Subversiontjeneren at du har gjort noe.
Selv om du så absolutt kan hente ut en arbeidskopi med URLen til depotet som det eneste argumentet, kan du også spesifisere en katalog etter depot-URLen. Dette plasserer arbeidskopien din i den nye katalogen som du gir navn til. For eksempel:
$ svn checkout http://svn.collab.net/repos/svn/trunk subv A subv/subversion.dsw A subv/svn_check.dsp A subv/COMMITTERS A subv/configure.in A subv/IDEAS … Checked out revision 2499.
Dette vil plassere arbeidskopien i en katalog kalt subv istedenfor en katalog kalt trunk som vi gjorde tidligere.
Subversion har mange funksjoner, valg og avanserte muligheter, men på en dag-til-dag-basis er oddsene store for at du bare vil bruke et fåtall av dem. I denne seksjonen vil vi gå gjennom de vanligste tingene som du vil komme ut for med Subversion i løpet av en dags arbeid.
En typisk arbeidssyklus ser ut som dette:
Oppdater arbeidskopien din
svn update
Gjør forandringer
svn add
svn delete
svn copy
svn move
Se på forandringene dine
svn status
svn diff
svn revert
Flett andres forandringer inn i arbeidskopien din
svn update
svn resolved
Legg inn forandringene dine
svn commit
Når du arbeider på et prosjekt med et team, vil du oppdatere arbeidskopien din med alle forandringer gjort av andre utviklere siden den forrige oppdateringen av prosjektet. Bruk svn update for å få arbeidskopien i synk med den siste revisjonen i depotet.
$ svn update U foo.c U bar.c Updated to revision 2.
I dette tilfellet har noen andre lagt inn forandringer til både foo.c og bar.c siden forrige gang du oppdaterte, og Subversion har oppdatert arbeidskopien din til å inneholde disse forandringene.
La oss se på utdataene fra svn update litt til. Når tjeneren sender forandringer til arbeidskopien, blir en bokstavkode vist ved siden av hvert element for å la deg vite hva Subversion gjorde for å oppdatere arbeidskopien:
Filen foo ble oppdatert (Updated) og mottok forandringer fra tjeneren.
Filen eller katalogen foo ble lagt til (Added) i arbeidskopien din.
Filen eller katalogen foo ble slettet (Deleted) fra arbeidskopien.
Filen eller katalogen foo ble erstattet (Replaced) i arbeidskopien; det vil si, foo ble slettet og et nytt element med det samme navnet ble lagt til. Selv om de har det samme navnet, anser depotet dem for å være forskjellige objekter med hver sin historie.
Filen foo mottok nye forandringer fra depotet, mens den lokale kopien av filen inneholdt forandringer gjort av deg. Enten blandet ikke forandringene seg med hverandre, eller de var nøyaktig de samme som dine lokale forandringer, så Subversion klarte å flette (merGe) depotets forandringer uten noen problemer.
Filen foo mottok forandringer som førte til konflikt (Conflict) med dine egne. Forandringene fra tjeneren overlapper direkte med dine egne forandringer i filen. Men det er ingen grunn til panikk. Denne overlappingen må bli løst av et menneske (deg); vi diskuterer denne situasjonen senere i dette kapittelet.
Nå kan du gå i gang med arbeidet og gjøre forandringer i arbeidskopien. Det er vanligvis mer praktisk å bestemme seg for en spesiell forandring (eller et sett forandringer) som skal gjøres, som å lage en ny funksjonalitet, ordne en feil osv. Subversionkommandoene som du vil bruke her er svn add, svn delete, svn copy og svn move. Hvis du imidlertid bare redigerer filer som allerede finnes i Subversion, trenger du kanskje ikke å bruke noen av disse kommandoene før du legger dem inn. Forandringer som du kan gjøre med arbeidskopien:
Dette er den enkleste typen forandring. Du trenger ikke å fortelle Subversion at du har tenkt å forandre en fil; bare sett i gang med forandringene. Subversion vil bli i stand til å automatisk finne ut av hvilke filer som er blitt forandret.
Du kan spørre Subversion om å “merke” filer og kataloger for oppførte slettinger, tillegginger, kopieringer eller flytting. Selv om disse forandringene tar plass øyeblikkelig i arbeidskopien din, vil ingen tillegginger eller slettinger skje i depotet før du legger dem inn.
For å gjøre forandringer i filer, bruk teksteditoren din, tekstbehandlingsprogrammet, grafikkprogrammet, eller hvilket som helst verktøy du vanligvis bruker. Subversion behandler binærfiler like lett som tekstfiler – og like effektivt.
Her er en oversikt over de fire delkommandoene i Subversion som du vil bruke oftest for å gjøre forandringer i trestrukturen (vi vil dekke svn import og svn mkdir senere).
Selv om du kan redigere filene dine med hvilket verktøy du vil, bør du ikke forandre strukturen i arbeidskopien uten å la Subversion vite hva du gjør. Bruk kommandoene svn copy, svn delete og svn move for å forandre strukturen på arbeidskopien, og bruk svn add-kommandoen for å legge inn nye filer og kataloger under versjonskontroll.
Klargjør filen, katalogen eller den symbolske lenken foo for å bli lagt til i depotet. Når du legger inn filene neste gang, vil foo bli et barn av foreldrekatalogen. Legg merke til at hvis foo er en katalog, vil alt under foo også bli klargjort for tillegging. Hvis du bare vil klargjøre bare selve foo, legg til valget --non-recursive (-N).
Klargjør filen, katalogen eller den symbolske lenken foo for sletting fra depotet. Hvis foo er en fil eller lenke, blir den slettet øyeblikkelig fra arbeidskopien. Hvis foo er en katalog, blir den ikke slettet, men klargjort for sletting. Når du legger inn forandringene, vil foo bli slettet fra arbeidskopien og depotet.[2]
Opprett et nytt element bar som en duplikat av foo. bar er automatisk klargjort for tillegging. Når bar blir lagt til depotet under neste innlegging, blir kopieringshistorien lagret (som om den kommer originalt fra foo). svn copy lager ikke mellomliggende kataloger.
Denne kommandoen er nøyaktig den samme som å kjøre svn copy foo bar; svn delete foo. Det vil si, bar er klargjort for tillegging som en kopi av foo, og foo blir klargjort for fjerning. svn move lager ikke mellomliggende kataloger.
Når du er ferdig med å gjøre forandringer, må du legge dem inn i depotet, men før du gjør det, er det vanligvis en god idé å ta en kikk på nøyaktig hva du har forandret. Ved å studere forandringene dine før du legger dem inn, kan du lage en mer nøyaktig loggmelding. Du kan også oppdage om du har forandret en fil ved en ulykke, og dette gir deg en sjanse til å omgjøre disse forandringene før du legger dem inn. I tillegg er dette en god mulighet til å se over og skrote forandringer før du publiserer dem. Du kan se nøyaktig hvilke forandringer du har gjort ved å bruke svn status, svn diff og svn revert. Du vil vanligvis bruke de første to kommandoene til å finne ut hvilke filer som har forandret seg i arbeidskopien din, og deretter kanskje bruke den tredje for å omgjøre noen (eller alle) disse forandringene.
Subversion er blitt optimalisert for å hjelpe deg med denne oppgaven, og er i stand til å gjøre mange ting uten å kommunisere med depotet. Nærmere forklart, arbeidskopien inneholder en hemmelig “urørt” kopi av hver versjonskontrollert fil, og disse kopiene ligger i .svn-området. På grunn av dette kan Subversion raskt vise deg hvordan arbeidsfilene dine har forandret seg, og til og med tillate deg å omgjøre forandringene dine uten å kontakte depotet.
Du vil muligens bruke kommandoen svn status mer enn noen annen Subversionkommando.
Hvis du kjører svn status på toppen av arbeidskopien uten å angi argumenter, vil programmet detektere alle fil- og katalogforandringer som du har gjort. Nedenfor er eksempler på de forskjellige statuskodene som svn status kan returnere. (Legg merke til at teksten etter # i det følgende eksempelet ikke skrives ut av svn status-kommandoen.
L abc.c # svn har en lås i .svn-katalogen for abc.c
M bar.c # innholdet i bar.c har lokale forandringer
M baz.c # baz.c har egenskapsforandringer, men ingen
# forandring i innholdet
X 3rd_party # denne katalogen er del av en
# 'externals'-definering
? foo.o # svn kjenner ikke til foo.o
! some_dir # svn kontrollerer ikke denne, og er satt opp
# til å ignorere den
~ qux # versjonert som fil/katalog/lenke, men
# elementtypen er forandret
I .screenrc # denne filen er ignorert
A + moved_dir # lagt til med historien til der den kom fra
M + moved_dir/README # lagt til med historie og inneholder lokale
# forandringer
D stuff/fish.c # denne filen er klargjort for sletting
A stuff/loot/bloo.h # denne filen er klargjort for tillegging
C stuff/loot/lump.c # denne filen inneholder en konflikt fra en
R xyz.c # denne filen er klargjort for erstatning
# oppdatering
S stuff/squawk # denne filen eller katalogen er byttet til
# en annen plassering i depotet ved hjelp av
# svn switch-kommandoen
I dette utdataformatet skriver svn status fem kolonner med tegn, fulgt av flere blanke tegn, etterfulgt av et fil- eller katalognavn. Den første kolonnen forteller statusen til en fil eller katalog og/eller dens innhold. Kodene som kan skrives her er:
Filen, katalogen eller den symbolske lenken element er blitt klargjort for tillegging til depotet.
Filen element er i en tilstand av konflikt. Det betyr at forandringer mottatt fra tjeneren under en oppdatering overlapper med lokale forandringer som du har i arbeidskopien din. Du må løse denne konflikten før du legger inn dine forandringer i depotet.
Filen, katalogen eller den symbolske lenken element er blitt klargjort for sletting fra depotet.
Innholdet av filen element er blitt forandret.
Filen, katalogen, eller den symbolske lenken element er blitt klargjort for å erstatte element i depotet. Dette betyr at objektet først blir slettet, deretter blir et annet element lagt til, alt innenfor en enkelt revisjon.
Katalogen element er uversjonert, men er relatert til en ekstern definisjon i Subversion. For å finne ut mer om “externals”-defineringer, se “Externals Definitions”.
Filen, katalogen eller den symbolske lenken element er ikke under versjonskontroll. Du kan bli kvitt spørsmålstegnene ved å enten angi --quiet (-q)-valget til svn status, eller sette svn:ignore-egenskapen på foreldrekatalogen. For mer informasjon om ignorerte filer, se “svn:ignore”.
Filen, katalogen eller den symbolske lenken element er under versjonskontroll men mangler eller er ukomplett på en eller annen måte. Elementet kan mangle hvis det er fjernet ved bruk av en kommando utenfor Subversions kontroll. I tilfellet med en katalog, kan den være ukomplett hvis du har avbrutt en uthenting eller oppdatering. En rask svn update vil hente filen eller katalogen på nytt, eller en svn revert vil legge tilbake en manglende fil.
Filen, katalogen eller den symbolske lenken element er lagret i depotet som en type objekt, men det som faktisk er i arbeidskopien er en annen type. For eksempel kan Subversion ha en fil i katalogen, men du fjernet filen og opprettet en katalog på samme plassen uten å bruke kommandoen svn delete eller svn add.
Filen, katalogen eller den symbolske lenken element er ikke under versjonskontroll, og Subversion er satt opp til å ignorere den under operasjonene svn add, svn import og svn status. For mer informasjon om ignorerte filer, se “svn:ignore”. Merk at dette symbolet bare dukker opp hvis du spesifiserer valget --no-ignore til svn status – ellers ville filen bli ignorert og ikke listet i det hele tatt!
Den andre kolonnen forteller statusen på egenskapene til en fil eller katalog (se “Properties” for mer informasjon om egenskaper). Hvis en M viser seg i den andre kolonnen, er egenskapene blitt modifisert, ellers vil et blankt tegn bli skrevet.
Den tredje kolonnen vil bare vise blanktegn eller en L som betyr at Subversion har låst elementet i arbeidsområdet under .svn-katalogen. Du vil se en L hvis du kjører svn status i en katalog hvor en svn commit er i gang – kanskje mens du redigerer loggmeldingen. Hvis Subversion ikke kjører, ble Subversion sannsynligvis avbrutt og låsene må frigjøres ved å kjøre svn cleanup (mer om det senere i dette kapitlet).
Den fjerde kolonnen vil bare vise blanktegn eller en + som betyr at filen eller katalogen er klargjort for tillegging eller er modifisert med en historie lagt til i tillegg. Dette skjer vanligvis når du bruker svn move eller svn copy på en fil eller katalog. Hvis du ser A +, betyr det at elementet er klargjort for tillegging med historie. Det kan være en fil eller roten på en kopiert katalog. + betyr at elementet er en del av et katalogtre klargjort for tillegging med historie, det vil si at en forelder ble kopiert, og den følger bare med på lasset. M + betyr at elementet er en del av et katalogtre klargjort for tillegging med historie, og det har lokale forandringer. Når du legger det inn, vil først forelderen bli lagt til med historie (kopiert), noe som betyr at denne filen automatisk vil eksistere i kopien. Deretter blir de lokale forandringene lagt inn i kopien.
Den femte kolonnen vil bare vise blanktegn eller en S. Dette viser at filen eller katalogen er blitt byttet om fra stien som resten av arbeidskopien bruker (ved bruk av svn switch) til en gren.
Hvis du gir en spesifikk sti til svn status, gir den deg informasjon om dette elementet alene:
$ svn status stuff/fish.c D stuff/fish.c
svn status har også valget --verbose (-v), som vil vise deg statusen til hvert eneste element i arbeidskopien, selv om det ikke er blitt forandret:
$ svn status --verbose
M 44 23 sally README
44 30 sally INSTALL
M 44 20 harry bar.c
44 18 ira stuff
44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
44 21 sally stuff/things
A 0 ? ? stuff/things/bloo.h
44 36 harry stuff/things/gloo.c
Dette er den “lange formen” av utdata fra svn status. Den første kolonnen forblir den samme, men den andre kolonnen viser arbeidsrevisjonen til elementet. Den tredje og fjerde kolonnen viser revisjonen elementet sist ble forandret i, og hvem som gjorde det.
Ingen av kjøringene av svn status ovenfor kontakter depotet, de virker bare lokalt ved å sammenligne metadataene i .svn-katalogen med arbeidskopien. Til slutt er det --show-updates (-u)-valget, som kontakter depotet og legger til informasjon om ting som er utdatert:
$ svn status --show-updates --verbose
M * 44 23 sally README
M 44 20 harry bar.c
* 44 35 harry stuff/trout.c
D 44 19 ira stuff/fish.c
A 0 ? ? stuff/things/bloo.h
Status against revision: 46
Legg merke til de to asteriskene: Hvis du skulle kjøre svn update på dette tidspunktet, ville du motta forandringer til README og trout.c. Dette gir deg nyttig informasjon – du må oppdatere og få tjenerforandringene for README før du legger inn filene, ellers vil depotet avslå innleggingen fordi den ikke er oppdatert. (Mer om dette temaet senere.)
En annen måte å undersøke forandringene dine er med kommandoen svn diff. Du kan finne ut nøyaktig hvordan du har forandret på ting ved å kjøre svn diff uten argumenter, noe som lister ut filforandringer i unified diff-format:[3]
$ svn diff
Index: bar.c
===================================================================
--- bar.c (revision 3)
+++ bar.c (working copy)
@@ -1,7 +1,12 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <stdio.h>
int main(void) {
- printf("Sixty-four slices of American Cheese...\n");
+ printf("Sixty-five slices of American Cheese...\n");
return 0;
}
Index: README
===================================================================
--- README (revision 3)
+++ README (working copy)
@@ -193,3 +193,4 @@
+Note to self: pick up laundry.
Index: stuff/fish.c
===================================================================
--- stuff/fish.c (revision 1)
+++ stuff/fish.c (working copy)
-Welcome to the file known as 'fish'.
-Information on fish will be here soon.
Index: stuff/things/bloo.h
===================================================================
--- stuff/things/bloo.h (revision 8)
+++ stuff/things/bloo.h (working copy)
+Here is a new file to describe
+things about bloo.
Kommandoen svn diff produserer disse utdataene ved å sammenligne dine arbeidsfiler mot de lagrede “urørte” kopiene i .svn-området. Filer klargjort for tillegging blir vist som om all teksten er lagt til, og filer klargjort for sletting vises som om all teksten er slettet.
Utdataene blir vist i unified diff-format. Det vil si at slettede linjer vises med en - i begynnelsen av linjen, og linjer lagt til har en + i begynnelsen. svn diff skriver også filnavn og informasjon om filposisjon nyttig for programmet patch, så du kan generere “patcher” ved å omdirigere utdataene fra diff til en fil:
$ svn diff > patchfil
Du kan for eksempel sende denne patchfilen til en annen utvikler så han kan kontrollere eller teste den før den legges inn i depotet.
Tenk deg nå at du ser resultatet av diffen ovenfor, og oppdager at forandringene dine til README er en feil; kanskje du la denne teksten inn i feil fil ved en ulykke.
Dette er en perfekt mulighet til å bruke kommandoen svn revert.
$ svn revert README Reverted 'README'
Subversion reverserte filen til sin før-modifiserte tilstand ved å overskrive den med den lagrede “urørte” kopien fra .svn-området. Men legg også merke til at svn revert kan omgjøre alle klargjorte operasjoner – for eksempel kan du bestemme deg for at du likevel ikke vil legge til en ny fil:
$ svn status foo ? foo $ svn add foo A foo $ svn revert foo Reverted 'foo' $ svn status foo ? foo
svn revert ELEMENT har nøyaktig den samme effekten som om du sletter ELEMENT fra arbeidskopien din og deretter kjører svn update -r BASE ELEMENT. Imidlertid, hvis du reverserer en fil har svn revert en forskjell som er verdt å legge merke til – den trenger ikke å kommunisere med depotet for å legge tilbake filen din.
Eller kanskje du fjernet en fil fra versjonskontroll ved en ulykke:
$ svn status README
README
$ svn delete README
D README
$ svn revert README
Reverted 'README'
$ svn status README
README
Vi har allerede sett hvordan svn status -u kan forutsi konflikter. Tenk deg at du kjører svn update og noen interessante ting skjer:
$ svn update U INSTALL G README C bar.c Updated to revision 46.
Kodene U og G gir ingen grunn til å foreta seg noe; disse filene absorberte forandringene fra depotet på en grei måte. Filene merket med U inneholdt ingen lokale forandringer, men ble oppdatert (Updated) med forandringer fra depotet. G-en står for “merGed” (flettet), noe som betyr at filen hadde lokale forandringer til å begynne med, men forandringene som kom fra depotet overlappet ikke med de lokale forandringene.
Men C-en står for konflikt. Dette betyr at forandringene fra tjeneren overlappet med dine egne, og nå må du velge mellom dem manuelt.
Når en konflikt oppstår, skjer vanligvis tre ting som hjelper deg med å legge merke til og løse denne konflikten:
Subversion skriver en C under oppdateringen, og husker at filen er i en konflikttilstand.
Hvis Subversion anser filen å være av en flettbar type, plasserer den konfliktmerker – spesielle strenger med tekst som skiller “sidene” av konflikten – inn i filen for å vise de overlappende områdene visuelt. (Subversion bruker svn:mime-type-egenskapen for å bestemme om en fil er mulig å flette ved hjelp av kontekst- og linjebaserert fletting. Se “svn:mime-type” for å lære mer.)
For hver fil som har en konflikt plasserer Subversion opp til tre ekstra filer i arbeidskopien din:
Dette er din fil som den var da den eksisterte i arbeidskopien din før du oppdaterte arbeidskopien – det vil si uten konfliktmerker. Denne filen har dine seneste forandringer og ingenting annet. (Hvis Subversion avgjør at filen ikke er flettbar, blir ikke .mine-filen opprettet, siden den ville vært identisk med arbeidsfilen.)
Dette er den filen som var BASE-revisjonen før du oppdaterte arbeidskopien. Det vil si den filen som du hentet ut før du gjorde dine seneste redigeringer.
Dette er den filen som Subversionklienten nettopp mottok fra tjeneren når du oppdaterte arbeidskopien. Denne filen samsvarer med HEAD-revisjonen av depotet.
Her er GAMMELREV revisjonsnummeret til filen i ditt .svn-område og NYREV er revisjonsnummeret til HEAD i depotet.
For eksempel, Sally gjør forandringer til filen sandwich.txt i depotet. Harry har akkurat forandret filen i arbeidskopien sin og har lagt den inn. Sally oppdaterer sin arbeidskopi før hun legger den inn og får en konflikt:
$ svn update C sandwich.txt Updated to revision 2. $ ls -1 sandwich.txt sandwich.txt.mine sandwich.txt.r1 sandwich.txt.r2
På dette tidspunktet vil Subversion ikke tillate deg å legge inn filen sandwich.txt før de tre midlertidige filene er fjernet.
$ svn commit --message "Add a few more things" svn: Commit failed (details follow): svn: Aborting commit: '/home/sally/svn-work/sandwich.txt' remains in conflict
Hvis du får en konflikt, må du gjøre en av tre ting:
Flette teksten med konflikt “for hånd” (ved å studere og redigere konfliktmerkene i filen).
Kopiere en av de midlertidige filene over arbeidsfilen din.
Kjøre svn revert <filnavn> for å skrote alle dine lokale forandringer.
Når du har løst konflikten, må du la Subversion få vite dette ved å kjøre svn resolved. Dette fjerner de tre midlertidige filene og Subversion anser ikke lenger filen for å være i konflikt.[4]
$ svn resolved sandwich.txt Resolved conflicted state of 'sandwich.txt'
Å flette konflikter for hånd kan være ganske skremmende den første gangen du prøver det, men med litt øvelse kan det bli like lett som å ramle av en sykkel.
Her er et eksempel. På grunn av dårlig kommunikasjon redigerer både du og Sally, din kollega, filen sandwich.txt samtidig. Sally legger inn sine forandringer, og når du oppdaterer arbeidskopien din får du en konflikt og vi må redigere sandwich.txt for å løse konfliktene. Først, la oss ta en kikk på filen:
$ cat sandwich.txt Top piece of bread Mayonnaise Lettuce Tomato Provolone <<<<<<< .mine Salami Mortadella Prosciutto ======= Sauerkraut Grilled Chicken >>>>>>> .r2 Creole Mustard Bottom piece of bread
Strengene med “mindre enn”-tegn, likhetstegn og “større enn”-tegn er konfliktmerker, og er ikke del av de aktuelle dataene i konflikt. Du vil vanligvis forsikre deg om at de er fjernet fra filen før din neste innlegging. Teksten mellom de to første settene med merker er satt sammen av de forandringene du gjorde i konfliktområdet:
<<<<<<< .mine Salami Mortadella Prosciutto =======
Teksten mellom det andre og tredje settet av konfliktmerker er teksten fra Sallys innlegging:
======= Sauerkraut Grilled Chicken >>>>>>> .r2
Vanligvis vil du ikke ønske å bare slette konfliktmerkene og Sallys forandringer – hun kommer til å bli voldsomt forbauset når smørbrødet ankommer og det ikke er det hun ville ha. Så det er nå du tar opp telefonen eller går til andre enden av kontoret og forklarer til Sally at du får ikke sauerkraut fra en italiensk ¤deli.[5] Når dere er blitt enige om forandringene du vil legge inn, rediger filen din og fjern konfliktmerkene.
Top piece of bread Mayonnaise Lettuce Tomato Provolone Salami Mortadella Prosciutto Creole Mustard Bottom piece of bread
Nå kan du kjøre svn resolved, og du er klar til å legge inn forandringene dine:
$ svn resolved sandwich.txt $ svn commit -m "Go ahead and use my sandwich, discarding Sally's edits."
Husk at hvis du blir forvirret mens du redigerer filen med konflikt, kan du alltids konsultere de tre filene som Subversion lagde for deg i arbeidskopien din – inkludert filen din sånn som den var før du oppdaterte. Du kan til og med bruke et tredjeparts interaktivt fletteverktøy for å studere de tre filene.
Hvis du får en konflikt og bestemmer deg for å forkaste endringene dine, trenger du bare å kopiere en av de midlertidige filene laget av Subversion over filen i arbeidskopien din:
$ svn update C sandwich.txt Updated to revision 2. $ ls sandwich.* sandwich.txt sandwich.txt.mine sandwich.txt.r2 sandwich.txt.r1 $ cp sandwich.txt.r2 sandwich.txt $ svn resolved sandwich.txt
Hvis du får en konflikt og under undersøkelsen bestemmer deg for å forkaste endringene dine og starte på nytt, bare reverser forandringene dine:
$ svn revert sandwich.txt Reverted 'sandwich.txt' $ ls sandwich.* sandwich.txt
Legg merke til at når du reverserer en fil i konflikt, trenger du ikke å kjøre svn resolved.
Nå er du klar til å legge inn forandringene dine. Legg merke til at svn resolved, ulikt mesteparten av de andre kommandoene vi har holdt på med i dette kapittelet, trenger et argument. I alle tilfeller må du være forsiktig og bare kjøre svn resolved når du er sikker på at du har ordnet konflikten i filen – når de midlertidige filene er fjernet, vil Subversion la deg legge inn filen selv om den fortsatt inneholder konfliktmerker.
Endelig! Du er ferdig med forandringene dine, du har flettet inn alle forandringene fra tjeneren, og du er klar til å legge inn forandringene til depotet.
Kommandoen svn commit sender alle dine forandringer til depotet. Når du legger inn en forandring, må du skrive en loggmelding som beskriver forandringen. Loggmeldingen vil bli lagt ved den nye revisjonen som du lager. Hvis loggmeldingen er kort, vil du kanskje legge den inn fra kommandonlinjen ved å bruke --message (eller -m)-valget:
$ svn commit --message "Corrected number of cheese slices." Sending sandwich.txt Transmitting file data . Committed revision 3.
Hvis du derimot har laget loggmeldingen mens du arbeidet, vil du kanskje be Subversion om å hente meldingen fra en fil ved å angi filnavnet med --file-valget:
$ svn commit --file logmsg Sending sandwich.txt Transmitting file data . Committed revision 4.
Hvis du lar være å angi --message- eller --file-valget, vil Subversion starte favoritteditoren din automatisk (se seksjonen om editor-cmd i “Config”) så du kan skrive en loggmelding.
Hvis du skriver en loggmelding i tekstbehandleren og bestemmer deg for å avbryte innleggingen, kan du bare avbryte uten å lagre endringene. Hvis du allerede har lagret innleggingsmeldingen, kan du slette teksten og lagre på nytt.
$ svn commit Waiting for Emacs...Done Log message unchanged or not specified a)bort, c)ontinue, e)dit a $
Depotet vet ikke og bryr seg ikke om forandringene dine gir noen mening som helhet; det sjekker bare for å være sikker på at ingen andre har forandret noen av de samme filene som du gjorde når du ikke fulgte med. Hvis noen har gjort det, vil hele innleggingen feile med en melding om at en eller flere av filene dine er utdaterte:
$ svn commit --message "Add another rule" Sending rules.txt svn: Commit failed (details follow): svn: Out of date: 'rules.txt' in transaction 'g'
På dette punktet må du kjøre svn update, ta deg av eventuelle flettinger eller konflikter som oppstår og prøve en ny innlegging.
Dette dekker den grunnleggende arbeidssyklusen for å bruke Subversion. Det er mange andre funksjoner i Subversion som du kan bruke til å vedlikeholde depotet og arbeidskopien, men du kan klare deg ganske greit ved å bare bruke de kommandoene som vi har diskutert så langt i kapittelet.
Som vi nevnte tidligere, er depotet som en tidsmaskin. Det lagrer alle forandringene som er lagt inn, og tillater deg å utforske denne historien ved å studere tidligere versjoner av filer og kataloger så vel som metadataene som tilhører dem. Med en enkel Subversionkommando kan du hente ut depotet (eller hente tilbake en eksisterende arbeidskopi) nøyaktig som den var på en vilkårlig dato eller revisjon i fortiden. Men noen ganger vil du bare kikke inn i fortiden istedenfor å reise til fortiden.
Det er flere kommandoer som kan gi deg historiske data fra depotet:
Viser deg bred informasjon: Loggmeldinger sammen med revisjoner og hvilke stier som forandret seg i hver revisjon.
Viser deg de spesifikke detaljene om hvordan en fil forandret seg over tid.
Denne blir brukt til å hente en vilkårlig fil som den var da den eksisterte i en spesiell revisjon og vise den på skjermen.
List filene i katalogen for en vilkårlig revisjon.
For å finne informasjon om historien til en fil eller katalog, bruk kommandoen svn log. svn log vil gi deg en oversikt over hvem som gjorde forandringer i en fil eller katalog, i hvilken revisjon den forandret seg, tid og dato for denne revisjonen, og, hvis den ble skrevet, loggmeldingen som hører til innleggingen.
$ svn log ------------------------------------------------------------------------ r3 | sally | Mon, 15 Jul 2002 18:03:46 -0500 | 1 line Added include lines and corrected # of cheese slices. ------------------------------------------------------------------------ r2 | harry | Mon, 15 Jul 2002 17:47:57 -0500 | 1 line Added main() methods. ------------------------------------------------------------------------ r1 | sally | Mon, 15 Jul 2002 17:40:08 -0500 | 1 line Initial import ------------------------------------------------------------------------
Legg merke til at loggmeldinger listes ut i omvendt kronologisk rekkefølge som standard. Hvis du ønsker å se et annet område av revisjoner i en spesiell rekkefølge, eller bare en enkelt revisjon, bruk --revision (-r)-valget:
$ svn log --revision 5:19 # viser loggene 5 til 19 i kronologisk
# rekkefølge
$ svn log -r 19:5 # viser loggene 5 til 19 i omvendt
# rekkefølge
$ svn log -r 8 # viser loggen for revisjon 8
Du kan også undersøke logghistorien for en enkelt fil eller katalog. For eksempel:
$ svn log foo.c … $ svn log http://foo.com/svn/trunk/code/foo.c …
Dette vil vise loggmeldinger bare for de revisjonene der arbeidsfilen (eller URLen) forandret seg.
Hvis du vil ha enda mer informasjon om en fil eller katalog, tar svn log også et --verbose (-v)-valg. Fordi Subversion tillater deg å flytte og kopiere filer og kataloger, er det viktig å ha muligheten til å følge stiforandringer i filsystemet, så i “verbose”-modus vil svn log inkludere en liste over forandrede stier for en revision i listeformatet:
$ svn log -r 8 -v ------------------------------------------------------------------------ r8 | sally | 2002-07-14 08:15:29 -0500 | 1 line Changed paths: M /trunk/code/foo.c M /trunk/code/bar.h A /trunk/code/doc/README Frozzled the sub-space winch. ------------------------------------------------------------------------
Vi har allerede sett svn diff før – den viser filforskjeller i unified diff-format; den ble brukt til å vise de lokale forandringene i en arbeidskopi før de ble lagt inn i depotet.
Faktisk skal det vise seg at det er tre distinkte bruksmåter for svn diff:
Undersøke lokale forandringer
Sammenligne arbeidskopien din med depotet
Sammenligne depot med depot
Som vi har sett, vil det å utføre svn diff uten noen valg sammenligne arbeidsfilene dine med de lagrede “urørte” kopiene i .svn-området:
$ svn diff Index: rules.txt =================================================================== --- rules.txt (revision 3) +++ rules.txt (working copy) @@ -1,4 +1,5 @@ Be kind to others Freedom = Responsibility Everything in moderation -Chew with your mouth open +Chew with your mouth closed +Listen when others are speaking $
Hvis et enkelt nummer blir gitt til --revision (-r)-valget, vil arbeidskopien bli sammenlignet med den spesifiserte revisjonen i depotet.
$ svn diff --revision 3 rules.txt Index: rules.txt =================================================================== --- rules.txt (revision 3) +++ rules.txt (working copy) @@ -1,4 +1,5 @@ Be kind to others Freedom = Responsibility Everything in moderation -Chew with your mouth open +Chew with your mouth closed +Listen when others are speaking $
Hvis to revisjonsnumre, separert med kolon, blir gitt til --revision (-r), blir disse to revisjonene sammenlignet direkte.
$ svn diff --revision 2:3 rules.txt Index: rules.txt =================================================================== --- rules.txt (revision 2) +++ rules.txt (revision 3) @@ -1,4 +1,4 @@ Be kind to others -Freedom = Chocolate Ice Cream +Freedom = Responsibility Everything in moderation Chew with your mouth open $
Ikke bare kan du bruke svn diff til å sammenligne filer i arbeidskopien din mot depotet, men hvis du oppgir en URL kan du undersøke forskjellene mellom elementer i depotet uten å en gang ha en arbeidskopi. Dette er spesielt nyttig hvis du ønsker å inspisere forandringer i en fil når du ikke har en arbeidskopi på den lokale maskinen din:
$ svn diff --revision 4:5 http://svn.red-bean.com/repos/example/trunk/text/rules.txt … $
Hvis du vil undersøke en tidligere versjon av en fil og ikke nødvendigvis forskjellene mellom to filer, kan du bruke svn cat:
$ svn cat --revision 2 rules.txt Be kind to others Freedom = Chocolate Ice Cream Everything in moderation Chew with your mouth open $
Du kan også omdirigere utdataene direkte til en fil:
$ svn cat --revision 2 rules.txt > rules.txt.v2 $
Du lurer sikkert på hvorfor vi rett og slett ikke bare bruker svn update --revision for å oppdatere filen til den eldre revisjonen. Det er et par grunner til at vi kanskje foretrekker å bruke svn cat.
For det første kan det hende at du ønsker å se på forskjellene mellom to revisjoner av en fil ved å bruke et eksternt diffprogram (kanskje et grafisk et, eller kanskje filen din er i et format som gjør at unified diff-formatet ikke strekker til). I dette tilfellet må du hente en kopi av den gamle revisjonen, omdirigere den til en fil, og angi både den og filen i arbeidskopien til det eksterne diffprogrammet.
Noen ganger er det lettere å se på en eldre versjon av en fil i sin helhet i stedet for bare forskjellene mellom den og en annen revisjon.
svn list-kommandoen viser deg hvilke filer som er i en depotkatalog uten å hente filene til din lokale maskin:
$ svn list http://svn.collab.net/repos/svn README branches/ clients/ tags/ trunk/
Hvis du vil ha en mer detaljert liste, angi --verbose (-v)-flagget for å få en liste som dette:
$ svn list --verbose http://svn.collab.net/repos/svn 2755 harry 1331 Jul 28 02:07 README 2773 sally Jul 29 15:07 branches/ 2769 sally Jul 29 12:07 clients/ 2698 harry Jul 24 18:07 tags/ 2785 sally Jul 29 19:07 trunk/
Kolonnene viser deg revisjonen filen eller katalogen sist ble modifisert, brukeren som gjorde det, størrelsen hvis det er en fil, datoen den sist ble forandret, og elementets navn.
I tillegg til alle de ovennevnte kommandoene, kan du bruke svn update og svn checkout med --revision-valget for å ta en hel arbeidskopi “tilbake i tid”[6]:
$ svn checkout --revision 1729 # Henter ut en ny arbeidskopi fra r1729
…
$ svn update --revision 1729 # Oppdaterer en eksisterende arbeidskopi
# til r1729
…
Selv om de ikke blir så ofte brukt som kommandoene tidligere nevnt i dette kapitlet, vil du innimellom trenge disse kommandoene.
Når Subversion modifiserer arbeidskopien din (eller noe som helst i .svn), prøver programmet å gjøre det på en så trygg måte som mulig. Før arbeidskopien blir forandret, skriver Subversion det som skal utføres til en loggfil. Deretter kjører programmet kommandoene i loggfilen for å utføre forandringen det blir bedt om. Til slutt fjerner Subversion loggfilen. Arkitekturmessig er dette likt et journalfilsystem. Hvis en Subversionoperasjon blir avbrutt (hvis prosessen blir drept eller maskinen krasjer, for eksempel), blir loggfilen liggende på disken. Ved å utføre kommandoene i loggfilen en gang til kan Subversion fullføre den tidligere påbegynte operasjonen, og arbeidskopien din kan sette seg tilbake til en fullverdig tilstand.
Og dette er nøyaktig hva svn cleanup gjør: Den leter gjennom arbeidskopien og kjører alle etterlatte logger mens den fjerner låser under prosessen. Hvis Subversion noen ganger forteller deg at en del av arbeidskopien er “låst” (locked), er dette kommandoen du bør kjøre. I tillegg vil svn status vise en L ved siden av låste elementer:
$ svn status L somedir M somedir/foo.c $ svn cleanup $ svn status M somedir/foo.c
svn import-kommandoen er en rask måte å kopiere et uversjonert filtre inn i et depot og lage mellomliggende kataloger der det er nødvendig.
$ svnadmin create /usr/local/svn/nyttdepot $ svn import mitt_tre file:///usr/local/svn/nyttdepot/etellerannet/prosjekt Adding mitt_tre/foo.c Adding mitt_tre/bar.c Adding mitt_tre/subdir Adding mitt_tre/subdir/quux.h Committed revision 1.
Det forrige eksempelet kopierte innholdet av katalogen mitt_tre under katalogen etellerannet/prosjekt i depotet:
$ svn list file:///usr/local/svn/nyttdepot/etellerannet/prosjekt bar.c foo.c subdir/
Legg merke til at etter importen er fullført, blir det originale treet ikke konvertert til en arbeidskopi. For å starte arbeidet, trenger du fortsatt å kjøre svn checkout for å få en fersk arbeidskopi av treet.
Nå har vi dekket mesteparten av klientkommandoene i Subversion. Unntak som vi kan legge merke til er de som har med forgreninger og fletting å gjøre (se Kapittel 4, Forgrening og fletting) og egenskaper (se “Properties”). Det kan være du ønsker å bruke litt tid på å skumme gjennom Kapittel 9, Subversion Complete Reference for å få en idé om de mange forskjellige kommandoene Subversion har – og hvordan du kan bruke dem til å gjøre arbeidet ditt enklere.
[2] Selvfølgelig, ingenting blir noensinne totalt slettet fra depotet – bare fra HEAD i depotet. Du kan få alt tilbake ved å hente ut (eller oppdatere arbeidskopien til) en tidligere revisjon i forhold til den som ble slettet.
[3] Subversion bruker sine egne interne diffrutiner, som produserer diff-format som standard. Hvis du vil ha utdataene fra diff i et annet format, spesifiser et eksternt diffprogram ved å bruke --diff-cmd og legg til alle flagg du vil trenge ved å bruke --extensions-bryteren. For eksempel, for å se lokale forskjeller i filen foo.c i context format mens forandringer i blanktegn blir ignorert, kan du kjøre svn diff --diff-cmd /usr/bin/diff --extensions '-bc' foo.c.
[4] Du kan alltids fjerne de midlertidige filene selv, men gidder du egentlig det når Subversion kan gjøre det for deg? Det var det vi trodde.
[5] Og hvis du spør dem om det, kan det hende de bærer deg ut av byen på en planke.
[6] Ser du? Vi sa jo at Subversion er en tidsmaskin.
Innholdsfortegnelse
Forgrening (“branching”), merking (“tagging”) og fletting (“merging”) er konsepter felles for nesten alle versjonskontrollsystemer. Hvis du ikke er vant med disse idéene, gir vi en god introduksjon i dette kapittelet. Hvis du kjenner til dem, finner du det forhåpentligvis interessant å se hvordan Subversion har implementert disse idéene.
Forgreninger er en fundamental del av versjonskontroll. Hvis du skal tillate Subversion å behandle dine data, er dette en funksjon som du etterhvert kommer til å basere deg mye på. Dette kapittelet går ut i fra at du allerede er kjent med Subversions grunnleggende konsepter (Kapittel 2, Grunnleggende konsepter).
Tenk deg at det er din jobb å vedlikeholde et dokument for en avdeling i firmaet ditt, en håndbok av et eller annet slag. En dag spør en annen avdeling deg etter den samme håndboken, men med noen deler “spesialtilpasset” for dem, siden de gjør ting litt forskjellig.
Hva gjør du i denne situasjonen? Du gjør den opplagte tingen: Du lager en annen kopi av dokumentet og begynner å vedlikeholde de to kopiene separat. Etterhvert som hver avdeling ber deg om å gjøre små forandringer, legger du dem inn i den ene kopien eller den andre.
Du vil ofte ønske å gjøre den samme forandringen i begge kopiene. Hvis du for eksempel finner en skrivefeil i den første kopien, er det veldig sannsynlig at den samme trykkfeilen eksisterer i den andre kopien. De to dokumentene er nesten like når alt kommer til alt; forskjellene er små og spesifikke.
Dette er det grunnleggende konseptet for en forgrening – det vil si en utviklingslinje som eksisterer uavhengig av en annen linje, men som likevel deler en felles historie hvis du ser langt nok tilbake i tid. En forgrening begynner bestandig livet som en kopi av noe, og går videre derfra ved å lage sin egen historie (se Figur 4.1, “Forgreninger av utviklingen”).
Subversion har kommandoer for å hjelpe deg å vedlikeholde parallelle forgreninger av filene og katalogene dine. Programmet lar deg opprette forgreninger ved å kopiere data, og husker at kopiene er relaterte til hverandre. I tillegg får du også hjelp til å duplisere forandringer fra en gren til en annen. Til sist, Subversion kan la porsjoner av arbeidskopien reflektere forskjellige forgreninger, så du kan “blande og tilpasse” forskjellige utviklingslinjer i ditt daglige arbeid.
På dette punktet skal du ha fått forståelsen av hvordan hver innlegging oppretter et helt nytt filsystemtre (kalt en “revisjon”) i depotet. Hvis ikke, gå tilbake og les om revisjoner i “Revisjoner”.
I dette kapittelet skal vi gå tilbake til det samme eksempelet fra kapittel 2. Du husker at du og din kollega Sally deler et depot som inneholder to prosjekter – paint og calc. Merk imidlertid at i Figur 4.2, “Depotets utseende til å begynne med” inneholder hver prosjektkatalog underkataloger kalt trunk og branches. Grunnen til dette vil du snart få greie på.
Som tidligere, tenk deg at du og Sally begge har arbeidskopier av “calc”-prosjektet. Mer spesifikt, dere har begge en arbeidskopi av /calc/trunk. Alle filene for prosjektet er i denne underkatalogen istedenfor i selve /calc, fordi teamet ditt har bestemt at /calc/trunk er der “hovedlinjen” av utviklingen skal foregå.
La oss si at du har fått oppgaven å utføre en radikal reorganisering av prosjektet. Det vil ta lang tid å skrive, og vil påvirke alle filene i prosjektet. Problemet her er at du vil ikke forstyrre Sally, som er i full gang med å fikse småfeil her og der. Hun er avhengig av at den seneste versjonen av prosjektet (i /calc/trunk) alltid fungerer. Hvis du starter med å legge inn forandringene dine bit for bit, vil du ganske sikkert ødelegge ting for Sally.
En strategi er å krabbe inn i et hull; du og Sally kan stoppe med å dele informasjon for en uke eller to. Det vil si, starte med å omorganisere alle filene i arbeidskopien din, men ikke legge inn eller oppdatere før du er helt ferdig med oppgaven. Men det er en del problemer med denne metoden. For det første er det ikke særlig trygt. Folk flest liker å lagre arbeidet sitt til depotet med jevne mellomrom i tilfelle noe stygt skulle skje med arbeidskopien. For det andre er det ikke spesielt fleksibelt. Hvis du gjør arbeidet ditt på forskjellige datamaskiner (kanskje du har en arbeidskopi av /calc/trunk på to forskjellige maskiner) må du kopiere forandringene manuelt fram og tilbake, eller gjøre hele jobben på en enkelt maskin. På samme måte er det vanskelig å dele forandringene dine som er under utvikling med andre. Vanlig god praksis innen programutvikling er å la dine kolleger få se over arbeidet ditt mens du holder på. Hvis ingen ser innleggingene dine, går du glipp av potensiell respons. Til slutt, når du er ferdig med alle forandringene dine, kan du oppleve at det er veldig vanskelig å flette sammen det endelige resultatet ditt med resten av koden til firmaet. Sally (eller andre) kan ha gjort mange forandringer i depotet som er vanskelig å legge inn i arbeidskopien din – spesielt hvis du kjører svn update etter flere uker med isolasjon.
En bedre løsning er å opprette din egen forgrening, eller utviklingslinje, i depotet. Dette lar deg lagre det halvfungerende resultatet ditt med jevne mellomrom uten å blande det med arbeidet til andre, og samtidig kan du velge ut informasjon som du vil dele med dine kolleger. Du vil etter hvert få se nøyaktig hvordan dette fungerer.
Det å opprette en ny gren er veldig enkelt – du lager en kopi av prosjektet i depotet ved å bruke kommandoen svn copy. Subversion er ikke bare i stand til å kopiere enkle filer, men også hele kataloger. I dette tilfellet vil du lage en kopi av /calc/trunk-katalogen. Hvor skal den nye kopien være? Hvor du vil – det er et spørsmål om prosjektrutiner. La oss si at teamet ditt har som regel å opprette forgreninger i /calc/branches-området i depotet, og du vil kalle grenen din my-calc-branch. Det du vil er å lage en ny katalog, /calc/branches/my-calc-branch, som begynner livet som en kopi av /calc/trunk.
Det er to forskjellige måter å lage en kopi på. Vi vil demonstrere den rotete måten først, bare for å klargjøre konseptet. Til å begynne med, hent ut en arbeidskopi av prosjektets rotkatalog, /calc:
$ svn checkout http://svn.example.com/repos/calc bigwc A bigwc/trunk/ A bigwc/trunk/Makefile A bigwc/trunk/integer.c A bigwc/trunk/button.c A bigwc/branches/ Checked out revision 340.
For å lage en kopi er det nå bare å angi to arbeidskopistier til kommandoen svn copy:
$ cd bigwc $ svn copy trunk branches/my-calc-branch $ svn status A + branches/my-calc-branch
I dette tilfellet kopierer svn copy katalogen trunk rekursivt til en ny arbeidskatalog, branches/my-calc-branch. Som du kan se av kommandoen svn status er den nye katalogen nå klargjort for å legges til i depotet. Men legg også merke til “+”-tegnet ved siden av bokstaven A. Dette indikerer at den klargjorte tilleggingen er en kopi av noe, og ikke noe nytt. Når du legger inn forandringene dine, vil Subversion lage /calc/branches/my-calc-branch i depotet ved å kopiere /calc/trunk istedenfor å sende hele arbeidskopien over nettverket en gang til:
$ svn commit -m "Creating a private branch of /calc/trunk." Adding branches/my-calc-branch Committed revision 341.
Og nå den lettere måten å lage en gren på, som vi skulle fortalt deg om til å begynne med: svn copy kan operere direkte mot to URLer.
$ svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Lager en privat gren av /calc/trunk."
Committed revision 341.
Det er egentlig ingen forskjell på disse to metodene. Begge prosedyrene lager en ny katalog i revisjon 341, og den nye katalogen er en kopi av /calc/trunk. Dette er vist i Figur 4.3, “Depot med ny kopi”. Legg merke til at den andre metoden utfører en øyeblikkelig innlegging.[7] Det er en lettere prosedyre, fordi det ikke kreves at du må hente ut et stort speil av depotet. Faktisk trenger du med denne teknikken ikke en arbeidskopi i det hele tatt.
Nå som du har laget en gren av prosjektet, kan du hente ut en ny arbeidskopi for å starte bruken av den:
$ svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch A my-calc-branch/Makefile A my-calc-branch/integer.c A my-calc-branch/button.c Checked out revision 341.
Det er ingenting spesielt med denne arbeidskopien; den avspeiler simpelthen bare en annen katalog i depotet. Men når du legger inn forandringer, vil ikke Sally se noen av dem når hun oppdaterer. Hennes arbeidskopi er fra /calc/trunk. (Pass på å lese “Bytte om en arbeidskopi” senere i dette kapittelet: Kommandoen svn switch er en alternativ måte å lage en arbeidskopi av en forgrening.)
La oss late som om en uke går, og de følgende innlegginger blir gjort:
Du gjør en forandring i /calc/branches/my-calc-branch/button.c som lager revisjon 342.
Du gjør en forandring i /calc/branches/my-calc-branch/integer.c som lager revisjon 343.
Sally gjør en forandring i /calc/trunk/integer.c som lager revisjon 344.
Det er nå to uavhengige utviklingslinjer, vist i Figur 4.4, “Forgreningen av en fils historie”, som skjer med integer.c.
Ting blir interessante når du ser på historien til forandringene gjort i din kopi av integer.c:
$ pwd /home/user/my-calc-branch $ svn log --verbose integer.c ------------------------------------------------------------------------ r343 | user | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines Changed paths: M /calc/branches/my-calc-branch/integer.c * integer.c: frozzled the wazjub. ------------------------------------------------------------------------ r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines Changed paths: A /calc/branches/my-calc-branch (from /calc/trunk:340) Creating a private branch of /calc/trunk. ------------------------------------------------------------------------ r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines Changed paths: M /calc/trunk/integer.c * integer.c: changed a docstring. ------------------------------------------------------------------------ r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines Changed paths: M /calc/trunk/integer.c * integer.c: adding this file to the project. ------------------------------------------------------------------------
Legg merke til at Subversion går gjennom historien av forgreningens integer.c hele veien tilbake gjennom tiden og krysser til og med punktet den ble kopiert. Opprettelsen av forgreningen vises som en hendelse i historien, fordi integer.c også ble kopiert når alt under /calc/trunk/ ble kopiert. Se nå hva som skjer når Sally kjører den samme kommandoen på hennes kopi av filen:
$ pwd /home/sally/calc $ svn log --verbose integer.c ------------------------------------------------------------------------ r344 | sally | 2002-11-07 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines Changed paths: M /calc/trunk/integer.c * integer.c: fix a bunch of spelling errors. ------------------------------------------------------------------------ r303 | sally | 2002-10-29 21:14:35 -0600 (Tue, 29 Oct 2002) | 2 lines Changed paths: M /calc/trunk/integer.c * integer.c: changed a docstring. ------------------------------------------------------------------------ r98 | sally | 2002-02-22 15:35:29 -0600 (Fri, 22 Feb 2002) | 2 lines Changed paths: M /calc/trunk/integer.c * integer.c: adding this file to the project. ------------------------------------------------------------------------
Sally ser at hennes egen revisjon 344 forandrer seg, men ikke forandringen som du gjorde i revisjon 343. Hva Subversion angår, påvirket disse to innleggingene forskjellige filer på forskjellige plasseringer i depotet. Subversion viser imidlertid at de to filene deler en felles historie. Før grenkopieringen ble gjort i revisjon 341 var de den samme filen. Det er derfor både du og Sally ser forandringene gjort i revisjonene 303 og 98.
Det er to viktige ting du bør huske fra denne seksjonen.
Ulikt mange andre versjonskontrollsystemer eksisterer Subversions forgreninger som vanlige filsystemkataloger i depotet, ikke i en ekstra dimensjon. Disse katalogene inneholder bare noe ekstra historisk informasjon.
Subversion har ikke noe internt begrep om en gren – bare kopier. Når du kopierer en katalog, er den resulterende nye katalogen bare en “gren” fordi du legger denne meningen til den. Du kan tenke på denne katalogen på en spesiell måte eller behandle den forskjellig, men for Subversion er den bare en vanlig katalog som tilfeldigvis er blitt opprettet ved kopiering.
Nå arbeider du og Sally på parallelle grener i prosjektet: Du arbeider på en privat gren, og Sally jobber i trunk, eller hovedlinjen av utviklingen.
For prosjekter som har et stort antall bidragsytere er det vanlig for de fleste personer å ha arbeidskopier av trunk. Når noen må gjøre forandringer som vil ta litt tid og som sannsynligvis kommer til å forstyrre trunk, er standard prosedyre å lage en privat gren og legge inn forandringer der til alt arbeidet er fullført.
Så, de gode nyhetene er at du og Sally ikke forstyrrer hverandre. De dårlige nyhetene er at det er veldig lett å drive for langt avgårde. Husk at ett av problemene med “krabbe inn i et hull”-strategien er at når du er ferdig med grenen din, vil det bli nesten umulig å flette inn dine forandringer tilbake til trunk uten et stort antall konflikter.
Istedenfor kan du og Sally fortsette med å dele forandringer mens du arbeider. Det er opp til deg å bestemme hvilke forandringer som er verdt å dele; Subversion gir deg muligheten til å selektivt “kopiere” forandringer mellom grener. Og når du er fullstendig ferdig med din gren, kan hele settet med grenforandringer bli kopiert tilbake til trunk.
I den forrige seksjonen nevnte vi at både du og Sally gjorde forandringer til integer.c på forskjellige forgreninger. Hvis du ser på Sallys loggmelding for revisjon 344, kan du se at hun forandret noen stavefeil. Din kopi av den samme filen har uten tvil de samme skrivefeilene. Det er sannsynlig at dine fremtidige forandringer i denne filen vil påvirke de samme områdene som skrivefeilene ligger i, så du ligger an til å få potensielle konflikter når du en vakker dag fletter inn grenen din. Da er det bedre å motta Sallys forandringer nå, før du starter med å arbeide mye i det samme området.
Det er på tide å bruke kommandoen svn merge. Det skal vise seg at denne kommandoen er en veldig nær slektning av svn diff-kommandoen (som du leste om i kapittel 3). Begge kommandoene er i stand til å sammenligne to vilkårlige objekter i depotet og beskrive forskjellene. For eksempel kan du spørre svn diff om å vise deg den eksakte forandringen gjort av Sally i revisjon 344:
$ svn diff -r 343:344 http://svn.example.com/repos/calc/trunk
Index: integer.c
===================================================================
--- integer.c (revision 343)
+++ integer.c (revision 344)
@@ -147,7 +147,7 @@
case 6: sprintf(info->operating_system, "HPFS (OS/2 or NT)"); break;
case 7: sprintf(info->operating_system, "Macintosh"); break;
case 8: sprintf(info->operating_system, "Z-System"); break;
- case 9: sprintf(info->operating_system, "CPM"); break;
+ case 9: sprintf(info->operating_system, "CP/M"); break;
case 10: sprintf(info->operating_system, "TOPS-20"); break;
case 11: sprintf(info->operating_system, "NTFS (Windows NT)"); break;
case 12: sprintf(info->operating_system, "QDOS"); break;
@@ -164,7 +164,7 @@
low = (unsigned short) read_byte(gzfile); /* read LSB */
high = (unsigned short) read_byte(gzfile); /* read MSB */
high = high << 8; /* interpret MSB correctly */
- total = low + high; /* add them togethe for correct total */
+ total = low + high; /* add them together for correct total */
info->extra_header = (unsigned char *) my_malloc(total);
fread(info->extra_header, total, 1, gzfile);
@@ -241,7 +241,7 @@
Store the offset with ftell() ! */
if ((info->data_offset = ftell(gzfile))== -1) {
- printf("error: ftell() retturned -1.\n");
+ printf("error: ftell() returned -1.\n");
exit(1);
}
@@ -249,7 +249,7 @@
printf("I believe start of compressed data is %u\n", info->data_offset);
#endif
- /* Set postion eight bytes from the end of the file. */
+ /* Set position eight bytes from the end of the file. */
if (fseek(gzfile, -8, SEEK_END)) {
printf("error: fseek() returned non-zero\n");
Kommandoen svn merge gjør omtrent nøyaktig det samme. Istedenfor å skrive forskjellene til terminalen din, blir de lagt direkte til arbeidskopien din som lokale modifikasjoner:
$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk U integer.c $ svn status M integer.c
Utdataene fra svn merge viser at din kopi av integer.c ble patchet. Nå inneholder den Sallys forandring – forandringen er blitt “kopiert” fra trunk til din arbeidskopi på din private gren, og eksisterer nå som en lokal modifisering. På dette punktet er det opp til deg å se over den lokale modifiseringen og forsikre deg om at den fungerer korrekt.
I et annet scenario er det mulig at ting ikke gikk så bra og at integer.c gikk inn i en konflikttilstand. Du må kanskje løse konflikten ved hjelp av standard prosedyrer (se kapittel 3), eller hvis du finner ut at flettingen egentlig var en dårlig idé, kan du rett og slett gi opp og kjøre svn revert på den lokale forandringen.
Men hvis vi går ut i fra at du har sett over forandringen, kan du bruke svn commit til å legge inn forandringen på vanlig måte. På dette tidpunktet er forandringen blitt flettet inn i din depotgren. I versjonskontrollterminologi blir denne måten å kopiere forandringer mellom forgreninger på engelsk kalt porting. Det finnes ikke en standardisert betegnelse for dette på norsk, så vi bruker i denne boken uttrykket “flette”.
Når du legger inn de lokale modifiseringene, bør du forsikre deg om at loggmeldingen din nevner at du fletter en forandring fra en gren til en annen. For eksempel:
$ svn commit -m "integer.c: Flettet r344 (retting av skrivefeil) fra trunk." Sending integer.c Transmitting file data . Committed revision 360.
Som du vil se i de neste seksjonene, er dette en veldig viktig “god praksis” å følge.
En liten advarsel: Selv om svn diff og svn merge er veldig like i konsept, har de i mange tilfeller forskjellig syntaks. Vær sikker på at du får lest om dem i kapittel 9 for detaljer, eller spør svn help. For eksempel krever svn merge en arbeidskopi som et mål, det vil si en plass hvor den skal legge inn treforandringene. Hvis målet ikke er spesifisert, går den ut i fra at du prøver å utføre en av de følgende operasjonene:
Du vil flette katalogforandringer inn i den gjeldende arbeidskatalogen.
Du vil flette forandringene i en spesifikk fil inn i en fil med det samme navnet som eksisterer i den gjeldende arbeidskatalogen.
Hvis du fletter en katalog og ikke har spesifisert en målsti, går svn merge ut i fra det første tilfellet og prøver å legge inn forandringene til den gjeldende katalogen. Hvis du fletter en fil, og denne filen (eller en fil med det samme navnet) eksisterer i den gjeldende katalogen, går svn merge ut i fra det andre tilfellet og forsøker å legge inn forandringene til en lokal fil med det samme navnet.
Hvis du vil legge inn forandringer en annen plass, må du si fra om dette. Hvis du for eksempel sitter i foreldrekatalogen til arbeidskopien din, må du spesifisere målkatalogen som skal motta forandringene:
$ svn merge -r 343:344 http://svn.example.com/repos/calc/trunk my-calc-branch U my-calc-branch/integer.c
Du har nå sett et eksempel på svn merge-kommandoen, og du skal få se flere. Hvis du er forvirret omkring hvordan fletting faktisk virker, er du ikke alene om det. Mange brukere (spesielt de som er nye innen versjonskontroll) er usikker på den riktige syntaksen til kommandoen, og når denne funksjonaliteten skal brukes. Men frykt ikke, denne kommandoen er faktisk mye enklere enn du tror! Det er en veldig enkel teknikk for å forstå nøyaktig hvordan svn merge virker.
Hovedkilden til forvirringen er navnet på kommandoen. Terminologien merge – “flette” – indikerer på en måte at grener blir kombinert sammen, eller at det er en form for mystisk sammenblanding av data som foregår. Det er ikke tilfellet. Et bedre navn for kommandoen hadde vært svn diff-and-apply – “finn forskjell og legg denne til” – fordi det er alt som skjer: To depottrær blir sammenlignet, og forskjellene blir lagt inn i arbeidskopien.
Kommandoen tar tre argumenter:
Et innledende depottre (ofte kalt den venstre siden av sammenligningen),
Et slutt-depottre (ofte kalt den høyre siden av sammenligningen),
En arbeidskopi som skal motta forandringene som lokale forandringer (ofte kalt målet til flettingen).
Når disse tre argumentene er spesifisert, blir de to trærne sammenlignet og de forskjellene som programmet finner blir lagt inn i mål-arbeidskopien som lokale forandringer. Når kommandoen er ferdig, er ikke resultatet forskjellig fra om du hadde redigert filene for hånd, eller selv kjørt diverse svn add eller svn delete-kommandoer. Hvis du ikke liker resultatene, kan du enkelt kjøre svn revert for å omgjøre alle forandringene.
Syntaksen til svn merge lar deg spesifisere de tre nødvendige argumentene ganske fleksibelt. Her er noen eksempler:
$ svn merge http://svn.example.com/repos/branch1@150 \
http://svn.example.com/repos/branch2@212 \
min-arbeidskopi
$ svn merge -r 100:200 http://svn.example.com/repos/trunk min-arbeidskopi
$ svn merge -r 100:200 http://svn.example.com/repos/trunk
Den første syntaksen legger spesifikt opp alle tre argumentene, ved å nevne hvert tre på formen URL@REV og nevne arbeidskopimålet. Den andre syntaksen kan bli brukt som en snarvei for situasjoner når du sammenligner to forskjellige revisjoner på den samme URLen. Den siste syntaksen viser hvordan arbeidskopiargumentet er valgfritt; hvis det er utelatt, brukes den gjeldende katalogen.
Fletting av forandringer høres enkelt ut, men i praksis kan det bli en hodepine. Problemet er at hvis du gjentatte ganger fletter forandringer fra en gren til en annen, kan du ved en ulykke flette den samme forandringen to ganger. Når dette skjer, vil det noen ganger gå bra. Når en fil blir patchet vil Subversion vanligvis oppdage at filen inneholder forandringen, og gjør ingenting. Men hvis den allerede eksisterende forandringen er blitt forandret på en eller annen måte, vil du få en konflikt.
Ideelt sett bør versjonskontrollsystemet forhindre forsøket på å legge inn doble forandringer til en gren. Det bør huske automatisk hvilke forandringer en gren allerede har mottatt, og bør være i stand til å liste dem ut for deg. Det bør bruke denne informasjonen til å hjelpe til med å automatisere flettinger så mye som mulig.
Dessverre er ikke Subversion et sånt system. I likhet med CVS lagrer ikke Subversion 1.0 noen informasjon om fletteoperasjoner. Når du legger inn lokale forandringer, har ikke depotet noen idé om hvorvidt disse forandringene kom fra en kjøring av svn merge, eller fra en redigering av filene for hånd.
Hva betyr dette for deg, brukeren? Det betyr at inntil den dagen Subversion får denne funksjonen, må du selv holde rede på fletteinformasjonen. Den beste plassen å gjøre dette er i selve loggmeldingen. Som demonstrert i det tidligere eksempelet, anbefales det at loggmeldingen din nevner et spesifikt revisjonsnummer (eller område av revisjoner) som blir flettet inn i grenen. Senere kan du kjøre svn log for å se over hvilke forandringer forgreningen allerede inneholder. Dette vil la deg varsomt konstruere en etterfølgende svn merge-kommando som ikke vil bli overflødig i forhold til tidligere flettede forandringer.
I den neste seksjonen vil vi vise noen eksempler på denne teknikken i praksis.
Fordi fletting bare resulterer i lokale modifikasjoner, er det vanligvis ikke noen høyrisikooperasjon. Hvis flettingen går galt første gangen, kan du kjøre svn revert på forandringene og prøve igjen.
Men det er derimot mulig at arbeidskopien din allerede inneholder lokale forandringer. Forandringene lagt inn av en fletting vil bli blandet med de du har fra før, og det å kjøre svn revert er ikke lenger et alternativ. De to settene med forandringer kan bli umulig å separere.
I tilfeller som dette vil det være greit å kunne forutsi eller undersøke forandringer før de skjer. En enkel måte å gjøre det på er å kjøre svn diff med de samme argumentene som du planlegger å gi til svn merge, som vi allerede har vist i det første eksemplet vårt med fletting. En annen metode for forhåndsvisning er å angi --dry-run-valget til flettekommandoen:
$ svn merge --dry-run -r 343:344 http://svn.example.com/repos/calc/trunk U integer.c $ svn status # ingenting blir skrevet ut, arbeidskopien er fortsatt uforandret.
--dry-run-valget gjør egentlig ingen forandringer i arbeidskopien. Det viser bare statuskoder som ville blitt skrevet ut under en virkelig fletting. Det er nyttig for å få en “høynivå”-oversikt over den potensielle flettingen for de gangene der kjøring av svn diff gir alt for mange detaljer.
Akkurat som svn update-kommandoen, legger svn merge inn forandringer i arbeidskopien din. Og derfor er den også i stand til å lage konflikter. Konfliktene produsert av svn merge er imidlertid noen ganger forskjellige, og denne seksjonen forklarer disse forskjellene.
Til å begynne med, tenk deg at arbeidskopien din ikke inneholder noen lokale redigeringer. Når du svn update-er til en spesiell revisjon, vil forandringene sendt fra tjeneren alltid bli lagt inn på en “renslig” måte i arbeidskopien. Tjeneren produsererer deltaet ved å sammenligne to trær: Et virtuelt øyeblikksbilde av arbeidskopien din, og revisjonstreet du er er interessert i. Fordi den venstre siden av sammenligningen er nøyaktig lik det du allerede har, er deltaet garantert å korrekt konvertere arbeidskopien din til treet som er på høyre side.
Men svn merge har ingen slike garantier og kan være mye mer kaotisk: Brukeren kan be tjeneren om å sammenligne alle mulige trær, til og med trær som ikke er relatert til arbeidskopien! Dette betyr at det er et stort potensiale for menneskelige feil. Brukere vil noen ganger sammenligne to gale trær, og dermed lage et delta som ikke kan legges inn på en ren måte. svn merge vil gjøre sitt beste for å legge inn så mye av deltaet som mulig, men noen deler kan være umulige. Akkurat som patch-kommandoen i Unix noen ganger klager over “failed hunks”, vil svn merge klage over “skipped targets”:
$ svn merge -r 1288:1351 http://svn.example.com/repos/branch U foo.c U bar.c Skipped missing target: 'baz.c' U glub.c C glorb.h $
I det forrige eksempelet kan tilfellet være at baz.c eksisterer både i øyeblikksbildet av grenen som sammenlignes, og det resulterende deltaet vil forandre filens innhold, men filen eksisterer ikke i arbeidskopien. Hva som enn er tilfelle, betyr “skipped”-meldingen at brukeren mest sannsynlig sammenligner to gale trær; de er det klassiske tegnet på en feil gjort av brukeren. Når dette skjer er det lett å rekursivt omgjøre alle forandringene gjort under flettingen (svn revert --recursive), slette eventuelle uversjonerte filer eller kataloger som ligger igjen etter tilbakestillingen, og kjøre svn merge med forskjellige argumenter.
Legg også merke til at det forrige eksempelet viser en konflikt som skjer i glorb.h. Vi har allerede fastslått at arbeidskopien ikke har noen lokale forandringer; hvordan er det da mulig at en lokal konflikt kan oppstå? Igjen, fordi brukeren kan bruke svn merge for å definere og legge til enhver gammel delta til arbeidskopien, kan denne deltaen inneholde tekstmessige forandringer som ikke kan legges helt uproblematisk inn i en arbeidsfil, selv om denne filen ikke har noen lokale forandringer.
En annen liten forskjell mellom svn update og svn merge er navnene på fulltekst-filene som blir opprettet når en konflikt oppstår. I “Løse konflikter (Flette inn andres forandringer)” så vi at en oppdatering produserer filene filnavn.mine, filnavn.rGAMMELREV og filnavn.rNYREV. Men når svn merge produserer en konflikt, oppretter den tre filer kalt filnavn.working, filnavn.left og filename.right. I dette tilfellet beskriver terminologien “left” og “right” hvilken side filen kom fra. I alle fall, disse forskjellige navnene vil hjelpe deg å skille mellom filer som er opprettet som følge av en oppdatering versus filer som er opprettet som resultat av en fletting.
Når du snakker med en Subversionutvikler kan det hende du hører referanser til begrepet slektskap – ancestry. Dette ordet blir brukt til å beskrive forholdet mellom to objekter i et depot: Hvis de er relaterte til hverandre, vil det ene objektet være en stamfar til det andre.
For eksempel, tenk deg at du legger inn revisjon 100, som inkluderer en forandring i en fil kalt foo.c. Da er foo.c@99 en “stamfar” til foo.c@100. På den annen side, tenk at du legger inn en sletting av foo.c i revisjon 101, og deretter legger til en ny fil med det samme navnet i revisjon 102. I dette tilfellet kan det se ut som om foo.c@99 og foo.c@102 er relaterte (de har den samme filstien), men de er faktisk totalt forskjellige objekter i depotet. De deler ingen historie eller “slektskap”.
Grunnen til at vi tar dette opp er for å fremheve en viktig forskjell mellom svn diff og svn merge. Den første kommandoen ignorerer slektskap, mens den sistnevnte kommandoen er ganske følsom for det. Hvis du for eksempel ber svn diff om å sammenligne revisjon 99 og 102 av foo.c, vil du se linjebaserte forskjeller; diffkommandoen sammenligner blindt to stier. Men hvis du ber svn merge om å sammenligne de samme to objektene, vil den oppdage at de er urelaterte og forsøker først å slette den gamle filen og deretter legge til den nye filen; du vil se en D foo.c fulgt av en A foo.c.
De fleste flettinger involverer sammenligning av trær som er slektsmessig relatert til hverandre, og derfor har svn merge denne oppførselen som standard. Men nå og da vil du kanskje bruke flettekommandoen til å sammenligne to urelaterte trær. For eksempel har du kanskje to kildekodetrær som representerer forskjellige utgivelser av et programprosjekt (se “Vendor branches”). Hvis du ber svn merge om å sammenligne de to trærne, vil du se at hele det første treet blir slettet, fulgt av en tillegging av hele det andre treet!
I disse situasjonene vil du at svn merge bare gjør en stibasert sammenligning og ignorerer enhver relasjon mellom filer og kataloger. Legg til valget --ignore-ancestry til flettekommandoen, og den vil oppføre seg akkurat som svn diff. (Og på motsatt måte vil --notice-ancestry-valget få svn diff til å oppføre seg som merge-kommandoen.
Det er mange forskjellige bruksområder for forgreninger og svn merge, og denne seksjonen beskriver de vanligste som du sannsynligvis vil komme over.
For å fullføre eksempelet vårt vil vi nå reise fram i tiden. Tenk deg at flere dager har gått, og mange forandringer har skjedd både i trunk og på den private grenen din. Tenk deg så at du er ferdig med arbeidet på den private grenen; funksjonaliteten eller feilrettingen er endelig fullført, og nå vil du flette alle forandringene fra grenen din til trunk så andre kan få glede av dem.
Så hvordan bruker vi svn merge i dette tilfellet? Husk at denne kommandoen sammenligner to trær og legger forandringene inn i en arbeidskopi. For å motta forandringene må du derfor ha en arbeidskopi av trunk. Vi går ut i fra at du enten har den originale liggende (helt oppdatert), eller at du nylig hentet ut en fersk arbeidskopi av /calc/trunk.
Men hvilke to trær skal sammenlignes? Ved første øyekast ser det innlysende ut: Bare sammenlign seneste treet fra trunk med det seneste treet fra forgreningen. Men pass på – denne antakelsen er feil, noe som mange nye brukere har brent seg på. Siden svn merge opererer på samme måte som svn diff, vil en sammenligning mellom trærne i nyeste trunk og gren ikke bare vise forandringene som du har gjort på grenen. En slik sammenligning viser alt for mange forandringer: Den vil ikke bare vise tilleggingene av dine forandringer på grenen, men også fjerningen av forandringer i trunk som aldri skjedde på din gren.
For å bare vise forandringene som skjedde på din gren, må du sammenligne tilstanden ved starten av grenen din i forhold til dens endelige tilstand. Ved å bruke svn log på grenen kan du se at den ble opprettet i revisjon 341. Og den endelige tilstanden får du ved å bruke HEAD-revisjonen. Dette betyr at du vil sammenligne revisjonene 341 og HEAD i forgreningskatalogen og legge disse forskjellene inn i en arbeidskopi av trunk.
En fin måte å finne revisjonen en gren ble opprettet i (“basen” av forgreningen) er å bruke valget --stop-on-copy til svn log. Delkommandoen svn log vil normalt vise hver eneste forandring gjort på grenen, inkludert å gå forbi kopieringen som opprettet grenen. Så vanligvis vil du også se historien fra trunk. --stop-on-copy-valget vil stoppe loggutlistingen med en gang svn log finner ut at målet ble kopiert eller skiftet navn.
Så hvis vi går videre i eksempelet,
$ svn log --verbose --stop-on-copy \
http://svn.example.com/repos/calc/branches/my-calc-branch
…
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
A /calc/branches/my-calc-branch (from /calc/trunk:340)
$
Som forventet er den siste revisjonen skrevet ut av denne kommandoen den revisjonen der my-calc-branch ble opprettet ved kopiering.
Her er den siste fletteprosedyren, og deretter:
$ cd calc/trunk $ svn update At revision 405. $ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile $ svn status M integer.c M button.c M Makefile # … Undersøk forskjellene, kompiler, test osv … $ svn commit -m "Flettet forandringer mellom r341:405 fra my-calc-branch til trunk." Sending integer.c Sending button.c Sending Makefile Transmitting file data ... Committed revision 406.
Igjen, legg merke til at loggmeldingen veldig spesifikt nevner området av forandringer som ble flettet inn på trunk. Husk alltid å gjøre dette, fordi det er vital informasjon som du vil trenge senere.
For eksempel, tenk at du bestemmer deg for å fortsette arbeidet på grenen en uke til, for å fullføre feilrettingen eller en forbedring av den originale funksjonaliteten. Depotets HEAD-revisjon er nå 480, og du er klar til å utføre en ny fletting fra den private grenen din til trunk. Men som diskutert i “Beste praksiser for fletting” vil du ikke flette forandringene du allerede har flettet før; du vil bare flette alt “nytt” på grenen siden forrige gang du flettet. Trikset er å finne ut hva som er nytt.
Første skritt er å kjøre svn log på trunk og se etter en loggmelding fra forrige gang du flettet fra grenen:
$ cd calc/trunk $ svn log … ------------------------------------------------------------------------ r406 | bruker | 2004-02-08 11:17:26 -0600 (Sun, 08 Feb 2004) | 1 line Flettet forandringer mellom r341:405 fra my-calc-branch til trunk. ------------------------------------------------------------------------ …
Aha! Siden alle grenforandringene som skjedde mellom revisjonene 341 og 405 ble flettet til trunk som revisjon 406, vet du nå at du bare vil flette grenforandringene etter dette – ved å sammenligne revisjonene 406 og HEAD.
$ cd calc/trunk $ svn update At revision 480. # Vi ser at HEAD er 480 for øyeblikket, så vi bruker den for å utføre # flettingen: $ svn merge -r 406:480 http://svn.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile $ svn commit -m "Flettet forandringer mellom r406:480 fra my-calc-branch til trunk." Sending integer.c Sending button.c Sending Makefile Transmitting file data ... Committed revision 481.
Nå inneholder trunk alle forandringene som ble gjort i den andre omgangen på grenen. På dette punktet kan du enten slette grenen (dette vil vi komme tilbake til) eller fortsette arbeidet på grenen og repetere denne prosedyren for etterfølgende flettinger.
En annen vanlig bruksmåte for svn merge er å omgjøre en forandring som allerede er blitt lagt inn. Tenk deg at du jobber glad og fornøyd på en arbeidskopi av /calc/trunk, og plutselig finner ut at forandringen du gjorde langt tilbake i revisjon 303, som forandret integer.c, er helt feil. Den skulle aldri vært lagt inn. Du kan bruke svn merge for å “angre” forandringen i arbeidskopien din, og deretter legge inn de lokale forandringene til depotet. Alt du trenger å gjøre er å spesifisere en omvendt forskjell:
$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk U integer.c $ svn status M integer.c $ svn diff … # Sjekk at forandringen er fjernet … $ svn commit -m "Fjernet forandringen som ble lagt inn i r303." Sending integer.c Transmitting file data . Committed revision 350.
En måte å tenke på en depotrevisjon er som en spesifikk gruppe av forandringer (noen versjonskontrollsystemer kaller disse forandringssett – changesets). Ved å bruke -r-valget kan du be svn merge om å legge inn et forandringssett, eller et helt område av forandringssett, vi ber svn merge om å legge inn forandringssett nummer 303 baklengs inn i arbeidskopien vår.
Husk at det å rulle tilbake en forandring som dette er akkurat likt enhver annen svn merge-operasjon, så du bør bruke svn status og svn diff for å forsikre deg om at arbeidet ditt er i den tilstanden du vil det skal være i, og deretter bruke svn commit for å sende den endelige versjonen til depotet. Etter innleggingen er dette spesielle forandringssettet ikke lenger representert i HEAD-revisjonen.
Og så tenker du kanskje: Nåh, dette omgjorde vel egentlig ikke innleggingen? Forandringen eksisterer fortsatt i revisjon 303. Hvis noen henter ut en versjon av calc-prosjektet mellom revisjonene 303 og 349, vil de se den gale forandringen, ikke sant?
Ja, det stemmer. Når vi snakker om å “fjerne” en forandring, snakker vi egentlig om å fjerne den fra HEAD. Den originale forandringen eksisterer fortsatt i depotets historie. I de fleste situasjoner er dette greit nok. De fleste er bare interessert i å følge HEAD av et prosjekt uansett. Det er imidlertid spesielle tilfeller du virkelig vil ødelegge alle spor etter innleggingen, kanskje la noen inn et konfidensielt dokument ved en ulykke. Dette er ikke så lett, viser det seg, fordi Subversion ble spesielt designet for å aldri miste informasjon. Revisjoner er uforanderlige trær som bygger på hverandre. Det å fjerne en revisjon fra historien vil forårsake en dominoeffekt som vil føre til kaos i alle etterfølgende revisjoner og muligens gjøre alle arbeidskopiene ubrukelige.[9]
Det som er fint med versjonskontrollsystemer er at informasjon aldri går tapt. Selv om du sletter en fil eller katalog, kan den være borte fra HEAD-revisjonen, men objektet eksisterer fortsatt i tidligere revisjoner. Et av de vanligste spørsmålene nye brukere spør om, er: “Hvordan får jeg den gamle filen eller katalogen min tilbake?”.
Første skritt er å definere nøyaktig hvilket element du skal prøve å hente tilbake. Her er en nyttig metafor: Du kan tenke på hvert objekt i depotet som om det eksisterer i et slags todimensjonalt koordinatsystem. Det første koordinatet er et spesifikt revisjonstre, og det andre koordinatet er en sti inne i dette treet. Så hver versjon av filen eller katalogen din kan bli definert som et spesifikt koordinatpar.
Subversion har ingen Attic-katalog som CVS har,[10] så du må bruke svn log for å finne det eksakte koordinatparet som du vil hente tilbake. En god strategi er å kjøre svn log --verbose i en katalog som inneholdt det slettede elementet ditt. Valget --verbose viser en liste over alle forandrede elementer i hver revisjon; alt du trenger å gjøre er å finne revisjonen der du slettet filen eller katalogen. Du kan gjøre dette visuelt, eller ved å bruke et annet verktøy for å undersøke utdataene fra loggen (ved hjelp av grep, eller kanskje ved hjelp av et inkrementelt søk i en tekstbehandler).
… ------------------------------------------------------------------------ r808 | joe | 2003-12-26 14:29:40 -0600 (Fri, 26 Dec 2003) | 3 lines Changed paths: D /calc/trunk/real.c M /calc/trunk/integer.c Added fast fourier transform functions to integer.c. Removed real.c because code now in double.c. …
I eksempelet går vi ut i fra at du ser etter en slettet fil kalt real.c. Ved å se gjennom loggene for en foreldrekatalog, har du funnet ut at denne filen ble slettet i revisjon 808. Derfor er den siste versjonen av filen der den fortsatt eksisterte i revisjonen like før dette. Konklusjon: Du vil hente tilbake stien /calc/trunk/real.c fra revisjon 807.
Dette var den vanskelige delen – etterforskningen. Nå som du vet hva du vil hente tilbake, har du to forskjellige valg.
En måte er å bruke svn merge for å legge inn revisjon 808 “i revers”. (Vi har allerede gått gjennom hvordan vi omgjør forandringer, se “Omgjøre forandringer”.) Dette vil ha samme effekten som å legge til real.c en gang til som en lokal modifisering. Filen vil bli klargjort for tillegging, og etter at du sender det inn til depotet med svn commit, vil filen atter en gang eksistere i HEAD.
Men i dette spesielle eksempelet er det kanskje ikke den beste strategien. Å legge inn revisjon 808 i revers vil ikke bare klargjøre real.c for tillegging, men loggmeldingen indikerer at det vil også bli omgjort forandringer i integer.c, noe som du ikke ønsker. Du kan selvfølgelig bakoverflette revisjon 808 og deretter kjøre svn revert på de lokale forandringene i integer.c, men denne teknikken skalerer ikke alltid like bra. Hva hvis det var 90 filer som forandret seg i revisjon 808?
En annen og mer målbevisst strategi er å ikke bruke svn merge i det hele tatt, men derimot svn copy. Kopier ganske enkelt den eksakte revisjonens og stiens “koordinatpar” fra depotet til arbeidskopien din:
$ svn copy --revision 807 \
http://svn.example.com/repos/calc/trunk/real.c ./real.c
$ svn status
A + real.c
$ svn commit -m "Resurrected real.c from revision 807, /calc/trunk/real.c."
Adding real.c
Transmitting file data .
Committed revision 1390.
Plusstegnet som vises i statusoversikten indikerer at elementet ikke bare er klargjort for tillegging, men er klargjort for tillegging “med historie”. Subversion husker hvor det ble kopiert fra. I framtiden vil kjøring av svn log på denne filen gå tilbake forbi gjenoppstandelsen av filen og gjennom hele historien den hadde før revisjon 807. Med andre ord, denne nye real.c er ikke egentlig ny; den er en direkte etterkommer av den originale, slettede filen.
Selv om eksempelet vårt viser at vi henter tilbake en fil, legg merke til at disse samme teknikkene virker like godt når det gjelder å hente tilbake slettede kataloger.
Versjonskontroll blir vanligvis brukt til programutvikling, så her er en rask kikk på to av de vanligste forgrenings-/flettemønstere som blir brukt av programmeringsteam. Hvis du ikke bruker Subversion til programutvikling, kan du hoppe over denne seksjonen. Hvis du er en programutvikler som bruker versjonskontroll for første gang, følg nøye med, da disse fremgangsmåtene ofte blant erfarne brukere er ansett som de beste metodene. Disse prosessene er ikke spesifikke til Subversion; de kan brukes med ethvert versjonskontrollsystem. Men det kan hjelpe å se dem beskrevet i Subversionterminologi.
Programvare har vanligvis denne livssyklusen: Kode, teste, offentliggjøre, repetere. Det er to problemer med denne prosessen. For det første trenger utviklere å skrive ny funksjonalitet mens kvalitetssikringsteam tester versjoner som er ment å skulle være stabil. Nytt arbeide kan ikke stoppe opp mens programvaren blir testet ut. For det andre, teamet må nesten alltid støtte eldre, utgitte versjoner av programvaren; hvis en feil blir oppdaget i den seneste koden, eksisterer den sannsynligvis også i utgitte versjoner, og kundene vil ønske å få denne feilen rettet uten å måtte vente på en ny stor utgivelse.
Og det er her versjonskontroll kan hjelpe. Den vanlige prosedyren ser ut som dette:
Utviklerne legger inn alt nytt arbeid til trunk. Daglige forandringer legges inn på /trunk: Ny funksjonalitet, retting av feil og så videre.
trunk blir kopiert til en “utgivelses”-gren. Når teamet synes programvaren er klar for utgivelse (for eksempel en 1.0-versjon), kan /trunk bli kopiert til /branches/1.0.
Teamene fortsetter å arbeide parallelt. Et team begynner inngående testing av utgivelsesgrenen, mens et annet team fortsetter på nytt arbeid (for eksempel på det som skal bli versjon 2.0) i /trunk. Hvis det blir funnet feil på et av stedene, blir reparasjoner flettet fram og tilbake så mye som nødvendig. Men på et punkt stopper også denne prosessen. Grenen er “frosset” for avsluttende testing rett før en utgivelse.
Grenen blir merket og utgitt. Når testingen er ferdig, blir /branches/1.0 kopiert til /tags/1.0.0 som et referansebilde. Merket pakkes og utgitt til kundene.
Grenen blir vedlikeholdt over tid. Mens arbeidet fortsetter på /trunk for versjon 2.0, blir fortsatt feil som blir funnet på /trunk flettet derfra til /branches/1.0. Når et tilstrekkelig antall feil er blitt rettet, bestemmer vedlikeholderne seg for å lage en 1.0.1-utgivelse: /branches/1.0 blir kopiert til /tags/1.0.1, og merket blir pakket og utgitt.
Hele denne prosessen repeteres mens programvaren modnes: Når arbeidet for 2.0 er komplett, blir en ny utgivelsesgren for 2.0 opprettet, testet, merket og eventuelt utgitt. Etter noen år ender depotet opp med et antall grener i “vedlikeholdsmodus”, og et antall merker som representerer ferdige, utgitte versjoner.
En funksjonalitetsgren er den typen gren som har vært det dominerende eksempelet i dette kapittelet, den typen du har arbeidet på mens Sally fortsetter å arbeide på /trunk. Det er en midlertidig gren som er opprettet for å arbeide på en kompleks forandring uten å la det gå ut over stabiliteten til Trunk. I motsetning til utgivelsesgrener (som må bli støttet for alltid) blir funksjonalitetsgrener født, brukt en stund, flettet tilbake til trunk, og deretter til sist slettet. De har en begrenset nyttighetsperiode.
Igjen, prosjekt-policy varierer veldig når det gjelder nøyaktig når det passer å opprette en funksjonalitetsgren. Noen prosjekter bruker ikke funksjonalitetsgrener i det hele tatt; innlegginger til /trunk er tilgjengelig for alle. Fordelen med dette systemet er at det er enkelt – ingen trenger å lære om forgrening eller fletting. Ulempen er at koden i trunk ofte er ustabil eller ubrukelig. Andre prosjekter bruker forgreninger til det ekstreme; ingen forandringer blir noen gang lagt direkte inn på trunk. Til og med de enkleste forandringer blir laget på en gren med kort levetid, nøye kontrollert og deretter flettet til trunk. Så blir grenen slettet. Dette systemet garanterer en eksepsjonelt stabil og brukbar trunk til enhver tid, men til en pris i form av mer arbeid i prosessen.
Noen prosjekter velger en rute midt i mellom. De insisterer på at /trunk skal kunne kompilere og passere systemsjekker til enhver tid. En funksjonalitetsgren er bare nødvendig når en forandring krever et stort antall innlegginger som kan gå ut over stabiliteten. En god tommelfingerregel er å stille dette spørsmålet: Hvis utvikleren jobbet i flere dager i isolasjon og deretter la inn hele den store forandringen på en gang (så /trunk aldri ble ustabil), ville den blitt for stor for gjennomlesing? Hvis svaret til det er “ja”, bør forandringen bli utviklet på en funksjonalitetsgren. Etter hvert som utvikleren legger inn økende forandringer til grenen, kan de bli sett over av kolleger.
Til sist har vi saken med hvordan man best kan holde en fremtidig gren i “synk” med trunk etterhvert som arbeidet går fram. Som vi nevnte tidligere, er det en stor risiko å arbeide på en gren i flere uker eller måneder; forandringer på trunk kommer strømmende inn, til punktet der de to utviklingslinjene er såpass forskjellige at det kan bli et mareritt å flette grenen tilbake til trunk.
Denne situasjonen unngås best ved å jevnlig flette trunk-forandringer inn på grenen. Lag en regel: En gang i uken fletter du forrige ukes forandringer til grenen. Vær nøye når du gjør dette; flettingen må sjekkes for å unngå problemet med gjentatte flettinger (som beskrevet i “Følge flettinger manuelt”). Du må skrive nøyaktige loggmeldinger med detaljer om hvilket revisjonsområde som allerede er blitt flettet (som demonstrert i “Flette en hel gren til en annen”). Det kan høres skremmende ut, men er faktisk ganske enkelt å gjøre.
Etterhvert er du klar til å flette den “synkroniserte” funksjonalitetsgrenen tilbake til trunk. For å gjøre dette, start med å gjøre en avsluttende fletting av de siste trunk-forandringene til grenen. Når det er gjort, vil de siste versjonene av grenen og trunk være absolutt like, bortsett fra forandringene på grenen din. Så i dette spesielle tilfellet, vil du flette ved å sammenligne grenen med trunk:
$ cd trunk-working-copy
$ svn update
At revision 1910.
$ svn merge http://svn.example.com/repos/calc/trunk@1910 \
http://svn.example.com/repos/calc/branches/mybranch@1910
U real.c
U integer.c
A newdirectory
A newdirectory/newfile
…
Ved å sammenligne HEAD-revisjonen for trunk med HEAD-revisjonen på grenen, kan du definere et delta som beskriver kun de forandringene som du gjorde til grenen; begge utviklingsgrener har allerede forandringene fra trunk.
En annen måte å tenke på dette mønsteret er at den ukentlige synkroniseringen er det samme som å kjøre svn update i en arbeidskopi, mens det siste trinnet med fletting er det samme som å kjøre svn commit fra en arbeidskopi. Når alt kommer til alt, er jo en arbeidskopi en grunn, privat gren. Det er en gren som bare er i stand til å lagre én forandring på en gang.
Kommandoen svn switch forandrer en eksisterende arbeidskopi til en annen gren. Selv om denne kommandoen strengt tatt ikke er nødvendig for å arbeide med forgreninger, gir den en fin snarvei for brukere, I det tidligere eksempelet vårt, etter at du opprettet en privat gren, hentet du ut en fersk arbeidskopi av den nye katalogen i depotet. Istedenfor kan du rett og slett be Subversion om å forandre arbeidskopien din av /calc/trunk til å avspeile plasseringen til den nye grenen:
$ cd calc $ svn info | grep URL URL: http://svn.example.com/repos/calc/trunk $ svn switch http://svn.example.com/repos/calc/branches/my-calc-branch U integer.c U button.c U Makefile Updated to revision 341. $ svn info | grep URL URL: http://svn.example.com/repos/calc/branches/my-calc-branch
Etter å ha “byttet om” til grenen, er ikke arbeidskopien mer forskjellig enn om du hadde gjort en fersk uthenting av katalogen. Og det er vangligvis med praktisk å bruke denne kommandoen, fordi grener ¤differ kun i liten grad. Tjeneren sender bare et minimalt sett med forandringer nødvendig for å avspeile grenkatalogen.
svn switch-kommandoen tar også et --revision (-r)-valg, så du trenger ikke alltid å flytte arbeidskopien din til “tuppen” av grenen.
Selvfølgelig, de fleste prosjekter er mer kompliserte enn calc-eksempelet vårt, og inneholder flere underkataloger. Subversionbrukere følger ofte en spesiell algoritme ved bruk av grener:
Kopier hele “trunk” fra prosjektet til en ny grenkatalog.
Bytt bare om en del av trunk-arbeidskopien for å avspeile forgreningen.
Med andre ord, hvis en bruker vet at grenarbeidet kun trenger å skje i en spesiell underkatalog, bruker de svn switch for å flytte bare denne underkatalogen til grenen. (Og noen ganger vil brukere bare bytte om en enkelt arbeidsfil til grenen!) På denne måten kan de fortsette å motta normale oppdateringer fra “trunk” til mesteparten av arbeidskopien deres, men de ombyttede delene vil forbli immune (unntatt hvis noen legger inn en forandring til denne grenen). Denne funksjonalitetel legger til en hel ny dimensjon til konseptet med en “blandet arbeidskopi” – ikke bare kan arbeidskopier inneholde en blanding av av arbeidsrevisjoner, men også en blanding av depotbeliggenheter.
Hvis arbeidskopien din inneholder et antall ombyttede katalogtrær fra forskjellige depotplasseringer, fortsetter den å virke som normalt. Når du oppdaterer, vil du motta patcher til hvert undertre der det passer. Når du legger inn forandringer, vil dine lokale forandringer fortsatt bli lagt inn som en enkel, atomisk forandring til depotet.
Legg merke til at mens det er greit for arbeidskopien din å gjenspeile en blanding av depotplasseringer, må alle disse plasseringene være innenfor det samme depotet. Subversiondepot er foreløpig ikke i stand til å kommunisere med hverandre; det er en funksjonalitet som er planlagt etter SUbversion 1.0.[11]
Fordi svn switch egentlig er en variant av svn update oppfører den seg på samme måte; alle lokale modifiseringer i arbeidskopen blir tatt vare på når nye data ankommer fra depotet. Dette lar deg gjøre en masse lure ting.
For eksempel, tenk at du har en arbeidskopi av /calc/trunk og gjør et antall forandringer i den. Så finner du plutselig ut at du skulle gjøre disse forandringene til en gren istedenfor. Ikke noe problem! Når du bruker svn switch for å flytte arbeidskopien til grenen, vil de lokale forandringene bli værende. Du kan deretter teste og legge dem inn på grenen.
Et annet vanlig versjonskontrollkonsept er et merke – tag. Et merke er bare et “øyeblikksbilde” av et prosjekt på en spesiell tid. I Subversion ser denne idéen ut til å være overalt. Hver depotrevisjon er akkurat det – et øyeblikksbilde av filsystemet etter hver innlegging.
Men folk vil ofte ønske å gi mer menneskevennlige navn til merker, som release-1.0. Og de ønsker å ta øyeblikksbilder av mindre underkataloger i filsystemet. Når alt kommer til alt, er det ikke så lett å huske at release-1.0 av et programprosjekt er en spesiell underkatalog av revisjon 4822.
Atter en gang kommer svn copy og redder situasjonen. Hvis du vil lage et øyeblikksbilde av /calc/trunk nøyaktig som den ser ut i HEAD-revisjonen, kan du lage en kopi av den:
$ svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/tags/release-1.0 \
-m "Tagging the 1.0 release of the 'calc' project."
Committed revision 351.
Dette eksempelet forutsetter at en /calc/tags-katalog allerede eksisterer. (Hvis den ikke gjør det, se svn mkdir.) Etter at kopieringen er fullført, vil den nye release-1.0-katalogen for alltid være et øyeblikksbilde av hvordan prosjektet så ut i HEAD-revisjonen på den tiden du lagde kopien. Selvfølgelig vil du være mer presis angående hvilken revisjon du kopierer, i tilfelle noen andre kan ha lagt inn forandringer til prosjektet mens du ikke så det. Så hvis du vet at revisjon 350 av /calc/trunk er nøyaktig det øeblikksbildet du ønsker, kan du spesifisere dette ved å angi -r 350 til svn copy-kommandoen.
Men vent nå litt: Er ikke denne prosedyren med opprettelse av merker den samme prosedyren som vi brukte da vi lagde en gren? Ja, faktisk er det det. I Subversion er det ingen forskjell mellom et merke og en gren. Begge er bare vanlige kataloger som er opprettet ved kopiering. Akkurat som med grener, den eneste grunnen til at en kopiert katalog er et “merke” er fordi mennesker har bestemt seg for å behandle det på denne måten: Så lenge ingen noen gang legger inn forandringer til katalogen, forblir den for alltid et øyeblikksbilde. Hvis noen begynner å legge inn forandringer til den, blir den en gren.
Hvis du administrerer et depot, er det to fremgangsmåter du kan bruke for å holde rede på merker. Den første fremgangsmåten er “ikke rør”: Som en del av reglene innen prosjektet, bestem hvor i depotet merkene skal ligge, og gjør det klart for alle brukere hvordan de skal behandle kataloger som de kopierer inn dit. (Det vil si, vær sikker på at de vet at nye innlegginger ikke skal skje der.) Den andre fremgangsmåten er mer paranoid: Du kan bruke et av aksesskontrollskriptene som følger med Subversion til å forhindre noen fra å gjøre noe annet enn å opprette nye kopier i merkeområdet (se Kapittel 6, Server Configuration). Den paranoide ruten er imidlertid vanligvis ikke nødvendig. Hvis en bruker legger inn en forandring til en merkekatalog, kan du enkelt og greit omgjøre forandringen som nevnt i den forrige seksjonen. Dette er versjonskontroll, tross alt.
Noen ganger vil du at “øyeblikksbildet” skal være mer komplisert enn en enkel katalog med en enkelt revisjon.
For eksempel, tenk deg at prosjektet ditt er mye større enn calc-eksempelet vårt: Det inneholder mange underkataloger og mange flere filer. Mens du holder på med arbeidet, bestemmer du deg kanskje for at du må lage en arbeidskopi som er beregnet på å ha en spesiell funksjonalitet og feilrettinger. Du kan oppnå dette ved å selektivt tilbakedatere filer eller kataloger til spesielle revisjoner (ved å bruke svn update -r i stor skala), eller ved å bytte om filer og kataloger til spesielle grener (ved hjelp av svn switch). Når du er ferdig, er arbeidskopien din et virvar av depotplasseringer fra forskjellige revisjoner. Men etter at du har kjørt noen tester, vet du at dette er akkurat sånn som du vil ha det.
På tide å lagre et øyeblikksbilde. Det å kopiere en URL til en annen vil ikke fungere her. I dette tilfellet ønsker du å lage et øyeblikksbilde av din eksakte arbeidskopi og lagre den i depotet. Heldigvis har svn copy faktisk fire forskjellige bruksmåter (som du kan lese om i kapittel 9), inkludert muligheten til å kopiere et tre av en arbeidskopi til depotet:
$ ls my-working-copy/ $ svn copy my-working-copy http://svn.example.com/repos/calc/tags/mytag Committed revision 352.
Nå er det en ny katalog i depotet, /calc/tags/mytag, som er et eksakt øyeblikksbilde av arbeidskopien din – blandede revisjoner, URLer og hele pakken.
Andre brukere har funnet interessante måter å bruke denne funksjonaliteten på. Noen ganger er det situasjoner hvor du har en dunge med lokale forandringer i arbeidskopien, og du vil at en kollega skal se på dem. Istedenfor å kjøre svn diff og sende patchfilen (som ikke vil fange opp treforandringer), kan du istedenfor bruke svn copy for å “sende” arbeidskopien til et privat område i depotet. Kollegaen din kan dermed enten hente ut en nøyaktig kopi av arbeidskopien din, eller bruke svn merge for å motta de eksakte forandringene dine.
Du har kanskje sett nå at Subversion er ekstremt fleksibel. Fordi grener og merker blir laget med den samme underliggende mekanismen (kopier av kataloger), og fordi grener og merker vises i filsystemet, finner mange Subversion litt skremmende. Den er nesten for fleksibel. I denne seksjonen vil vi komme med noen forslag for hvordan du kan arrangere og vedlikeholde dataene dine over tid.
Det er noen standardiserte, anbefalte måter å organisere et depot på. Mange lager en trunk-katalog for der “hovedlinjen” av utvikling foregår, en branches-katalog som inneholder grenkopier, og en tags-katalog som inneholder merker. Hvis et depot bare inneholder ett prosjekt, lager man ofte disse toppkatalogene:
/trunk /branches /tags
Hvis et depot inneholder flere prosjekter, legger administratorer vanligvis depotet opp etter prosjekt (se “Velge en depot-layout” for å lese mer om “prosjektrøtter”):
/paint/trunk /paint/branches /paint/tags /calc/trunk /calc/branches /calc/tags
Du står selvfølgelig fritt til å ignorere disse vanlige layoutene. Du kan lage alle mulige varianter, hva som enn fungerer best for deg og ditt team. Husk at hva du enn velger, er du ikke forpliktet til å ha det sånn for alltid. Du kan reorganisere depotet ditt når du vil. Fordi grener og merker er vanlige kataloger, kan svn move-kommandoen flytte eller skifte navn på dem sånn som du ønsker. Å bytte fra en layout til en annen er bare snakk om å en serie flyttinger på tjeneren; hvis du ikke liker måten ting er organisert i depotet, bare flytt katalogene rundt om kring.
Men husk at selv om det å flytte kataloger er en enkel sak, må du også tenke på brukerne dine. Ommøbleringen kan være forvirrende for brukere med eksisterende arbeidskopier. Hvis en bruker har en arbeidskopi fra en spesiell depotkatalog, kan svn move-operasjonen fjerne stien fra den seneste revisjonen. Når brukeren kjører svn update neste gang, vil han bli fortalt at arbeidskopien hans representerer en sti som ikke lenger finnes, og brukeren vil bli tvunget til å bruke svn switch for å sette arbeidskopien til den nye plasseringen.
En annen fin funksjonalitet med Subversions modell er at grener og merker kan ha begrenset levetid, akkurat som ethvert annet versjonert element. For eksempel, tenk at du etterhvert blir ferdig med alt arbeidet på din personlige gren i calc-prosjektet. Etter at du har flettet alle dine forandringer tilbake til /calc/trunk, trenger ikke grenkatalogen å ligge der mer:
$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \
-m "Removing obsolete branch of calc project."
Committed revision 375.
Og nå er forgreningen borte. Vel, selvfølgelig er den ikke egentlig borte, katalogen mangler bare fra HEAD-revisjonen og distraherer dermed ingen. Hvis du bruker svn checkout, svn switch eller svn list for å undersøke en tidligere revisjon, vil du fortsatt være i stand til å se den gamle grenen din.
Hvis det ikke er nok å bare undersøke den slettede katalogen, kan du alltids hente den tilbake. Å hente tilbake data i Subversion er veldig enkelt. Hvis det er en slettet katalog (eller fil) som du vil hente tilbake inn i HEAD, kan du bruke svn copy -r for å kopiere den fra den gamle revisjonen:
$ svn copy -r 374 http://svn.example.com/repos/calc/branches/my-calc-branch \
http://svn.example.com/repos/calc/branches/my-calc-branch
Committed revision 376.
I eksempelet vårt hadde den personlige grenen din en relativt kort levetid, du kan ha laget den for å ordne en feil eller legge til en ny funksjonalitet. Når oppgaven din er gjort, er levetiden til grenen over. Men innen programutvikling er det også vanlig å ha to “hoved”-grener som lever ved siden av hverandre for veldig lange perioder. For eksempel, tenk at det er på tide å utgi en stabil versjon av calc-prosjektet til offentligheten, og du vet at det kommer til å ta noen måneder å luke ut feil av programmet. Du vil ikke at folk skal legge inn ny funksjonalitet til prosjektet, men du vil heller ikke at utviklerne skal stoppe programmeringen. Så istedenfor lager du en “stabil” gren av programmet som ikke vil forandre seg så mye:
$ svn copy http://svn.example.com/repos/calc/trunk \
http://svn.example.com/repos/calc/branches/stable-1.0 \
-m "Creating stable branch of calc project."
Committed revision 377.
Og nå kan utviklerne fortsette med å legge til rykende ferske (eller eksperimentelle) funksjoner til /calc/trunk, og du kan lage en regel for prosjektet som sier at bare feilrettinger skal legges inn i /calc/branches/stable-1.0. Det vil si, mens brukerne fortsetter å arbeide i trunk, kan feilrettinger selektivt bli flettet over til den stabile grenen. Selv etter at den stabile grenen er på markedet, vil du sannsynligvis fortsette å vedlikeholde grenen i lang tid – det vil si så lenge du fortsetter med å vedlikeholde utgivelsen for kunder.
Vi har gått igjennom en god del i dette kapittelet. Vi har diskutert konseptene med merker og grener, og demonstrert hvordan Subversion implementerer disse konseptene ved å kopiere kataloger med svn copy-kommandoen. Vi har vist hvordan svn merge brukes for å kopiere forandringer fra en gren til en annen, eller omgjøre feile forandringer. Vi har gått over bruken av svn switch for å lage arbeidskopier med blandede revisjoner. Og vi har snakket om hvordan man kan vedlikeholde organiseringen og levetiden til grener i et depot.
Husk mantraet til Subversion: Grener og merker er billige. Så bruk dem så mye du vil!
[7] Subversion støtter ikke kopiering mellom forskjellige depot. Når du bruker URLer med svn copy eller svn move kan du bare kopiere elementer innenfor det samme depotet.
[8] I fremtiden planlegger Subversionprosjektet å bruke (eller finne opp) et utvidet patchformat som beskriver treforandringer.
[9] Subversionprosjektet har imidlertid planer om å legge inn en svnadmin obliterate-kommando som vil ta seg av oppgaven med å permanent slette informasjon. I mellomtiden, se “svndumpfilter” for en mulig omvei om problemet.
[10] Fordi CVS ikke versjonerer trær, lager den et Attic-område innenfor hver depotkatalog som en måte å huske slettede filer på.
[11] Du kan imidlertid bruke svn switch med --relocate-valget hvis URL-en til tjeneren forandrer seg og du ikke vil forkaste en eksisterende arbeidskopi. Se svn switch-seksjonen i Kapittel 9, Subversion Complete Reference for mer informasjon og et eksempel.
Innholdsfortegnelse
Subversiondepotet er den sentrale lagringsplassen for versjonerte data for ethvert antall prosjekter. Som sådan blir det en opplagt kandidat for all kjærlighet og oppmerksomhet en administrator kan tilby. Selv om depotet vanligvis er et element som krever lite vedlikehold, er det viktig å forstå hvordan du kan konfigurere og passe på det så potensielle problemer blir unngått, og aktuelle problemer løst på en trygg måte.
I dette kapittelet vil vi diskutere hvordan du kan opprette og konfigurere et Subversiondepot. Vi vil også snakke om depotvedlikehold, inkludert bruken av verktøyene svnlook og svnadmin (som følger med Subversion). Vi vil adressere vanlige spørsmål og feilgrep, og komme med noen forslag til hvordan du kan legge opp dataene i depotet.
Hvis du planlegger å aksessere et Subversiondepot bare i rollen som en bruker som har data under versjonskontroll (det vil si via en Subversionklient), kan du hoppe over hele dette kapitlet. Hvis du derimot er, eller vil bli, en administrator for et Subversiondepot,[12] bør du så absolutt følge med i dette kapittelet.
Før vi hopper inn i det mer generelle innen emnet depotadministrasjon, la oss definere videre hva et depot er. Hvordan ser det ut? Hvordan føles det? Vil det ha teen sin varm eller med is, søt, og med sitron? Som en administrator blir det forventet av deg at du må forstå sammensetningen av et depot både fra et logisk perspektiv – det å styre med hvordan data er representert i depotet – og fra et fysisk skrue-og-mutter-perspektiv – hvordan et depot ser ut og oppfører seg i sammenheng med verktøy som ikke hører med til Subversion-pakken. Den følgende seksjonen dekker noen av disse grunnleggende konseptene på et høyt nivå.
Konseptmessig sagt er et Subversiondepot en sekvens av trær. Hvert tre er et øyeblikksbilde av hvordan filene og katalogene så ut på et spesielt tidspunkt. Disse øyeblikksbildene er opprettet som et resultat av klientoperasjoner og blir kalt revisjoner.
Hver revisjon begynner livet som et transaksjonstre. Når du gjør en innlegging, bygger en klient en Subversiontransaksjon som avspeiler deres lokale forandringer (pluss eventuelle forandringer som er blitt lagt inn i tillegg til depotet siden begynnelsen av innleggingsprosessen), og instruerer deretter depotet om å lagre dette treet som det neste øyeblikksbildet i sekvensen. Hvis innleggingen lykkes, blir transaksjonen innlemmet i et nytt revisjonstre, og blir tildelt et nytt revisjonsnummer. Hvis innleggingen av en eller annen grunn ikke lykkes, blir transaksjonen ødelagt og klienten informeres om feilen.
Oppdateringer fungerer på en lignende måte. Klienten bygger et midlertidig transaksjonstre som avspeiler tilstanden til arbeidskopien. Depotet sammenligner deretter dette transaksjonstreet med revisjonstreet i den forespurte revisjonen (vanligvis det nyeste, eller “yngste” treet), og sender tilbake informasjon som forteller klienten om hvilke forandringer som er nødvendig for å forandre deres arbeidskopi til et replikat av det aktuelle revisjonstreet. Etter at oppdateringen er fullført, blir den midlertidige transaksjonen slettet.
Bruken av transaksjonstrær er den eneste måten å gjøre permanente forandringer til et depots versjonskontrollerte filsystem. Det er imidlertid viktig å forstå at livløpet til en transaksjon er komplett fleksible. I tilfellet med oppdateringer er transaksjoner midlertidige trær som blir øyeblikkelig ødelagt. I tilfellet med innlegginger, blir transaksjoner transformert inn i permanente revisjoner (eller fjernet hvis innleggingen feiler). I tilfelle en generell feil eller programfeil oppstår, er det mulig at en transaksjon kan bli liggende igjen i depotet (uten å påvirke noe som helst, men tar opp diskplass).
I teorien vil kanskje hele arbeidsflytapplikasjoner en gang gi mer nøyaktig kontroll med levetiden til en transaksjon. Det er mulig å tenke seg et system som ved hver transaksjon beregnet på å bli en revisjon blir satt på venting etter at klienten er ferdig med å beskrive sine forandringer til depotet. Dette ville muliggjøre at hver ny innlegging kan bli sett over av noen andre, kanskje en manager eller ledende utvikler, som kan velge å forfremme transaksjonen til en revisjon eller avbryte den.
Transaksjoner og revisjoner i Subversiondepotet kan ha vedlagte egenskaper. Disse egenskapene er generiske “nøkkel-til-verdi”-mappinger, og blir vanligvis brukt til å lagre informasjon om treet som de er vedlagt. Navnene og verdiene til disse egenskapene er lagret i depotets filsystem, sammen med resten av dataene for treet.
Revisjons- og transaksjonsegenskaper er nyttige for å assosiere informasjon sammen med et tre som ikke er spesifikt relatert til filene og katalogene i dette treet ― den typen informasjon som ikke er behandlet av arbeidskopier for klienten. For eksempel, når en ny innleggingstransaksjon er opprettet i depotet, legger Subversion en egenskap til denne transaksjonen kalt svn:date – et tidsmerke som representerer tidspunktet transaksjonen ble opprettet. Når innleggingsprosessen er fullført, og transaksjonen blir forfremmet til en permanent revisjon, har treet også fått en egenskap for å lagre brukernavnet til revisjonens forafatter (svn:author) og en egenskap for å lagre loggmeldingen som hører til denne revisjonen svn:log.
Revisjons- og transaksjonsegenskaper er uversjonerte egenskaper – når de blir modifisert blir den foregående verdien permanent forkastet. Selv om revisjonstrær er uforanderlige, gjelder ikke dette egenskapene som er vedlagt disse trærne. Du kan legge til, fjerne, og modifisere revisjonsegenskaper til enhver tid i fremtide. Hvis du legger inn en ny revisjon og senere finner ut at du la inn feil informasjon eller skrivefeil i loggmeldingen, kan du enkelt forandre verdien i svn:log-egenskapen med en ny, rettet loggmelding.
I Subversion 1.1 er det to valg for å lagre data i et Subversiondepot. En depottype lagrer alt i en Berkeley DB-database; den andre typen lagrer data som ordinære “flate filer” i et tilpasset format. Fordi Subversionutvilkere ofte referer til et depot som “det [versjonerte] filsystemet”, har de lagt seg til vanen med å refere til den sistnevnte typen depot som FSFS: Det vil si, det er en versjonert filsystemimplementasjon som bruker det vanlige filsystemet til operativsystemet for å lagre data.
Når et depot er opprettet, må administratoren bestemme om Berkeley DB eller FSFS skal brukes. Det er fordeler og ulemper med begge to, som vi vil gå gjennom ganske snart. Ingen av de bakenforliggende depotene er mer “offisielt” enn det andre, og programmer som aksesserer depotet er isolert fra denne implementasjonsdetaljen. Programmer har ingen formening om hvordan depotet lagrer data; de ser bare revisjons- og transaksjonstrær gjennom depotgrensesnittet.
Her er en tabell som gir en oversikt over forskjellene mellom depoter av typen Berkeley DB og FSFS. De neste seksjonene går inn i detalj.
Tabell 5.1. Sammenligning av datalagring i depot
| Funksjonalitet | Berkeley DB | FSFS |
|---|---|---|
| Følsom for avbrytelser | veldig; krasj og problemer med rettigheter kan etterlate databasen i en “fastkilt” (Engelsk: wedged) tilstand som krever prosedyrer for journalgjenoppretting. | ganske ufølsom. |
| Kan brukes fra et ikke-skrivbart medium | nei | ja |
| Plattformuavhengig lagring | nei | ja |
| Brukbar over nettverksfilsystem | nei | ja |
| Depotstørrelse | litt større | litt mindre |
| Skalerbarhet: Antall revisjonstrær | database; ingen problemer | ytelsen på noen eldre filsystemer blir dårligere med tusenvis av elementer i en katalog. |
| Skalerbarhet: Kataloger med mange filer | langsommere | raskere |
| Hastighet: Uthenting av nyeste kode | raskere | langsommere |
| Hastighet: Store innlegginger | langsommere, men arbeidet blir spredt ut over innleggingen | raskere, men forsinkelse i avslutningen kan forårsake tidsavbrudd for klienten. |
| Behandling av grupperettigheter | følsom for problemer med umask; det beste er om den blir aksessert av bare en bruker. | jobber seg rundt problemer med umask |
| Kodemodenhet | i bruk siden 2001 | i bruk siden 2004 |
I den innledende designfasen av Subversion bestemte utviklerne seg for å bruke Berkeley DB av diverse grunner, blant annet dens åpne lisens, transaksjonsstøtte, stabilitet, effektivitet, enkelt grensesnitt, trådsikkerhet, støtte for ¤cursors og så videre.
Berkeley DB tilbyr reell støtte for transaksjoner – kanskje dens kraftigste funksjonalitet. Flere prosesser som aksesserer Subversiondepotene dine trenger ikke å tenke på faren ved å rote til hverandres data. Isolasjonen som transaksjonssystemet tilbyr er slik at for enhver operasjon ser Subversions depotkode et statisk bilde av en database – ikke en database som konstant forandres av en annen prosess – og kan foreta avgjørelser basert på dette bildet. Hvis avgjørelsen kommer i konflikt med det en annen prosess gjør, blir hele operasjonen rullet tilbake som om den aldri skjedde, og Subversion prøver forsiktig operasjonen mot et nytt, oppdatert (og fortsatt statisk) bilde av databasen.
En annen fin funksjonalitet med Berkeley DB er varme backuper (hot backups) – muligeten til å ta sikkerhetskopi av databasen uten å ta den ned. Vi vil diskutere hvordan du tar backup av depotet i “Sikkerhetskopi av depotet”, men fordelene med å kunne ta fullstendige fungerende kopier av depotene dine uten nedetid er innlysende.
Berkeley DB er også et veldig sikkert databasesystem. Subversion bruker Berkeley DBs loggefasiliteter, som betyr at databasen først lager en beskrivelse i loggfiler på disken om hva den er i ferd med å gjøre, og foretar deretter selve modifiseringen. Dette er for å forsikre om at i tilfelle noe går galt, kan databasesystemet hente tilbake et tidligere sjekkpunkt – en posisjon i loggfilene som den vet ikke er ødelagt – og “spille av” transaksjoner inntil dataene er gjenopprettet til en fungerende tilstand. Se “Passe på diskplassen” for mer angående loggfilene i Berkeley DB.
Men alle roser har torner, og derfor må vi være klar over noen kjente begrensninger i Berkeley DB. For det første er ikke Berkeley DB-miljøer portable. Du kan ikke bare kopiere et Subversiondepot som er laget på et UNIX-system til et Windows-system og forvente at det skal virke. Selv om mye av Berkely DB-formatet er arkitekturuavhengig, er det andre aspekter med miljøet som ikke er det. For det andre bruker Subersion Berkeley DB på en måte som ikke vil fungere i Windows 95/98 – hvis du må ha depotet på en Windowsmaskin, bruk Windows 2000 eller Windows XP. Legg heller aldri et Berkeley DB-depot på en nettverksdisk. Selv om Berkeley DB lover å oppføre seg korrekt på nettverksdisker som oppfyller spesielle kriterier, er det nesten ingen kjente nettverksdelinger som faktisk oppfyller alle disse spesifikasjonene.
Til sist, fordi Berkeley DB er et bibliotek linket direkte inn i Subversion, er det mer sensibelt for avbrudd enn et typisk relasjonsdatabesestystem. De fleste SQL-systemer har for eksempel en dedikert tjenerprosess som fordeler all tilgang til tabeller. Hvis et program som aksesserer databasen krasjer av en eller annen grunn, oppdager daemonen som styrer databasen dette og ordner opp i eventuelt rot som blir liggende igjen. Og fordi databasedaemonen er den eneste prosessen som aksesserer tabellene, trenger ikke applikasjoner å tenke på rettighetskonflikter. Disse tingene er imidlertid ikke tilfelle med Berkeley DB. Subversion (og programmer som bruker Subversionbiblioteker) aksesserer databasetabellene direkte, noe som betyr at et programkrasj kan etterlate databasen i en midlertidig, utilgjengelig tilstand. Når dette skjer, må en administrator be Berkeley DB om gjenoppretting til et sjekkpunnkt, noe som er litt plagsomt. Andre ting kan føre til at et depot “kiler seg fast” (“wedge”) sammen med krasjede prosesser, som for eksempel programmer som kommer i konflikt angående eierskap og tilgang til databasefilene. Så mens et Berkeley DB-depot er ganske raskt og skalerbart, fungerer det best når det kun blir brukt av én enkelt tjenerprosess som kjører som én bruker – som for eksempel Apaches httpd eller svnserve (se Kapittel 6, Server Configuration) – istedenfor å aksessere det med mange forskjellige brukere via file:/// eller svn+ssh://-URLer. Hvis et Berkeley DB-depot blir brukt direkte med flere forskjellige brukere, pass på at du har lest “Supporting Multiple Repository Access Methods”.
I midten av 2004 ble et nytt lagringsformat for depotet laget, et som ikke bruker en database i det hele tatt. Et FSFS-depot lagrer et revisjonstre i en enkelt fil, og alle depotets revisjoner kan dermed finnes i en enkelt underkatalog full av nummererte filer. Transaksjoner blir opprettet i separate underkataloger. Når den er komplett, blir en enkelt transaksjonsfil opprettet og flyttet til revisjonskatalogen og garanterer dermed at innleggingen blir atomisk. Og fordi en revisjonsfil er permanent og uforanderlig, kan depotet også bli tatt backup av mens det er i bruk, akkurat som et Berkeley DB-depot.
Revisjonsfilformatet representerer en revisjons katalogstruktur, filinnhold, og forskjeller i forhold til filer i andre revisjonstrær. Ulikt en Berkeley DB-database, er dette lagringsformatet portabelt mellom forskjellige operativsystemer og er ikke sensitiv ovenfor CPU-arkitektur. Fordi det ikke blir brukt journalfiler eller filer som bruker delt hukommelse, kan depotet trygt aksesseres via et nettverksfilsystem og bli utforsket i et skrivebeskyttet miljø. Mangelen på databaseoverhead betyr også at den samlede depotstørrelsen blir en del mindre.
FSFS har også forskjellige karakteristikker når det gjelder effektivitet. Når en katalog med mange filer legges inn, bruker FSFS en O(N) algoritme for å legge til poster, mens Berkeley DB bruker en O(N^2) algoritme for å skrive hele katalogen en gang til. På den annen side skriver FSFS den seneste versjonen av en fil som en delta (forskjell) mot en tidligere versjon, noe som betyr at det å hente ut det seneste treet er litt langsommere enn å hente fullteksten lagret i en HEAD-revisjon i Berkeley DB. FSFS har også en lengre forsinkelse når en innlegging skal fullføres, noe som i ekstreme tilfeller kan føre til at klienter får et tidsavbrudd under venting på respons.
Men den viktigste forskjellen er FSFSs evne til å ikke bli “fastkilt” hvis noe går galt. Hvis en prosess som bruker en Berkeley DB-database får problemer med rettigheter eller plutselig krasjer, er ikke databasen brukbar før en administrator gjenoppretter den. Hvis det samme skjer med en prosess som bruker et FSFS-depot, blir ikke depotet påvirket i det hele tatt. I verste fall blir bare noen transaksjonsdata liggende igjen.
Det eneste virkelige argumentet mot FSFS er fartstiden det har hatt i forhold til Berkeley DB. Det er ikke like mye brukt og har ikke blitt stresstestet like mye, så derfor er mange av disse antakelsene om hastighet og skalerbarhet nettopp det: Antakelser, basert på gode gjetninger. I teorien lover den en lavere barriere å forsere for nye administratorer å forsere og er forventet å skape mindre problemer. I praksis vil bare tiden vise om dette stemmer.
Det å opprette et Subversiondepot er en utrolig enkel oppgave. svnadmin-verktøyet som følger med Subversion har en delkommando som gjør akkurat det. For å opprette et nytt depot, er det bare å kjøre:
$ svnadmin create /sti/til/depot
Dette oppretter et nytt depot i katalogen /sti/til/depot. Dette nye depotet starter livet ved revisjon 0, som er definert til å bare innholde en rot på toppnivå (/) i filsystemet. Revisjon 0 har også en enkelt revisjonsegenskap satt innledningsvis, svn:date, som er satt til tidspunktet depotet ble opprettet.
I Subversion 1.1 blir et depot opprettet med en bakenforliggende Berkeley DB-database som standard. Denne oppførselen kan forandre seg i fremtidige releaser. Uansett, typen kan bli eksplisitt valgt med --fs-type-valget:
$ svnadmin create --fs-type fsfs /sti/til/depot $ svnadmin create --fs-type bdb /sti/til/annet/depot
Opprett ikke et Berkeley DB-depot på en nettverksdisk – det kan ikke eksistere på et fjerntliggende filsystem som NFS, AFS eller Windows SMB. Berkeley DB krever at det underliggende filsystemet har implementert streng POSIX låsesemantikk, og enda viktigere, muligheten til å mappe filer direkte inn i prosesshukommelsen. Nesten ingen nettverksfilsystemer tilbyr disse mulighetene. Hvis du forsøker å bruke Berkeley DB på en nettverksdisk, er resultatene uforutsigelige – du kan se mystiske feilmeldinger med en gang, eller det kan gå månedsvis før du oppdager at depotet i all stillhet blir herpa.
Hvis du trenger flere datamaskiner for å aksessere depotet, lager du et FSFS-depot på nettverksdisken, ikke et Berkeley DB-depot. Eller enda bedre, sett opp en skikkelig tjenerprosess (som for eksempel Apache eller svnserve), legg depotet i et lokalt filsystem som tjeneren aksessere, og gjør depotet tilgjengelig over et nettverk. Kapittel 6, Server Configuration dekker denne prosessen i detalj.
Du har kanskje lagt merke til at stiargumentet til svnadmin bare var en vanlig filsystemsti og ikke en URL lik det svn-klienten bruker når den refererer til depot. Både svnadmin og svnlook ansees som tjenerside-verktøy – de blir brukt på maskinen hvor depotet ligger for å undersøke og modifisere aspekter ved depotet, og er faktisk ikke i stand til å utføre oppgaver over nettverket. En vanlig feil som blir gjort av nybakte Subversionbrukere er å gi URLer (til og med av typene “local” og file:) til disse to programmene.
Så, etter at du har kjørt svnadmin create-kommandoen, har du et skinnende nytt depot i sin egen katalog. La oss ta en kikk på hva som egentlig er laget i denne katalogen.
$ ls depot conf/ dav/ db/ format hooks/ locks/ README.txt
Med unntak av filene README.txt og format, er depotkatalogen en samling av underkataloger. Som i andre områder av Subversion-designet er modularitet gitt stor oppmerksomhet, og hierarkisk organisering er foretrukket framfor rotete kaos. Her er en rask beskrivelse av alle elementene som du ser i den nye depotkatalogen:
En katalog som inneholder konfigurasjonsfiler for depotet.
En katalog der Apache og mod_dav_svn lagrer sine husholdningsdata.
Hvor alle dine versjonerte data ligger. Denne katalogen er enten et Berkeley DB-miljø (full av databasetabeller og andre ting), eller i et FSFS-miljø som inneholder revisjonsfiler.
En fil som inneholder en enkelt heltallsverdi som spesifiserer versjonsnummeret til depotlayouten.
En katalog full av maler for påhakningsskript (Engelsk: hooks) (og påhakningsskriptene selv, når du har installert noen).
En katalog for Subversions låsedata, brukt for å følge tilganger til depotet.
En fil som bare informerer sine lesere om at de ser på et Subversiondepot.
Generelt sett skal du ikke fikle med depotet “for hånd”. svnadmin-verktøyet skal være nok til alle nødvendige forandringer i depotet, eller du kan se på tredjeparti-verktøy (som Berkeley DBs verktøysamling) for å finpusse relevante subseksjoner av depotet. Noen unntak gjelder imidlertid, og disse skal vi gå gjennom her.
Et påhakningsskript (på engelsk kalt hooks) er et program som blir utløst av en hendelse i depotet, som for eksempel opprettelsen av en ny revisjon eller modifiseringen av en uversjonert egenskap. Hver påhakning blir gitt nok informasjon til å fortelle hva denne hendelsen er, hvilke mål den opererer på, og brukernavnet til personen som satte i gang hendelsen. Avhengig av påhakningens utdata eller returstatus, kan påhakningsprogrammet fortsette hendelsen, stoppe den, eller utsette den på en eller annen måte.
hooks-katalogen er, som standard, fylt med maler for diverse depotpåhakninger.
$ ls depot/hooks/ post-commit.tmpl pre-revprop-change.tmpl post-revprop-change.tmpl start-commit.tmpl pre-commit.tmpl
Det er en mal for hver påhakning som Subversiondepotet implementerer, og ved å undersøke innholdet til disse eksempelskriptene, kan du se hva som fører til at hvert av disse skriptene begynner å kjøre og hvilke data som blir levert til dette skriptet. Det som også ligger i mange av disse malene er eksempler på hvordan man kan bruke skriptet, i sammenheng med andre Subversion-støttede programmer, for å utføre vanlige, nyttige oppgaver. For å faktisk installere en påhakning som virker, trenger du bare å plassere et kjørbart program eller skript i depot/hooks-katalogen som kan bli kjørt basert på navnet (som for eksempel start-commit eller post-commit) til påhakningen.
På Unix-plattformer betyr dette å legge inn et skript eller program (som kan være et shellskript, et Pythonprogram, et kompilert C-program, eller hva som helst annet) som heter akkurat det samme som påhakningen. Selvfølgelig, malfilene er der for mer enn bare informasjonsverdien – den letteste måten å installere en påhakning på Unixplattformer er å bare kopiere den riktige malfilen til en ny fil som mangler .tmpl-etternavnet, finpusse på innholdet, og forsikre deg om at skriptet er kjørbart. Men Windows bruker filetternavn til å avgjøre hvorvidt et program er kjørbart eller ikke, så da må du legge inn et program der hovednavnet er navnet på påhakningen, og der etternavnet er en av de spesielle etternavnene som Windows kjenner igjen som kjørbare, for eksempel .exe eller .com for programmer, og .bat for batchfiler.
Av sikkerhetsgrunner kjører Subversion påhakningsskript med et tomt miljø (environment) – det betyr, ingen miljøvariabler er satt i det hele tatt, ikke engang $PATH eller %PATH%. På grunn av dette blir mange adminstratorer forvirret når påhakningsskriptet kjører fint for hånd, men virker ikke når det blir kjørt av Subversion. Pass på å eksplisitt sette variabler i skriptet og/eller bruk absolutte stier til programmer.
Foreløpig er det fem påhakninger som er lagt inn i Subversiondepotet:
Denne blir kjørt allerede før innleggingstransaksjonen er opprettet. Det blir vanligvis brukt til å avgjøre om brukeren har innleggingsprivilegier i det hele tatt. Depotet leverer to argumenter til dette programmet: Stien til depotet, og brukernavnet som forsøker seg på innleggingen. Hvis programmet returnerer en verdi som ikke er null, blir denne innleggingen stoppet allerede før transaksjonen blir laget. Hvis påhakningen skriver data til stderr, vil det bli sendt tilbake til klienten.
Dette blir kjørt når transaksjonen er komplett, men før den er lagt inn. Vanligvis blir denne påhakningen brukt til å beskytte mot innlegginger som er forbudt på grunn av innhold eller beliggenhet (for eksempel kan tjeneren kreve at alle innlegginger til en spesiell gren inneholder et billettnummer fra feildatabasen, eller at den innkommende loggmeldingen ikke er tom). Depotet leverer to argumenter til dette programmet: Stien til depotet, og navnet på transaksjonen som legges inn. Hvis programmet returnerer en verdi som ikke er null, avbrytes innleggingen og transaksjonen blir fjernet. Hvis programmet skriver data til stderr, vil dette bli sendt tilbake til klienten.
Subversiondistribusjonen inneholder noen aksesskontrollskript (i tools/hook-scripts-katalogen i kildekodefiltreet til Subversion) som kan bli kalt fra pre-commit for å kunne legge inn finjustert kontroll for skriveaksess. En annen mulighet er å bruke Apache-modulen mod_authz_svn, som gir både lese- og skrivekontroll i individuelle kataloger (se “Per-Directory Access Control”). I en fremtidig versjon av Subversion planlegger vi å legge inn lister for aksesskontroll (ACL) direkte inn i filsystemet.
Dette blir kjørt etter at transaksjonen er lagt inn, og en ny revisjon er opprettet. De fleste vil bruke denne påhakningen til å sende ut beskrivende emailer om innleggingen eller for å ta backup av depotet. Depotet gir to argumenter til dette programmet: Stien til depotet, og det nye revisjonsnummeret som ble opprettet. Returverdien fra programmet blir ignorert.
Subversiondistribusjonen inneholder skriptene mailer.py og commit-email.pl (i tools/hook-scripts/-katalogen i Subversiontreet) som kan bli brukt til å sende email med (og/eller legge til en loggfil) en beskrivelse av en mottatt innlegging. Denne mailen inneholder en liste over de stiene som ble forandret, loggmeldingen som er vedlagt innleggingen, forfatteren og datoen til innleggingen, sammen med en GNU diff-visning av forandringene som ble gjort til diverse filer som del av innleggingen.
Et annet nyttig verktøy som følger med Subversion er skriptet hot-backup.py (i tools/backup/-katalogen i kildekodetreet til Subversion). Dette skriptet utfører varme backuper av Subversiondepotet (en funksjonalitet støttet av den bakenforliggende Berkeley DB-databasen), og kan bli brukt til å foreta en øyeblikkslagring av depotet mellom hver innlegging for arkiverings- eller sikkerhetsformål.
Fordi Subversions revisjonsegenskaper ikke er versjonerte, vil det å forandre slike egenskaper (for eksempel svn:log-egenskapen som inneholder loggmeldingen for innleggingen) overskrive den forrige verdien for alltid. Siden data kan gå tapt her, har Subversion denne påhakningen (og dens motstykke post-revprop-change) så depotadministratorer kan lage logger med forandringer til disse elementene ved bruk av eksterne hjelpemidler hvis de vil det. Som en forholdsregel mot å miste uversjonerte data, vil ikke Subversionklienter få lov til å forandre revisjonsegenskaper i det hele tatt hvis ikke denne påhakningen er aktivisert i depotet ditt.
Denne påhakningen kjører like før en slik modifisering blir lagt inn i depotet. Depotet leverer fire argumenter til denne påhakningen: Stien til depotet, revisjonen som egenskapen som skal forandres befinner seg på, det godkjente brukernavnet til personen som gjør forandringen, og navnet på selve egenskapen.
Som nevnt tidligere, er denne påhakningen motstykket til pre-revprop-change-påhakningen. Faktisk, for paranoiaens skyld vil ikke dette skriptet kjøre uten at pre-revprop-change-skriptet finnes. Når begge disse skriptene finnes, kjører post-revprop-change-påhakningen like etter en revisjonsegenskap er blitt forandret, og blir vanligvis brukt til å sende en mail som inneholder den nye verdien til den forandrede egenskapen. Depotet leverer fire argumenter til denne påhakningen: Stien til depotet, revisjonen som egenskapen tilhører, det godkjente brukernavnet til personen som gjør forandringen, og navnet på selve egenskapen.
Subversiondistribusjonen inkluderer skriptet propchange-email.pl (i tools/hook-scripts/-katalogen i Subversions kildekodetre) som kan bli brukt til å sende mail med (og/eller legge til en loggfil) detaljene om en egenskapsforandring. Denne mailen inneholder revisjonen og navnet til den forandrede egenskapen, brukeren som gjorde forandringen, og den nye verdien til egenskapen.
Ikke forsøk å forandre transaksjonen ved å bruke påhakningsskript. Et vanlig eksempel på dette ville være å automatisk sette egenskaper som svn:eol-style eller svn:mime-type under innleggingen. Selv om dette kan se ut som en god idé, fører det til problemer. Hovedproblemet er at klienten ikke vet om forandringen gjort av påhakningsscriptet, og det er ingen måte å informere klienten om at den er utdatert. Denne inkonsekventheten kan føre til overraskende og uventet oppførsel.
Istedenfor å forsøke å modifisere transaksjonen, er det mye bedre å sjekke transaksjonen i pre-commit og nekte innlegging hvis den ikke møter de ønskede kravene.
Subversion vil forsøke å kjøre påhakningsskriptene som den samme brukeren som eier prosessen som aksesserer Subversiondepotet. I de fleste tilfeller blir depotet aksessert via Apache HTTP-tjeneren og mod_dav_svn, så denne brukeren er den samme som Apache kjører som. Selve påhakningen må bli satt opp med rettigheter på operativsystemnivå som tillater denne brukeren å kjøre dem. Dette betyr også at enhver fil eller program (inkludert selve Subversiondepotet) aksessert direkte eller indirekte av påhakningen vil bli aksessert som den samme brukeren. Med andre ord, vær obs på mulig rettighetsproblematikk som kan forhindre påhakningsskriptet fra å utføre de oppgavene du har satt det til.
Et Berkeley DB-miljø er en innkapsling av en eller flere databaser, loggfiler, regionfiler og konfigurasjonsfiler. Berkeley DB-miljøet har sitt eget sett med konfigurasjonsverdier for ting som antallet låser som er tillatt brukt på en gang, maksimal størrelse på journalloggfiler og så videre. Subversions filsystemkode velger i tillegg standardverdier for noen av konfigurasjonsvalgene i Berkeley DB.
Folkene i Sleepycat (produsentene av Berkeley DB) innser at forskjellige databaser har forskjellige krav, og de har derfor lagt inn en mekanisme for å overstyre mange av konfigurasjonsverdiene til Berkeley DB under kjøring. Berkeley sjekker at filen DB_CONFIG finnes i hver miljøkatalog, og leser valgene som er i denne filen for bruk med akkurat dette Berkeley-miljøet.
Berkeley-konfigurasjonsfilen for ditt depot ligger i miljøkatalogen db i depot/db/DB_CONFIG. Subversion oppretter selv denne filen når den lager resten av depotet. Filen inneholder innledningsvis noen standardvalg sammen med pekere til dokumentasjonen til Berkeley DB som ligger online så du kan lese om hva disse valgene gjør. Selvfølgelig kan du legge til alle støttede Berkeley DB-valg til DB_CONFIG-filen. Pass bare på at mens Subversion aldri prøver å lese eller forstå innholdet i filen og bruker ingen av valgene i den, vil du unngå å gjøre forandringer i konfigurasjonen som får Berkeley DB seg til å oppføre seg annerledes enn det Subversionkoden forventer. I tillegg vil ikke forandringer gjort i DB_CONFIG aktiviseres før du gjenoppretter databasemiljøet (ved bruk av svnadmin recover.
Å vedlikeholde et Subversiondepot kan være en skremmende oppgave, mest på grunn av kompleksiteten innebygget i systemer som har en bakenforliggende database. Å utføre oppgaven godt handler om å kjenne verktøyene – hva de er, når de skal brukes, og hvordan de brukes. Denne seksjonen vil introdusere deg for administrasjonsverktøy som følger med Subversion, og hvordan få dem til å utføre oppgaver som flytting av depot, oppgraderinger, sikkerhetskopier og opprydding.
Subversion har en håndfull programmer nyttige for å opprette, inspisere, modifisere og reparere depotet ditt. La oss se litt nærmere på hvert av disse verktøyene. Etterpå vil vi gå raskt over noen av de programmene som er inkludert i Berkeley DB-distribusjonen som tilbyr funksjonalitet spesifikk til databasedelen i depotet som ikke Subversions egne verktøy tar seg av.
svnlook er et verktøy som følger med Subversion. Det er beregnet for å utforske de forskjellige revisjonene og transaksjonene i et depot. Ingen deler av dette programmet prøver å forandre depotet – det er et “bare for lesing”-verktøy. svnlook blir vanligvis brukt av påhakningene i depotet for å rapportere forandringene som er i ferd med å bli lagt inn (i tilfellet med pre-commit-påhakningen) eller det som nettopp ble lagt inn (i tilfellet med post-commit-påhakningen) i depotet. En depotadministrator kan bruke dette verktøyet for diagnoseformål.
svnlook har en likefrem syntaks:
$ svnlook help
general usage: svnlook SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...]
Note: any subcommand which takes the '--revision' and '--transaction'
options will, if invoked without one of those options, act on
the repository's youngest revision.
Type "svnlook help <subcommand>" for help on a specific subcommand.
…
Nesten hver eneste av delkommandoene til svnlook kan operere på enten et revisjons- eller transaksjonstre og skrive ut informasjon om selve treet, eller hvordan det skiller seg ut fra den forrige revisjonen i depotet. Du bruker valgene --revision og --transaction for å spesifisere henholdsvis hvilken revisjon eller transaksjon som skal undersøkes. Legg merke til at mens revisjonsnumre fremstår som naturlige tall, er transaksjonsnavn alfanumeriske strenger. Husk at filsystemet bare tillater gjennomgang av transaksjoner som enda ikke er lagt inn (transaksjoner som ikke har resultert i en ny revisjon). De fleste depot vil ikke ha noen slike transaksjoner, fordi transaksjoner vanligvis enten er lagt inn (som diskvalifiserer dem fra visning) eller avbrutt og fjernet.
Hvis både valgene --revision og --transaction mangler, vil svnlook undersøke den yngste (eller “HEAD”) revisjonen i depotet. Så de følgende to kommandoene gjør akkurat det samme når 19 er den yngste revisjonen i depotet som ligger i /sti/til/depot:
$ svnlook info /sti/til/depot $ svnlook info /sti/til/depot --revision 19
Det eneste unntaket fra disse reglene om delkommandoer er svnlook youngest-delkommandoen, som ikke tar noen valg, og bare skriver ut nummeret for HEAD-revisjonen.
$ svnlook youngest /sti/til/depot 19
Utdata fra svnlook er designet for å være både for menneske og maskin. Som et eksempel kan vi ta utdataene fra delkommandoen info:
$ svnlook info /sti/til/depot sally 2002-11-04 09:29:13 -0600 (Mon, 04 Nov 2002) 27 Added the usual Greek tree.
Utdataene fra info-delkommandoen er definert som:
Forfatteren, etterfulgt av linjeslutt.
Datoen, etterfulgt av linjeslutt.
Antallet tegn i loggmeldingen, etterfulgt av linjeslutt.
Selve loggmeldingen, etterfulgt av linjeslutt.
Disse utdataene er leselige for det menneskelige øye, det vil si at elementer som datoen blir vist som tekst istedenfor noe mer obskurt (som antall nanosekunder siden “Tasy Freeze”-fyren kjørte forbi). Men disse utdataene er også lesbare for en datamaskin – fordi loggmeldingen kan inneholde flere linjer og ha ubegrenset lengde, skriver svnlook lengden på denne meldingen før selve meldingen. Dette tillater skript og andre programmer som bruker denne kommandoen å gjøre intelligente valg angående loggmeldingen, som hvor mye hukommelse som skal reserveres for meldingen, eller i det minste hvor mange byte som må hoppes over når disse utdataene ikke er den siste datadelen i strømmen.
En annen vanlig bruk av svnlook er å faktisk vise innholdet i et revisjons- eller transaksjonstre. Kommandoen svnlook tree viser katalogene og filene i det forespurte treet. Hvis du angir valget --show-ids, vil den også vise noderevisjons-ID for hver av disse stiene (som vanligvis er mer nyttige for utviklere enn vanlige brukere).
$ svnlook tree /sti/til/depot --show-ids
/ <0.0.1>
A/ <2.0.1>
B/ <4.0.1>
lambda <5.0.1>
E/ <6.0.1>
alpha <7.0.1>
beta <8.0.1>
F/ <9.0.1>
mu <3.0.1>
C/ <a.0.1>
D/ <b.0.1>
gamma <c.0.1>
G/ <d.0.1>
pi <e.0.1>
rho <f.0.1>
tau <g.0.1>
H/ <h.0.1>
chi <i.0.1>
omega <k.0.1>
psi <j.0.1>
iota <1.0.1>
Når du har sett på layouten for kataloger og filer i treet ditt, kan du bruke kommandoer som svnlook cat, svnlook propget og svnlook proplist for å grave dypere inn i detaljene om disse filene og katalogene.
svnlook kan utføre en rekke andre forespørsler, vise delsett av biter av informasjon vi tidligere har nevnt, rapportere hvilke stier som ble modifisert i en gitt revisjon eller transaksjon, vise tekst- og egenskapsforskjeller gjort med filer og kataloger og så videre. Det følgende er en rask beskrivelse av den nåværende listen med delkommandoer som svnlook aksepterer, og utdataene fra disse delkommandoene:
Skriv treets forfatter.
Skriv innholdet av en fil i treet.
List alle filer og kataloger som forandret seg i treet.
Skriv dato og klokkeslett for treet.
Lag unified diff (forskjellsfil) av forandrede filer.
List katalogene i treet som enten selv forandret seg, eller der en fil under den forandret seg.
Vis interessante punkter i historien for en versjonert sti (steder der modifiseringer eller kopieringer skjedde).
Skriv treets forfatter, tidspunkt, antall tegn i loggmeldingen og selve loggmeldingen.
Skriv treets loggmelding.
Skriv verdien for en egenskap på en sti i treet.
Skriv navnene og verdiene av egenskapene satt på stier i treet.
Skriv en liste over treet, med valgfri visning av filsystemets noderevisions-ID assosiert med hver sti.
Skriv depotets UUID – Universal Unique IDentifier.
Skriv det yngste revisjonsnummeret.
Programmet svnadmin er depotadministratorens beste venn. Ved siden av å gi muligheten til å opprette Subversiondepot, tillater dette programmet deg å utføre flere vedlikeholdsoperasjoner på disse depotene. Syntaksen på svnadmin er lik den i svnlook:
$ svnadmin help general usage: svnadmin SUBCOMMAND REPOS_PATH [ARGS & OPTIONS ...] Type "svnadmin help <subcommand>" for help on a specific subcommand. Available subcommands: create deltify dump help (?, h) …
Vi har allerede nevnt delkommandoen create i svnadmin (se “Opprettelse og konfigurering av depotet”). De fleste andre vil vi gå grundigere gjennom senere i dette kapitlet. Nå nøyer vi oss med å ta en rask kikk på hva de forskjellige delkommandoene tilbyr.
Opprett et nytt Subversiondepot.
Gå over et spesifisert revisjonsområde og utfør forløper-deltifisering på stiene som forandret seg i disse revisjonene. Hvis ingen revisjoner er spesifisert, vil denne kommandoen rett og slett bare deltifisere HEAD-revisjonen.
Dump innholdet av depotet, begrenset av et spesifisert sett av revisjoner, i et portabelt dumpformat.
Lag en “varm kopi” av et depot. Du kan kjøre denne kommandoen til enhver tid og lage en trygg kopi av depotet, selv om andre prosesser bruker depotet.
(Bare Berkeley DB-depot.) List stiene for Berkeley DB-loggfiler assosiert med depotet. Denne listen inkluderer alle loggfiler ― de som fortsatt brukes av Subversion, sammen med de som ikke lenger er i bruk.
(Bare Berkeley DB-depot.) List stiene for Berkeley DB-loggfiler assosiert med, men ikke lenger brukt av depotet. Du kan trygt fjerne disse loggfilene fra depotet og muligens arkivere dem for senere bruk i tilfelle du en gang får bruk for katastrofe-gjenoppretting av depotet.
Last et sett revisjoner inn i et depot fra en strøm av data som bruker det samme portable dumpformatet generert av dump-delkommandoen.
List navnene på Subversiontransaksjonene som ikke er lagt inn, men som eksisterer i depotet.
Utfør gjenoppretting på et depot som trenger dette, vanligvis etter at en fatal feil er oppstått som forhindret en prosess fra å lukke kommunikasjonen med depotet på en skikkelig måte.
Fjern Subversiontransaksjoner fra depotet på en renslig måte (kan bruke utdataene fra lstxns-delkommandoen).
Erstatt den nåværende verdien i svn:log-egenskapen (loggmelding) for en angitt revisjon i depotet med en ny verdi.
Verifiser innholdet i et depot. Dette inkluderer blant annet sjekk av kontrollsummer til de versjonerte dataene som er lagret i depotet.
Siden Subversion lagrer alt i et skjult databasesystem, er det å prøve på manuelle forandringer ikke spesielt lurt, for ikke å si vanskelig. Og når dataene er lagret i depotet, har ikke Subversion noen enkel måte å fjerne disse dataene.[13] Men det er ikke til å komme forbi at det er noen ganger du vil manipulere historien til depotet ditt. Det kan være at du vil fjerne alle spor etter en fil som ble lagt til ved en ulykke (og som av en eller annen grunn ikke skal være der). Eller du har kanskje flere prosjekter som deler et enkelt depot, og du bestemmer deg for å dele dem opp i sine egne depot. For å utføre oppgaver som dette, trenger administratorer en mer håndterlig og formbar representasjon av dataene i deres egne depot – dumpformatet for Subversiondepot.
Dumpformatet for Subversiondepot er en representasjon av forandringene som du har gjort i dine versjonerte data over tid, i et menneskelig lesbart format. Du bruker kommandoen svnadmin dump for å generere dumpdataene, og svnadmin load for å legge disse dataene inn i et nytt depot (se “Flytte et depot”). Den fine tingen med det menneskelesbare dumpformatet er at du kan inspisere og modifisere det hvis du er forsiktig. Selvfølgelig, bakdelen er at hvis du har to år med depotaktivitet pakket inn i en stor dumpfil, vil det ta deg lang, lang tid å manuelt inspisere og modifisere den.
Selv om det ikke vil være det vanligste verktøyet til bruk for depotadministratoren, git svndumpfilter en veldig spesiell type nyttig funksjonalitet – muligheten til å raskt og enkelt modifisere disse dumpdataene bet å fungere som et stibasert filter. Du gir programmet ganske enkelt en liste over stier som du vil beholde, eller en liste med stier som du vil fjerne, og sender deretter dumpdataene fra depotet via et rør gjennom dette filteret. Resultatet er en modifisert strøm av dumpdata som kun inneholder de versjonerte stiene som du (eksplisitt eller implisitt) ba om.
Syntaksen for svndumpfilter er som følger:
$ svndumpfilter help general usage: svndumpfilter SUBCOMMAND [ARGS & OPTIONS ...] Type "svndumpfilter help <subcommand>" for help on a specific subcommand. Available subcommands: exclude include help (?, h)
Her er det bare to interessante delkommandoer. De gir deg valget mellom eksplisitt eller implisitt inkludering av stier i strømmen:
Filtrer bort et sett med stier fra dumpdatastrømmen.
Tillat bare de angitte stiene å slippe gjennom dumpdatastrømmen.
La oss se på et realistisk eksempel på hvordan du vil bruke dette programmet. Vi diskuterer en annen plass (se “Velge en depot-layout”) prosessen med å bestemme deg for hvordan dataene i depotene dine skal legges opp – om du vil bruke ett depot for hvert prosjekt eller kombinere dem, hvordan du arrangerer ting innenfor depotet og så videre. Men noen ganger etter at nye revisjoner begynner å komme inn, tenker du på nytt over layouten og vil gjøre noen forandringer. En vanlig forandring er å bestemme seg for å flytte flere prosjekter som deler et enkelt depot inn i separate depot for hvert prosjekt.
Vårt hypotetiske depot inneholder tre prosjekter: calc, calendar og spreadsheet. De har levd ved siden av hverandre som følger:
/
calc/
trunk/
branches/
tags/
calendar/
trunk/
branches/
tags/
spreadsheet/
trunk/
branches/
tags/
For å få disse tre prosjektene inn i deres egne depot, dumper vi først hele depotet:
$ svnadmin dump /path/to/repos > repos-dumpfile * Dumped revision 0. * Dumped revision 1. * Dumped revision 2. * Dumped revision 3. … $
Så kjører vi denne dumpfilen gjennom filteret og for hver gang inkluderer vi bare en av katalogene på toppnivå, noe som resulterer i tre nye dumpfiler:
$ cat repos-dumpfile | svndumpfilter include calc > calc-dumpfile … $ cat repos-dumpfile | svndumpfilter include calendar > cal-dumpfile … $ cat repos-dumpfile | svndumpfilter include spreadsheet > ss-dumpfile … $
På dette punktet må du ta en avgjørelse. Hver av dumpfilene fine vil lage et gyldig depot, men vil gjenskape stiene nøyaktig som de var i det opprinnelige depotet. Dette betyr at selv om du vil ha et depot kun for calc-prosjektet, vil dette depotet fortsatt ha en toppkatalog kalt calc. Hvis du vil at katalogene trunk, tags og branches skal være i roten av depotet, vil du kanskje ønske å redigere dumpfilene og forandre Node-path og Copyfrom-path i headerne så de ikke lengre inneholder denne calc/-komponenten i stien. I tillegg ønsker du å fjerne seksjonen i dumpdataene som oppretter calc-katalogen. Det vil se ut omtrent som dette:
Node-path: calc Node-action: add Node-kind: dir Content-length: 0
Hvis du planlegger å redigere dumpfilen manuelt for å fjerne en toppkatalog, vær sikker på at tekstbehandleren din ikke er satt til å automatisk konvertere linjeslutt til det lokale formatet (det vil si \r\n til \n). Dette vil føre til at innholdet ikke vil samsvare med metadataene og dumpfilen vil dermed bli ubrukelig.
Alt som gjenstår nå er å opprette dine tre nye depot og laste hver dumpfil inn i det riktige depotet:
$ svnadmin create calc; svnadmin load calc < calc-dumpfile
<<< Started new transaction, based on original revision 1
* adding path : Makefile ... done.
* adding path : button.c ... done.
…
$ svnadmin create calendar; svnadmin load calendar < cal-dumpfile
<<< Started new transaction, based on original revision 1
* adding path : Makefile ... done.
* adding path : cal.c ... done.
…
$ svnadmin create spreadsheet; svnadmin load spreadsheet < ss-dumpfile
<<< Started new transaction, based on original revision 1
* adding path : Makefile ... done.
* adding path : ss.c ... done.
…
$
Begge delkommandoene til svndumpfilter godtar valg for å bestemme hva de skal gjøre med “tomme” revisjoner. Hvis en gitt revisjon bare inneholdt forandringer i stier som ble filtrert bort kan denne revisjonen som nå er tom bli betraktet som uinteressant eller til og med uønsket. Så for å gi brukeren kontroll over hva som skal gjøres med disse revisjonene, har svndumpfilter disse kommandolinjevalgene:
Ikke lag tomme revisjoner i det hele tatt – utelat dem.
Hvis tomme revisjoner er droppet (ved bruk av valget --drop-empty-revs), forandres revisjonsnumrene på de gjenværende revisjonene så det ikke blir mellomrom i den numeriske sekvensen.
Hvis tomme revisjoner ikke droppes, ta vare på revisjonsegenskaper (loggmelding, forfatter, dato, egendefinerte egenskaper og så videre) for disse tomme revisjonene. Eller vil tomme revisjoner bare inneholde det originale tidspunktet og en generert loggmelding som indikerer at denne revisjonen ble tømt av svndumpfilter.
Selv om svndumpfilter kan være veldig nyttig og sparer masse tid, er det dessverre et par ting å passe på. For det første er dette verktøyet overfølsom for sti-semantikk. Legg merke til om stiene i dumpfilen er spesifisert med eller uten skråstreker i starten. Dette vil du se i Node-path og Copyfrom-path i headerne.
… Node-path: spreadsheet/Makefile …
Hvis stiene har innledende skråstreker, skal du også ha innledende skråstreker i stiene som du leverer til svndumpfilter include og svndumpfilter exclude (og hvis de ikke har det, skal du ikke gjøre det). Videre, hvis dumpfilen din har inkonsekvent bruk av innledende skråstreker av en eller annen grunn,[14] bør du normalisere disse stiene så de alle enten har eller mangler innledende skråstreker.
I tillegg kan kopierte stier gi deg litt trøbbel. Subversion støtter kopioperasjoner i depotet, der en ny sti blir opprettet ved å kopiere en allerede eksisterende sti. Det er mulig at på et eller annet punkt i livsløpet til depotet blir en fil eller katalog kopiert fra en plass som svndumpfilter utelater, til en plass som programmet inkluderer. For å gjøre dumpdataene selvforsynt, må svndumpfilter vise tilleggingen av den nye stien – inkludert innholdet av alle filer opprettet av kopieringen – og ikke vise denne tilleggingen som en kopiering fra en kilde som ikke eksisterer i den filtrerte dumpdatastrømmen. Men fordi dumpformatet i Subversion bare viser hva som ble forandret i hver revisjon, kan det være at innholdet i kilden for kopien ikke er tilgjengelig. Hvis du har mistanke om at du har denslags kopier i depotet ditt, vil du kanskje tenke gjennom valget ditt av inkluderte/utelatte stier på nytt.
Kildekodetreet til Subversion kommer også med et skall-lignende grensesnitt til depotet. Python-skriptet svnshell.py (lokalisert i tools/examples/ i kildekodetreet) bruker Subversions språkbindinger (så du må ha disse skikkelig kompilert og installert for at dette skriptet skal virke) for å koble seg til depot- og filsystembibliotekene.
Når det er startet, oppfører programmet som likt et skall og lar deg bla gjennom de forskjellige katalogene i depotet ditt. I starten er du “plassert” i rotkatalogen for HEAD-revisjonen i depotet, og du får en kommandolinje. Du kan bruke help-kommandoen til enhver tid for å vise en liste over tilgjengelige kommandoer og hva de gjør.
$ svnshell.py /sti/til/depot <rev: 2 />$ help Available commands: cat FILE : dump the contents of FILE cd DIR : change the current working directory to DIR exit : exit the shell ls [PATH] : list the contents of the current directory lstxns : list the transactions available for browsing setrev REV : set the current revision to browse settxn TXN : set the current transaction to browse youngest : list the youngest browsable revision number <rev: 2 />$
Navigering i katalogstrukturen til depotet ditt gjøres på samme måten som du gjør i et vanlig Unix- eller Windowsskall – ved å bruke kommandoen cd. Kommandolinjeprompten vil til enhver tid vise deg hvilken revisjon (inneholder forstavelsen rev:) eller transaksjon (inneholder forstavelsen txn:) du undersøker for øyeblikket, og stiplasseringen i denne revisjonen eller transaksjonen. Du kan forandre din nåværende revisjon eller transaksjon med henholdsvis setrev- og settxn-kommandoen. Som i et Unixskall kan du bruke kommandoen ls for å vise innholdet av den nåværende katalogen, og du kan bruke cat-kommandoen for å vise innholdet av en fil.
Eksempel 5.1. Bruk av svnshell for å navigere i depotet
<rev: 2 />$ ls
REV AUTHOR NODE-REV-ID SIZE DATE NAME
----------------------------------------------------------------------------
1 sally < 2.0.1> Nov 15 11:50 A/
2 harry < 1.0.2> 56 Nov 19 08:19 iota
<rev: 2 />$ cd A
<rev: 2 /A>$ ls
REV AUTHOR NODE-REV-ID SIZE DATE NAME
----------------------------------------------------------------------------
1 sally < 4.0.1> Nov 15 11:50 B/
1 sally < a.0.1> Nov 15 11:50 C/
1 sally < b.0.1> Nov 15 11:50 D/
1 sally < 3.0.1> 23 Nov 15 11:50 mu
<rev: 2 /A>$ cd D/G
<rev: 2 /A/D/G>$ ls
REV AUTHOR NODE-REV-ID SIZE DATE NAME
----------------------------------------------------------------------------
1 sally < e.0.1> 23 Nov 15 11:50 pi
1 sally < f.0.1> 24 Nov 15 11:50 rho
1 sally < g.0.1> 24 Nov 15 11:50 tau
<rev: 2 /A>$ cd ../..
<rev: 2 />$ cat iota
This is the file 'iota'.
Added this text in revision 2.
<rev: 2 />$ setrev 1; cat iota
This is the file 'iota'.
<rev: 1 />$ exit
$
Som du ser i det forrige eksempelet kan flere kommandoer bli spesifisert på en enkelt kommandolinje, separert med et semikolon. I tillegg forstår skallet relative og absolutte stier og vil behandle stikomponentene . og .. på en skikkelig måte.
youngest-kommandoen viser den yngste revisjonen. Dette er nyttig for å anslå området av gyldige revisjoner du kan bruke som argumenter til setrev-kommandoen – du har lov til å bla gjennom alle revisjonene (husk at de benevnes med heltall) mellom 0 til og med den yngste. Å anslå de gyldige transaksjonene som du kan bla gjennom er ikke like enkelt. Bruk lstxns-kommandoen for å liste transaksjonene som du kan bla gjennom. Listen med transaksjoner som du kan bla gjennom er den samme som svnadmin lstxns returnerer, og den samme listen som er gyldig for bruk sammen med svnlook-valget --transaction.
Når du er ferdig med å bruke skallet, kan du trygt avslutte ved å bruke exit-kommandoen. Alternativt kan du skrive et filslutt-tegn – Ctrl-d (selv om noen Win32-distribusjoner av Python bruker Windows-varianten Ctrl-z istedenfor).
Hvis du bruker et Berkeley DB-depot, vil alle strukturer og data være i et sett med databasetabeller i katalogen db i depotet ditt. Denne underkatalogen er en vanlig Berkeley DB miljøkatalog, og kan derfor bli brukt i forbindelse med alle Berkeley databaseverktøy (du kan se dokumentasjonen for disse verktøyene på SleepyCats hjemmeside, http://www.sleepycat.com/).
For daglig Subversionbruk er disse verktøyene unødvendige. Mesteparten av funksjonaliteten som vanligvis er nødvendig for Subversiondepot er blitt duplisert i svnadmin-verktøyet. For eksempel, svnadmin list-unused-dblogs og svnadmin list-dblogs utfører en del av hva Berkeley-kommandoen db_archive gjør, og svnadmin recover gjenspeiler de vanlige bruksmåter for db_recover-programmet.
Det er fortsatt noen få Berkeley DB-programmer som du kan finne nyttige. Programmene db_dump og db_load skriver og leser henholdsvis et tilpasset filformat som beskriver nøkler og verdier i en Berkeley DB-database. Siden Berkeley DB-databaser ikke er portable på kryss av maskinarkitekturer, er dette formatet en nyttig måte å overføre disse databasene fra maskin til maskin, uavhengig av arkitektur eller operativsystem. I tillegg kan programmet db_stat gi deg nyttig informasjon om statusen til Berkeley DB-miljøet, inkludert detaljert statistikk om undersystemer som tar seg av låsing og lagring.
Subversiondepotet ditt vil vanligvis kreve veldig lite tilsyn når det er konfigurert sånn som du vil ha det. Det er imidlertid enkelte ganger en administrator må trå til med manuell assistanse. Verktøyet svnadmin gir nyttig funksjonalitet for å hjelpe deg med å utføre oppgaver som
modifisere loggmeldinger
fjerne døde transaksjoner
gjenopprette “fastkilte” depot, og
flytte depotinnholdet til et annet depot.
Den delkommandoen som kanskje blir brukt mest i svnadmin er setlog. Når en transaksjon er lagt inn i depotet og forfremmet til en revisjon, blir den beskrivende loggmeldingen assosiert med denne nye revisjonen lagret som en uversjonert egenskap vedlagt selve revisjonen. Med andre ord, depotet husker bare den seneste verdien til egenskapen, og forkaster tidligere versjoner.
Noen ganger skjer det at det blir feil i en loggmelding (en skrivefeil eller kanskje noe feilinformasjon). Hvis depotet er satt opp (ved bruk av påhakningene pre-rev-prop-change og post-revprop-change; se “Påhakningsskript”) til å akseptere forandringer i denne loggmeldingen etter at innleggingen er ferdig, kan brukeren “fikse” loggmeldingen fra en annen maskin ved bruk av svn-programmets propset-kommando (se Kapittel 9, Subversion Complete Reference). Men fordi det her er en potensiell sjanse for å miste informasjon for alltid, er standard oppsett på et Subversiondepot å ikke godta forandringer til uversjonerte egenskaper – unntatt av en administrator.
Hvis en loggmelding må forandres av en administrator, kan dette gjøres ved å bruke svnadmin setlog. Denne kommandoen forandrer loggmeldingen (svn:log-egenskapen) på en gitt revisjoen i et depot, ved å lese den nye verdien fra en fil.
$ echo "Her er den nye, korrekte loggmeldingen" > nylogg.txt $ svnadmin setlog mittdepot nylogg.txt -r 388
Kommandoen svnadmin setlog alene er fortsatt bundet av den samme beskyttelsen mot å modifisere uversjonerte egenskaper som en klient på en annen maskin er – påhakningsskriptene pre-revprop-change og post-revprop-change blir fortsatt aktivisert og må derfor settes opp til å godta denne typen forandringer. Men en administrator kan gå rundt denne beskyttelsen ved å angi valget --bypass-hooks til svnadmin setlog-kommandoen.
Men husk at ved å omgå påhakningsskriptene unngår du også utføring av ting som meldinger via email om forandringer i egenskaper, backupsystemer som følger med på egenskapsforandringer og så videre. Med andre ord, vær meget forsiktig med hva du forandrer og hvordan du forandrer det.
En annen vanlig måte å bruke svnadmin på er å spørre depotet om utestående – muligens døde – Subversiontransaksjoner. Hvis en innlegging feiler, blir transaksjonen vanligvis ryddet opp i. Det vil si, selve transaksjonen blir fjernet fra depotet, og alle dataene assosiert med (og bare med) denne transaksjonen blir fjernet i samme slengen. Men enkelte ganger kan det skje en feil på en måte som gjør at oppryddingen av transaksjonen aldri skjer. Dette kan skje av flere grunner: Kanskje ble klientoperasjonen uelegant avsluttet av brukeren, eller en nettverksfeil kan ha skjedd midt i en operasjon og så videre. Uansett grunnen, disse døde transaksjonene er bare skrot i depotet og tar opp ressurser.
Du kan bruke svnadmins lstxns-kommando for å liste navnene på utestående transaksjoner som ligger i depotet for øyeblikket.
$ svnadmin lstxns mittdepot 19 3a1 a45 $
Hvert element i de resulterende utdataene kan deretter bli brukt sammen med svnlook (og dens valg --transaction) for å finne ut hvem som lagde transaksjonen, når den ble laget, hvilke typer forandringer som ble gjort i transaksjonen – med andre ord, om transaksjonen er eller ikke er en trygg kandidat for sletting! Hvis den er det, kan transaksjonens navn leveres til svnadmin rmtxns som vil utføre fjerning av transaksjonen. rmtxns-delkommandoen kan faktisk motta dataene direkte fra det som lstxns genererer!
$ svnadmin rmtxns mittdepot `svnadmin lstxns mittdepot` $
Hvis du bruker disse to delkommandoene som dette, bør du vurdere å gjøre depotet midlertidig utilgjengelig for klienter. På denne måten kan ingen starte på en legitim transaksjon før du starter på opprenskningen. Det følgende er en skallsnutt som raskt kan generere informasjon om hver utestående transaksjon i depotet ditt:
Eksempel 5.2. txn-info.sh (Rapporterer utestående transaksjoner)
#!/bin/sh
### Generer informasjon om alle utestående transaksjoner i et
### Subversiondepot.
SVNADMIN=/usr/local/bin/svnadmin
SVNLOOK=/usr/local/bin/svnlook
REPOS="${1}"
if [ "x$REPOS" = x ] ; then
echo "bruk: $0 STI_TIL_DEPOT"
exit
fi
for TXN in `${SVNADMIN} lstxns ${REPOS}`; do
echo "---[ Transaksjon ${TXN} ]-------------------------------------------"
${SVNLOOK} info "${REPOS}" --transaction "${TXN}"
done
Du kan kjøre det foregående skriptet ved å bruke /sti/til/txn-info.sh /sti/til/depot. Utdataene er hovedsaklig flere biter av utdataene fra svnlook info som er satt sammen (se “svnlook”), og vil se ut som noe i denne retningen:
$ txn-info.sh mittdepot ---[ Transaksjon 19 ]------------------------------------------- sally 2001-09-04 11:57:19 -0500 (Tue, 04 Sep 2001) 0 ---[ Transaksjon 3a1 ]------------------------------------------- harry 2001-09-10 16:50:30 -0500 (Mon, 10 Sep 2001) 39 Prøver å legge inn over et skrøpelig nettverk. ---[ Transaksjon a45 ]------------------------------------------- sally 2001-09-12 11:09:28 -0500 (Wed, 12 Sep 2001) 0 $
Vanligvis, hvis du ser en død transaksjon som ikke har noen loggmelding vedlagt, er dette et resultat av en oppdateringsoperasjon (eller noe i likhet med en oppdatering) som har feilet. Disse operasjonene bruker Subversiontransaksjoner under panseret for å simulere tilstanden til en arbeidskopi. Siden de aldri er ment å bli lagt inn i depotet, krever ikke Subversion en loggmelding for disse transaksjonene. Transaksjoner som har loggmeldinger vedlagt er omtrent helt sikkert mislykkede innlegginger av en eller annen type. I tillegg kan tidspunktet til en transaksjon bidra med interessant informasjon – for eksempel, hvor sannsynlig er det at en operasjon som begynte for ni måneder siden fortsatt er aktiv?
Kort sagt er det ikke nødvendig å foreta ukloke beslutninger angående fjerning av transaksjoner. Forskjellige kilder til informasjon – inkludert Apaches feil- og tilgangslogg, loggene med gjennomførte Subversioninnlegginger og så videre – kan bli tatt med i beslutningsprosessen. Til sist kan administratoren rett og slett kommunisere med eieren av en tilsynelatende død transaksjon (via email, for eksempel) for å fastslå om transaksjonen faktisk er i en zombietilstand.
Selv om prisen på datalagring har stupt de siste årene, er bruken av diskplass fortsatt noe administratorer må tenke på hvis de vil versjonere store mengder data. Hver eneste ekstra byte konsumert av det aktive depotet er en byte som må tas backup av utenfor maskinen, kanskje flere ganger som del av roterende backuper. Hvis du bruker et Berkeley DB-depot, er den primære lagringsmekanismen et komplekst databasesystem og det er nyttig å vite hvilke deler av dataene som må forbli på den aktive siten, hvilke som det må tas sikkerhetskopi av, og hva som trygt kan fjernes. Denne seksjonen er spesifikk til Berkeley DB, FSFS-depot har ingen ekstra data som kan ryddes opp i eller brukes om igjen.
Inntil nylig var de største synderene når det gjelder diskplass brukt av Subversiondepot loggfilene der Berkeley DB utfører sine forhåndsskrivinger før de aktuelle databasefilene modifiseres. Disse filene inneholder alle operasjonene som blir utført langs ruten å forandre databasen fra en tilstand til en annen – mens databasefilene avspeiler til enhver tid en viss tilstand, inneholder loggfilene alle de mange småforandringene langs veien mellom tilstander. Og derfor kan disse øke i omfang ganske fort.
Heldigvis, fra og med 4.2-versjonen av Berkeley DB har databasemiljøet muligheten til å fjerne sine egne ubrukte loggfiler uten eksterne prosedyrer. Ethvert depot som er laget med en svnadmin som er kompilert mot Berkeley DB 4.2 eller større vil bli konfigurert for denne automatiske slettingen av loggfiler. Hvis du ikke vil ha denne funksjonaliteten aktivisert, kan du ganske enkelt angi valget --bdb-log-keep til svnadmin create-kommandoen. Hvis du glemmer å gjøre dette, eller forandrer mening på et senere tidspunkt, kan du redigere filen DB_CONFIG som ligger i katalogen db i depotet. Du kommenterer bort linjen som inneholder direktivet set_flags DB_LOG_AUTOREMOVE og kjører deretter svnadmin recover på depotet ditt for å aktivisere forandringene i konfigrasjonen. Se “Konfigurasjon av Berkeley DB” for mer informasjon om databasekonfigurasjon.
Uten noen form for automatisk sletting av loggfiler på plass, vil loggfilene samle seg opp etterhvert som du bruker depotet. Dette er faktisk en form for God Ting i databasesystemet – du vil være i stand til å gjenopprette hele databasen ved bruk av loggfilene alene, så disse filene kan være nyttige for gjenoppretting etter en katastrofe. Men vanligvis vil du arkivere loggfilene som ikke lenger er i bruk av Berkeley DB og deretter fjerne dem fra disken for å spare plass. Bruk kommandoen svnadmin list-unused-dblogs for å liste ut de ubrukte loggfilene:
$ svnadmin list-unused-dblogs /sti/til/depot /sti/til/depot//log.0000000031 /sti/til/depot//log.0000000032 /sti/til/depot//log.0000000033 $ svnadmin list-unused-dblogs /sti/til/depot | xargs rm ## diskplassen er frigjort!
For å holde størrelsen på depotet så liten som mulig, bruker Subversion deltifisering (eller “deltifisert lagring”) inne i selve depotet. Deltifisering involverer å kode representasjonen av en porsjon data som en samling av forskjeller mot en annen porsjon data. Hvis de to delene med data er veldig like, resulterer denne deltifiseringen i sparing av lagringsplass for den deltifiserte porsjonen – istedenfor å ta opp plass lik størrelsen av de originale dataene, brukes nå nok plass for å si: “Jeg ser ut akkurat som de dataene der borte, unntatt de følgende forandringene”. Mer spesifikt, hver gang en ny versjon av en fil blir lagt inn i depotet, koder Subversion den forrige versjonen (egentlig flere tidligere versjoner) som en delta mot den nye versjonen. Resultatet er at mesteparten av depotdataene som har en tendens til å forandre seg i størrelse – innholdet av versjonerte filer – blir lagret med en mye mindre datamengde enn den originale “fulltekst”-representasjonen av disse dataene.
Fordi alle Subversions depotdata som blir deltifisert blir lagret i en enkelt Berkeley DB-databasefil, vil ikke nødvendigvis det å redusere størrelsen på de lagrede verdiene nødvendigvis redusere størrelsen på selve databasefilen. Berkeley DB vil imidlertid ha interne poster med ubrukte områder i databasefilen, og vil bruke disse områdene først før den øker størrelsen på databasefilen. Så selv om deltifisering ikke medfører øyeblikkelig sparing av diskplass, kan det redusere fremtidig økning i databasestørrelsen ganske drastisk.
Som nevnt i “Berkeley DB”, kan et Berkeley DB-depot noen ganger bli etterlatt i en frosset tilstand hvis den ikke blir skikkelig lukket. Når dette skjer, må en administrator rulle databasen tilbake i funksjonell stand.
For å beskytte dataene i depotet ditt, bruker Berkeley DB en låsemekanisme. Denne mekanismen forsikrer at deler av databasen ikke blir oppdatert samtidig av flere sin aksesserer databasen, og at hver prosess ser dataene i den korrekte tilstanden som da disse dataene ble lest fra databasen. Når en prosess må forandre noe i databasen sjekker den først om det eksisterer en lås på måldataene. Hvis dataene ikke er låst, låser denne prosessen dataene, gjør forandringene som den ønsker å gjøre, og låser deretter opp dataene. Andre prosesser blir tvunget til å vente inntil denne låsen er fjernet før de får lov til å fortsette med tilgangen til denne seksjonen av databasen.
Under bruk av Subversiondepotet kan fatale feil (som å gå tom for diskplass eller tilgjengelig hukommelse) eller avbrytelser forhindre en prosess fra å fjerne låsene som den har plassert i databasen. Resultatet er at det bakenforliggende databasesystemet blir “fastkilt”. Når dette skjer, vil ethvert forsøk på å aksessere depotet henge til evig tid (siden hver ny tilgang vil vente på at en lås skal forsvinne – noe som ikke kommer til å skje).
For det første, hvis dette skjer med depotet ditt, ingen panikk. Berkeley DB-filsystemet benytter seg av databasetransaksjoner og sjekkpunkter og forhåndsskriver journaler for å forsikre at bare de mest katastrofale hendelser[15] kan ødelegge databasen permanent. En tilstrekkelig paranoid depotadministrator vil ta sikkerhetskopier av depotet til en annen maskin på en eller annen måte, men ikke få systemadministratoren din til å hente tilbake det som er på backuptapen helt enda.
For det andre, bruk den følgende oppskriften for å forsøke å “fjerne fastkilingen” av depotet:
Sjekk at det ikker er noen prosesser som aksesserer (eller forsøker å få tilgang) til depotet. For depot på nettverket betyr dette at Apache HTTP også må kjøres ned.
Bli brukeren som eier og vedlikeholder depotet. Dette er viktig fordi en gjenoppretting av depotet med gal bruker kan forandre rettighetene på filene i depotet så det fortsatt vil være utilgjengelig etter at det er “reparert”.
Kjør kommandoen svnadmin recover /sti/til/depot. Du vil nå se noe i likhet med dette:
Repository lock acquired. Please wait; recovering the repository may take some time... Recovery completed. The latest repos revision is 19.
Denne kommandoen kan ta flere minutter å fullføre.
Start Subversiontjeneren på nytt.
Denne prosedyren reparerer nesten ethvert tilfelle av låste databaser. Vær sikker på at du kjører denne kommandoen som brukeren som eier og vedlikeholder databasen, ikke bare som root. Deler av gjenopprettingsprosessen kan føre til at diverse databasefiler blir opprettet på nytt (delte områder av hukommelsen, for eksempel). Gjenoppretting som root vil lage disse filene som om de er eid av root, noe som betyr at til og med etter at du setter opp forbindelsen til databasen igjen, klarer ikke vanlige brukere å få tilgang til den.
Hvis den nevnte prosedyren av en eller annen grunn ikke klarer å reparere depotet ditt, skal du gjøre to ting. Først flytter du det ødelagte depotet ut av veien og legger inn den siste sikkerhetskopien av den. Deretter, send en email til Subversionbrukerlisten (på <users@subversion.tigris.org>) der du beskriver problemet i detalj. Dataintegritet har ekstremt høy prioritet for Subversionutviklerne.
Et Subversionfilsystem har dataene sine spredt over flere databasetabeller på en måte som generelt bare er forstått av (og som bare har interesse for) selve Subversionutviklerne. Ting kan imidlertid skje som gjør at alt eller litt av disse dataene må samles i et enkeltstående og portabelt “flatt” filformat. Subversion har en slik mekanisme, implementert som et par svnadmin-delkommandoer: dump og load.
Den vanligste grunnen til å dumpe og laste et Subversiondepot er på grunn av forandringer i selve Subversion. Etterhvert som Subversion modnes, skjer det noen ganger at det blir gjort forandringer i det bakenforliggende databaseoppsettet som fører til at Subversion blir inkompatibel med tidligere versjoner av depotet. Andre grunner for dumping og lasting kan være å flytte et Berkeley DB-depot til et annet operativsystem eller CPU-arkitektur, eller for å bytte mellom Berkeley DB- og FSFS-lagringsformat. Den anbefalte måten å gjøre dette på er relativt enkel:
Bruk din nåværende versjon av svnadmin, dump depotene dine til dumpfiler.
Oppgrader til den nye versjonen av Subversion.
Flytt de gamle depotene ut av veien, og opprett nye på plassen deres ved å bruke den nye versjonen av svnadmin.
Så laster du dumpfilene inn i de respektive nyopprettede depotene ved å bruke den nye versjonen av svnadmin.
Pass på å kopiere alle egne tilpasninger fra de gamle depotene til de nye, inkludert DB_CONFIG-filer og påhakningsskript. Det kan være lurt å lese hva som er forandret i den nye versjonen av Subversion for å se om det er noen forandringer der som gjør at du må gjøre forandringer i påhakningsskriptene eller konfigurasjonen.
Hvis flytteprosessen gjør depotet ditt tilgjengelig på en annen URL (for eksempel flyttet til en annen maskin eller blir aksessert på en annen måte), bør du be alle brukerne dine om å kjøre svn switch --relocate på sine eksisterende arbeidskopier. Se svn switch.
svnadmin dump vil generere utdata med en rekke depotrevisjoner som er i Subversions spesiallagde filsystemdumpformat. Dumpformatet sendes til standard ut-strømmen (stdout), mens informative meldinger sendes til standardfeil-strømmen (stderr). Dette gjør at du kan omdirigere utdatastrømmen til en fil mens du holder øye med statusen i terminalvinduet.
$ svnlook youngest mittdepot 26 $ svnadmin dump mittdepot > dumpfil * Dumped revision 0. * Dumped revision 1. * Dumped revision 2. … * Dumped revision 25. * Dumped revision 26.
Når denne prosessen er ferdig, vil du ha en enkelt fil (dumpfil i det foregående eksempelet) som inneholder alle dataene som er lagret i depotet ditt i det forespurte området med revisjoner. Legg merke til at svnadmin dump leser revisjonstrær fra depotet akkurat som enhver annen leseprosess ville gjort det (svn checkout, for eksempel). Så det er trygt å kjøre denne kommandoen når som helst.
Den andre delkommandoen i dette radarparet, svnadmin load, tolker inndatastrømmen som en Subversiondumpfil og “spiller av” disse dumpede revisjonene inn i måldepotet for denne operasjonen. Den gir også informativ feedback, denne gangen ved å bruke standard ut-strømmen:
$ svnadmin load nyttdepot < dumpfil
<<< Started new txn, based on original revision 1
* adding path : A ... done.
* adding path : A/B ... done.
…
------- Committed new rev 1 (loaded from original rev 1) >>>
<<< Started new txn, based on original revision 2
* editing path : A/mu ... done.
* editing path : A/D/G/rho ... done.
------- Committed new rev 2 (loaded from original rev 2) >>>
…
<<< Started new txn, based on original revision 25
* editing path : A/D/gamma ... done.
------- Committed new rev 25 (loaded from original rev 25) >>>
<<< Started new txn, based on original revision 26
* adding path : A/Z/zeta ... done.
* editing path : A/mu ... done.
------- Committed new rev 26 (loaded from original rev 26) >>>
Legg merke til at fordi svnadmin bruker standard inn- og standard ut-strømmer for depotdumpen og lasteprosessen, kan folk som vet hva de driver på med prøve på ting som dette (kanskje til og med bruke forskjellige versjoner av svnadmin på hver side av røret):
$ svnadmin create nyttdepot $ svnadmin dump mittdepot | svnadmin load nyttdepot
Vanligvis vil dumpfilen bli ganske stor – mye større enn selve depotet. Dette er fordi hver eneste versjon av hver fil blir representert som fulltekst i dumpfilen. Dette er den raskeste og enkleste oppførselen, og fin hvis du sender dumpdataene gjennom et rør direkte til en annen prosess (som for eksempel et pakkeprogram, filterprogram eller inn i en lasteprosess). Men hvis du lager en dumpfil som er ment for langtidslagring, vil du nok ønske å spare diskplass ved å bruke valget --deltas. Med dette valget vil etterfølgende revisjoner av filer bli generert som pakkede, binære forskjeller – akkurat som filrevisjoner er lagret i et depot. Dette valget er langsommere, men resulterer i en dumpfil mer lik størrelsen til det originale depotet.
Vi nevnte tidligere at svnadmin dump sender en rekke revisjoner til standard ut. Bruk --revision-valget for å spesifisere en enkelt revisjon som skal dumpes, eller et område med revisjoner. Hvis du utelater dette valget, vil alle eksisterende depotrevisjoner bli dumpet.
$ svnadmin dump mittdepot --revision 23 > rev-23.dumpfil $ svnadmin dump mittdepot --revision 100:200 > rev-100-200.dumpfil
Når Subversion dumper hver ny revisjon, lagres akkurat nok informasjon til at et framtidig lasteprogram kan gjenskape denne revisjonen basert på en tidligere revisjon. Med andre ord, for enhver gitt revisjon i dumpfilen vil bare de elementene som ble forandret i den revisjonen havne i dumpen. Det eneste unntaket fra denne regelen er den første revisjonen som blir dumpet med den nåværende svnadmin dump-kommandoen.
Vanligvis vil Subversion ikke uttrykke den første dumpede revisjonen bare som forskjeller som skal legges til den forrige revisjonen. En grunn til dette er at det ikke er noen tidligere revisjon i dumpfilen! For det andre kan ikke Subversion vite tilstanden til depotet som dumpdataene vil bli lastet inn i (hvis det i det hele tatt noen gang blir et). For å garantere at utdataene etter hver kjøring av svnadmin dump er selvforsynt, er den første dumpede revisjonen vanligvis en full representasjon av hver katalog, fil og egenskap i denne revisjonen i depotet.
Denne standardoppførselen kan du imidlertid forandre. Hvis du legger til --incremental-valget når du dumper depotet ditt, vil svnadmin sammenligne den første dumpede revisjonen mot den forrige revisjonen i depotet, på den samme måten som den behandler alle andre revisjoner som blir dumpet. Programmet vil deretter generere den første revisjonen nøyaktig som det gjør med resten av revisjonene i dumpområdet – det tar bare med de forandringene som oppsto i den revisjonen. Fordelen med dette er at du kan lage flere små dumpfiler som kan bli lastet etter hverandre istedenfor en stor fil. Dette gjøres på denne måten:
$ svnadmin dump mittdepot --revision 0:1000 > dumpfil1 $ svnadmin dump mittdepot --revision 1001:2000 --incremental > dumpfil2 $ svnadmin dump mittdepot --revision 2001:3000 --incremental > dumpfil3
Disse dumpfilene kan så bli lastet inn i et nytt depot med den følgende kommandosekvensen:
$ svnadmin load nyttdepot < dumpfil1 $ svnadmin load nyttdepot < dumpfil2 $ svnadmin load nyttdepot < dumpfil3
Et annet lurt triks du kan utføre med dette --incremental-valget går ut å legge til en rekke revisjoner til en eksisterende dumpfil. For eksempel kan du ka en post-commit-påhakning som rett og slett legger til depotdumpen av den spesifikke revisjonen som utførte påhakningsskriptet. Eller du kan ha en skript som kjøres om natten for å legge til dumpdata for alle revisjonene som ble lagt til depotet siden forrige gang skriptet ble kjørt. Brukt på denne måten kan dump- og load-kommandoen til svnadmin tilby verdifull hjelp med å ta sikkerhetskopi av forandringer i depotet over tid i tilfelle systemkrasj eller andre katastrofer.
Dumpformatet kan også brukes til å flette innholdet av flere forskjellige depot inn i et enkelt depot. Ved å bruke valget --parent-dir når du kjører svnadmin load, kan du spesifisere en ny virtuell rotkatalog for lasteprosessen. Dette betyr at hvis du har dumpfiler for tre depot, la oss si tekst-dumpfil, kalender-dumpfil og regneark-dumpfil, kan du først opprette et nytt depot som skal inneholde alle sammen:
$ svnadmin create /sti/til/prosjekter $
Deretter, lag nye kataloger i depotet som for å “pakke inn” innholdet av hver av de tre foregående depotene:
$ svn mkdir -m "Opprettet røtter for prosjektene" \
file:///sti/til/prosjekter/tekst \
file:///sti/til/prosjekter/kalender \
file:///sti/til/prosjekter/regneark
Committed revision 1.
$
Til sist, last de individuelle dumpfilene inn i deres respektive plasseringer i det nye depotet:
$ svnadmin load /sti/til/prosjekter --parent-dir tekst < tekst-dumpfil … $ svnadmin load /sti/til/prosjekter --parent-dir kalender < kalender-dumpfil … $ svnadmin load /sti/til/prosjekter --parent-dir regneark < regneark-dumpfil … $
Vi vil nevne en siste måte å bruke depotdumpformatet i Subversion på – konvertering fra en annen lagringsmekanisme eller et annen versjonskontrollsystem. Fordi dumpfilformatet for det meste er lesbart for mennesker,[16] skulle det være relativt enkelt å beskrive generelle sett med forandringer – ved å bruke dette filformatet. Faktisk bruker programmet cvs2svn.py (se “Konvertere et depot fra CVS til Subversion”) dumpformatet for å representere innholdet i et CVS-depot så dette innholdet kan bli flyttet inn i et Subversiondepot.
Tross utallige forbedringer i teknologien siden den moderne datamaskinen ble født, gjør fortsatt en ting seg sterkt gjeldende – noen ganger går ting forferdelig galt. Strømbrudd, nettverksbrudd, ødelagte minnebrikker og krasjede harddisker er bare en forsmak på ondskapen som Skjebnen er troende til å øse over selv den mest samvittighetsfulle administrator. Vi lander derfor på et meget viktig tema – hvordan du tar sikkerhetskopier av depotdataene dine.
Det er generelt sett to backupmetoder som er tilgjengelig for administratorer av Subversiondepot – inkrementell og fullstendig. Vi diskuterte i en tidligere seksjon i dette kapittelet hvordan svnadmin dump --incremental kan brukes til å utføre en inkrementell backup (se “Flytte et depot”). Essensen i idéen er at på det punktet kopien lages blir kun forandringene i depotet siden forrige backup tatt med.
En fullstendig kopi av depotet er bokstavelig talt en duplisering av hele depotkatalogen (som inkluderer enten en Berkeley-database eller et FSFS-miljø). Hvis du nå tar en kopi uten å midlertidig sperre all tilgang til depotet, vil det å bare ta en rekursiv katalogkopi kanskje føre til at det blir feil i kopien, siden noen kan skrive til databasen på samme tidspunkt.
I tilfellet med Berkeley DB beskriver dokumenter laget av Sleepycat en viss rekkefølge databasefiler kan kopieres i som vil garantere en gyldig sikkerhetskopi. Og en lignende rekkefølge eksisterer for FSFS-data. Enda bedre, du trenger ikke selv å implementere disse algoritmene fordi det er allerede gjort av Subversionutviklerne. Skriptet hot-backup.py ligger i tools/backup/-katalogen i den distribuerte Subversionkildekoden. Ved å gi en depotsti og en backupplassering til hot-backup.py – som egentlig bare er en mer intelligent innpakning rundt svnadmin hotcopy-kommandoen – vil den utføre de nødvendige stegene for å ta backup av det aktive depotet ditt – uten at du må sperre tilgangen for alle sammen – og vil deretter rense bort de døde Berkeleyloggfilene fra det aktive depotet.
Selv om du også har en inkrementell backup, vil du sannsynligvis ville kjøre dette programmet på en regulær basis. For eksempel vil du kanskje vurdere å legge til hot-backup.py til en automatisk programkjøring (som cron på Unix-systemer). Eller, hvis du foretrekker finkornede backupløsninger, kan du sette post-commit-skriptet til å kalle hot-backup.py (se “Påhakningsskript”), som vil lage en ny kopi av depotet for hver ny revisjon som er opprettet. Bare legg det følgende til hook/post-commit-skriptet i katalogen til det aktive depotet:
(cd /sti/til/påhakningsskript; \
./hot-backup.py ${REPOS} /sti/til/sikkerhetskopier &)
Den resulterende backupen er et fullstendig fungerende Subversiondepot som du kan legge inn som en erstatning for det aktive depotet i tilfelle noe skulle gå forferdelig galt.
Det er fordeler med begge backupmetodene. Den letteste er helt klart å ta en full sikkerhetskopi, som alltid vil resultere i en fullstendig funksjonell duplikat av depotet ditt. Dette betyr igjen at hvis noe stygt skulle skje med det aktive depotet, kan du hente det tilbake fra sikkerhetskopien med en enkel rekursiv katalogkopiering. Uheldigvis, hvis du har flere kopier av depotet, vil disse fullstendige kopiene spise opp like mye diskplass som det aktive depotet.
Inkrementelle backuper som bruker depotdumpformatet er utmerket å ha for hånden hvis oppbygningen av databasen forandrer seg mellom versjonene av selve Subversion. Siden en komplett depotdump og depotlasting vanligvis er nødvendig for å oppgradere depotet ditt til det nye formatet, er det veldig enkelt å allerede ha halvparten av denne prosessen (dumpdelen) overstått. Uheldigvis tar opprettelsen av – og gjenoppretting fra – inkrementelle bakuper lengre tid, fordi hver innlegging enten blir spilt av inn i dumpfilen eller depotet.
I begge disse backupscenariene må depotadminstratorer være oppmerksom på hvordan forandringer i uversjonerte revisjonsegenskaper påvirker sikkerhetskopiene. Siden disse forandringene ikke selv lager nye revisjoner, vil de ikke aktivisere post-commit-påhakninger, og kanskje heller ikke aktivisere pre-revprop-change- og post-revprop-change-skriptene.[17] Og siden du kan forandre revisjonsegenskaper som går på tvers av den kronolgisk rekkefølgen – du kan forandre enhver egenskap til en revisjon til enhver tid – vil en inkrementell backup av de seneste få revisjonene kanskje ikke fange opp en egenskapsforandring i en revisjon som ble inkludert som del av en tidligere backup.
Vanligvis vil bare den helt paranoide trenge å ta backup av hele depotet, la oss si, hver gang en innlegging skjer. Imidlertid, hvis vi antar at et gitt depot har andre ekstra finkornede mekanismer på plass (som epost for hver innlegging), vil en varm backup av databasen være noe som en depotadministrator vil ønske å inkludere som del av en nattlig systembackup. For de fleste depot vil arkiverte eposter alene gi nok informasjon som gjenopprettelseskilde, i hvert fall for de siste få innleggingene. Men det er dine data – beskytt dem så mye som du vil.
Ofte er den beste tilnærmingsmåten til kopier av depotet delt. Du kan sette opp kombinasjoner av fullstendige og inkrementelle backuper, pluss et arkiv med innleggingsmeldinger. Subversionutviklerne tar for eksempel backup av kildekodedepotet etter hver ny revisjon er opprettet, og tar vare på et arkiv med alle eposter som varsler om innlegginger og forandringer i revisjoner og egenskaper. Løsningen din kan være noe lignende, men bør tilpasses dine behov og følge den fine linjen mellom bekvemmelighet og paranoia. Og selv om dette ikke vil redde hardwaren din fra Skjebnens jernhånd, vil det helt sikkert hjelpe deg å komme deg på fote igjen etter disse harde tider.
Når depotet ditt er opprettet og konfigurert, er alt som gjenstår å begynne å bruke det. Hvis du har en samling med eksisterende data som er klar til å bli plassert under versjonskontroll, vil du mest sannsynlig bruke svn-klientens import-delkommando for å gjøre dette. Men før du setter i gang bør du gå nøye gjennom planene for depotet på lang sikt. I denne seksjonen vil vi gi noen råd om hvordan du planlegger oppbygningen av depotet, og hvordan du plasserer dataene dine i dette oppsettet.
Selv om Subversion lar deg flytte rundt versjonerte filer og kataloger uten tap av informasjon, kan det forstyrre arbeidsflyten til de som ofte aksesserer depotet og forventer at ting er på sine respektive plasser. Prøv å se litt inn i fremtiden; planlegg på forhånd før du plasserer dataene dine under versjonskontroll. Ved å “legge opp” innholdet av depotene dine på en effektiv måte den første gangen, kan du forhindre en drøss med fremtidige hodepiner.
Det er noen få ting å tenke over når du setter opp Subversiondepoter. La oss anta at som depotadministrator, vil du være ansvarlig for å vedlikeholde versjonskontrollsystemet for flere prosjekter. Det første du må bestemme deg for er om du vil bruke et enkelt depot for flere prosjekter, om du vil gi hvert prosjekt sitt eget depot, eller en kombinasjon av disse løsningene.
Det er fordeler med å bruke et enkelt depot for flere prosjekter, først og fremst at du slipper duplisert vedlikehold. Et enkelt depot betyr at det er ett sett med påhakningsskript, én ting å ta backup av, én ting å dumpe og laste hvis Subversion kommer i en ny inkompatibel versjon og så videre. I tillegg kan du lett flytte data mellom prosjekter uten å miste historisk versjonsinformasjon.
Bakdelen med å bruke et enkelt depot er at forskjellige prosjekter kan ha forskjellige postlister eller forskjellige autentiserings- og autorisasjonskrav. Husk også at Subversion bruker globale revisjonsnumre i depotet. Noen liker ikke at selv om ingen forandringer er blitt gjort i deres prosjekt den senere tid, fortsetter likevel det yngste revisjonsnummeret å øke fordi andre prosjekter legger til nye revisjoner.
En kompromissløsning kan også bli brukt. For eksemel kan prosjekter bli gruppert sammen etter hvor relaterte de er til hverandre. Du kan ha noen få depoter med en håndfull prosjekter i hvert av dem. På denne måten kan prosjekter som kan komme til å dele data gjøre dette ganske enkelt, og etterhvert som nye revisjoner blir lagt til depotet, vet utviklerne at disse nye revisjonene ihvertfall er fjernt relaterte på en eller annen måte til alle som bruker dette depotet.
Etter å ha avgjort hvordan du vil organisere prosjektene i henhold til depotene, vil du kanskje tenke over kataloghierarkier i selve depotene. Fordi Subversion bruker vanlige katalogkopier til forgrening og merking (se Kapittel 4, Forgrening og fletting), anbefaler Subversionmiljløet at du velger en katalogplassering for hver prosjektrot – den “øverste” katalogen som inneholder data relatert til det prosjektet – og deretter oppretter tre kataloger under denne roten: trunk som er katalogen der hovedutviklingen av prosjektet foregår; branches som er en katalog som er ment for å lage diverse navngitte grener av hovedutviklingen; tags som er en katalog av grener som er laget, og kanskje ødelagt, men aldri forandret.
For eksempel kan depotet ditt se ut som dette:
/
calc/
trunk/
tags/
branches/
calendar/
trunk/
tags/
branches/
spreadsheet/
trunk/
tags/
branches/
…
Legg merke til at det har ingenting å si hvor i depotet hver prosjektrot er. Hvis du har bare ett prosjekt per depot, er den logiske plassen å putte hver prosjektrot i roten av det prosjektets respektive depot. Hvis du har flere prosjekter, vil du kanskje ønske å arrangere dem i grupper innenfor depotet, kanskje legge prosjekter med likelydende mål eller delt kode i den samme underkatalogen, eller kanskje rett og slett gruppere dem alfabetisk. Et slikt system kan se ut som dette:
/
utils/
calc/
trunk/
tags/
branches/
calendar/
trunk/
tags/
branches/
…
office/
spreadsheet/
trunk/
tags/
branches/
…
Legg opp depotet ditt på den måten du synes passer best. Subversion forventer ikke eller påtvinger deg ikke en spesiell layout – hva Subversion angår, er en katalog en katalog en katalog. Ideelt sett skal du kunne velge det depotoppsettet som fyller behovet til folkene som arbeider på prosjektet som bor der.
Etter at du har bestemt deg for hvordan du vil arrangere prosjektene i depotet ditt, vil du sannsynligvis ønske å fylle depotet med dette oppsettet og de innledende prosjektdataene. Det er et par måter du kan gjøre dette i Subversion. Du kan bruke svn mkdir-kommandoen (se Kapittel 9, Subversion Complete Reference) for å opprette hver katalog i skjelettet av depotlayouten, en og en. En raskere måte å utføre den samme oppgaven på er å bruke kommandoen svn import (se “svn import”). Ved å først opprette layouten i en midlertidig plassering på harddisken, kan du importere hele katalogtreet inn i depotet i en enkelt innlegging:
$ mkdir tmpdir $ cd tmpdir $ mkdir projectA $ mkdir projectA/trunk $ mkdir projectA/branches $ mkdir projectA/tags $ mkdir projectB $ mkdir projectB/trunk $ mkdir projectB/branches $ mkdir projectB/tags … $ svn import . file:///path/to/repos --message 'Initial repository layout' Adding projectA Adding projectA/trunk Adding projectA/branches Adding projectA/tags Adding projectB Adding projectB/trunk Adding projectB/branches Adding projectB/tags … Committed revision 1. $ cd .. $ rm -rf tmpdir $
Du kan kontrollere resultatene etter importen ved å kjøre kommandoen svn list:
$ svn list --verbose file:///path/to/repos
1 harry May 08 21:48 projectA/
1 harry May 08 21:48 projectB/
…
$
Når du har skjelettet til oppsettet på plass, kan du begynne å importere selve prosjektdataene inn i depotet ditt, hvis noen slike data allerede eksisterer. Som sagt, det er flere måter å gjøre dette på. Du kan bruuke svn import-kommandoen. Du kan hente ut en arbeidskopi fra det nye depotet ditt, flytte og arrangere prosjektdataene inne i arbeidskopien og bruke svn add- og svn commit-kommandoene. Men når vi begynner å snakke om slike ting snakker vi ikke lengre om depotadministrasjon. Hvis du ikke allerede er kjent med svn-klientprogrammet, se Kapittel 3, Guidet tur.
Nå skal du ha fått en grunnleggende forståelse av hvordan du oppretter, konfigurerer og vedlikeholder Subversiondepoter. Vi har introdusert deg for de forskjellige verktøyene som vil hjelpe deg med denne oppgaven. Gjennom kapittelet har vi gjort oppmerksom på noen feller en administrator kan gå i og hvordan disse fellene kan unngås.
Alt som gjenstår nå er å bestemme hvilke spennende data som du vil lagre i depotet ditt, og til sist, hvordan du vil gjøre det tilgjengelig over nettverket. Det neste kapittelet handler kun om nettverk.
[12] Dette kan høres råflott ut, men vi snakker bare om enhver som er interessert i mystikken omkring arbeidskopien hvor alles data ligger.
[13] Og det er forøvrig en innebygget funksjonalitet og ikke en feil.
[14] Selv om svnadmin dump har en konsekvent regel om innledende skråstreker – å ikke inkludere dme – kan det være at andre programmer som genererer dumpdata ikke er like konsekvente.
[15] Som for eksempel: Harddisk + kjempemagnet = krise.
[16] Dumpformatet i Subversion ligner et RFC-882-format, den samme typen format som vanligvis brukes i eposter.
[17] svnadmin setlog kan bli kjørt på en måte som går helt utenom påhakningsgrensesnittet.
Innholdsfortegnelse
A Subversion repository can be accessed simultaneously by clients running on the same machine on which the repository resides using the file:/// method. But the typical Subversion setup involves a single server machine being accessed from clients on computers all over the office—or, perhaps, all over the world.
This section describes how to get your Subversion repository exposed outside its host machine for use by remote clients. We will cover Subversion's currently available server mechanisms, discussing the configuration and use of each. After reading this section, you should be able to decide which networking setup is right for your needs, and understand how to enable such a setup on your host computer.
Subversion was designed with an abstract network layer. This means that a repository can be programmatically accessed by any sort of server process, and the client “repository access” API allows programmers to write plugins that speak relevant network protocols. In theory, Subversion can use an infinite number of network implementations. In practice, there are only two servers at the time of writing.
Apache is an extremely popular webserver; using the mod_dav_svn module, Apache can access a repository and make it available to clients via the WebDAV/DeltaV protocol, which is an extension of HTTP. In the other corner is svnserve: a small, standalone server program that speaks a custom protocol with clients. Table 6-1 presents a comparison of the two servers.
Note that Subversion, as an open-source project, does not officially endorse any server as “primary” or “official”. Neither network implementation is treated as a second-class citizen; each server has advantages and disadvantages. In fact, it's possible for different servers to run in parallel, each accessing your repositories in its own way, and each without hindering the other (see “Supporting Multiple Repository Access Methods”). Here's a brief overview and comparison of the two available Subversion servers—as an administrator, it's up to you to choose whatever works best for you and your users.
Tabell 6.1. Network Server Comparison
| Feature | Apache + mod_dav_svn | svnserve |
|---|---|---|
| Authentication options | HTTP(S) basic auth, X.509 certificates, LDAP, NTLM, or any other mechanism available to Apache httpd | CRAM-MD5 or SSH |
| User account options | private 'users' file | private 'users' file, or existing system (SSH) accounts |
| Authorization options | blanket read/write access, or per-directory read/write control | blanket read/write access, or per-directory write (but not read) control using a pre-commit hook |
| Encryption | via optional SSL | via optional SSH tunnel |
| Interoperability | partially usable by other WebDAV clients | not interoperable |
| Web viewing | limited built-in support, or via 3rd-party tools such as ViewCVS | via 3rd-party tools such as ViewCVS |
| Speed | somewhat slower | somewhat faster |
| Initial setup | somewhat complex | fairly simple |
This section is a general discussion of how a Subversion client and server interact with one another, regardless of the network implementation you're using. After reading, you'll have a good understanding of how a server can behave and the different ways in which a client can be configured to respond.
The Subversion client spends most of its time managing working copies. When it needs information from a repository, however, it makes a network request, and the server responds with an appropriate answer. The details of the network protocol are hidden from the user; the client attempts to access a URL, and depending on the URL schema, a particular protocol is used to contact the server (see Depot-URLer). Users can run svn --version to see which URL schemas and protocols the client knows how to use.
When the server process receives a client request, it typically demands that the client identify itself. It issues an authentication challenge to the client, and the client responds by providing credentials back to the server. Once authentication is complete, the server responds with the original information the client asked for. Notice that this system is different from systems like CVS, where the client pre-emptively offers credentials (“logs in”) to the server before ever making a request. In Subversion, the server “pulls” credentials by challenging the client at the appropriate moment, rather than the client “pushing” them. This makes certain operations more elegant. For example, if a server is configured to allow anyone in the world to read a repository, then the server will never issue an authentication challenge when a client attempts to svn checkout.
If the client's network request writes new data to the repository (e.g. svn commit), then a new revision tree is created. If the client's request was authenticated, then the authenticated user's name is stored as the value of the svn:author property on the new revision (see “Uversjonerte egenskaper”). If the client was not authenticated (in other words, the server never issued an authentication challenge), then the revision's svn:author property is empty. [18]
Many servers are configured to require authentication on every request. This can become a big annoyance to users, who are forced to type their passwords over and over again.
Happily, the Subversion client has a remedy for this: a built-in system for caching authentication credentials on disk. By default, whenever the command-line client successfully authenticates itself to a server, it saves the credentials in the user's private runtime configuration area—in ~/.subversion/auth/ on Unix-like systems or %APPDATA%/Subversion/auth/ on Windows. (The runtime area is covered in more detail in “Runtime Configuration Area”.) Successful credentials are cached on disk, keyed on a combination of hostname, port, and authentication realm.
When the client receives an authentication challenge, it first looks for the appropriate credentials in the disk cache; if not present, or if the cached credentials fail to authenticate, then the client simply prompts the user for the information.
The security-paranoid people may be thinking to themselves, “Caching passwords on disk? That's terrible! You should never do that!” But please remain calm. First, the auth/ caching area is permission-protected so that only the user (owner) can read data from it, not the world at large. If that's still not safe enough for you, you can disable credential caching. To disable caching for a single command, pass the --no-auth-cache option:
$ svn commit -F log_msg.txt --no-auth-cache Authentication realm: <svn://host.example.com:3690> example realm Username: joe Password for 'joe': Adding newfile Transmitting file data . Committed revision 2324. # password was not cached, so a second commit still prompts us $ svn delete newfile $ svn commit -F new_msg.txt Authentication realm: <svn://host.example.com:3690> example realm Username: joe [...]
Or, if you want to disable credential caching permanently, you can edit your runtime config file (located next to the auth/ directory). Simply set store-auth-creds to no, and no credentials will be cached on disk, ever.
[auth] store-auth-creds = no
Sometimes users will want to remove specific credentials from the disk cache. To do this, you need to navigate into the auth/ area and manually delete the appropriate cache file. Credentials are cached in individual files; if you look inside each file, you will see keys and values. The svn:realmstring key describes the particular server realm that the file is associated with:
$ ls ~/.subversion/auth/svn.simple/ 5671adf2865e267db74f09ba6f872c28 3893ed123b39500bca8a0b382839198e 5c3c22968347b390f349ff340196ed39 $ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28 K 8 username V 3 joe K 8 password V 4 blah K 15 svn:realmstring V 45 <https://svn.domain.com:443> Joe's repository END
Once you have located the proper cache file, just delete it.
One last word about client authentication behavior: a bit of explanation about the --username and --password options is needed. Many client subcommands accept these options; however it is important to understand using these options does not automatically send credentials to the server. As discussed earlier, the server “pulls” credentials from the client when it deems necessary; the client cannot “push” them at will. If a username and/or password are passed as options, they will only be presented to the server if the server requests them. [19] Typically, these options are used when:
the user wants to authenticate as a different user than her system login name, or
a script wants to authenticate without using cached credentials.
Here is a final summary that describes how a Subversion client behaves when it receives an authentication challenge:
Check whether the user specified any credentials as command-line options, via --username and/or --password. If not, or if these options fail to authenticate successfully, then
Look up the server's realm in the runtime auth/ area, to see if the user already has the appropriate credentials cached. If not, or if the cached credentials fail to authenticate, then
Resort to prompting the user.
If the client successfully authenticates by any of the methods listed above, it will attempt to cache the credentials on disk (unless the user has disabled this behavior, as mentioned earlier.)
The svnserve program is a lightweight server, capable of speaking to clients over TCP/IP using a custom, stateful protocol. Clients contact an svnserve server by using URLs that begin with the svn:// or svn+ssh:// schema. This section will explain the different ways of running svnserve, how clients authenticate themselves to the server, and how to configure appropriate access control to your repositories.
There are a few different ways to invoke the svnserve program. If invoked with no options, you'll see nothing but a help message. However, if you're planning to have inetd launch the process, then you can pass the -i (--inetd) option:
$ svnserve -i ( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
When invoked with the --inetd option, svnserve attempts to speak with a Subversion client via stdin and stdout using a custom protocol. This is the standard behavior for a program being run via inetd. The IANA has reserved port 3690 for the Subversion protocol, so on a Unix-like system you can add lines to /etc/services like these (if they don't already exist):
svn 3690/tcp # Subversion svn 3690/udp # Subversion
And if your system is using a classic Unix-like inetd daemon, you can add this line to /etc/inetd.conf:
svn stream tcp nowait svnowner /usr/local/bin/svnserve svnserve -i
Make sure “svnowner” is a user which has appropriate permissions to access your repositories. Now, when a client connection comes into your server on port 3690, inetd will spawn an svnserve process to service it.
On a Windows system, third-party tools exist to run svnserve as a service. Look on Subversion's website for a list of these tools.
A second option is to run svnserve as a standalone “daemon” process. Use the -d option for this:
$ svnserve -d $ # svnserve is now running, listening on port 3690
When running svnserve in daemon mode, you can use the --listen-port= and --listen-host= options to customize the exact port and hostname to “bind” to.
There's still a third way to invoke svnserve, and that's in “tunnel mode”, with the -t option. This mode assumes that a remote-service program such as RSH or SSH has successfully authenticated a user and is now invoking a private svnserve process as that user. The svnserve program behaves normally (communicating via stdin and stdout), and assumes that the traffic is being automatically redirected over some sort of tunnel back to the client. When svnserve is invoked by a tunnel agent like this, be sure that the authenticated user has full read and write access to the repository database files. (See Servers and Permissions: A Word of Warning.) It's essentially the same as a local user accessing the repository via file:/// URLs.
Once the svnserve program is running, it makes every repository on your system available to the network. A client needs to specify an absolute path in the repository URL. For example, if a repository is located at /usr/local/repositories/project1, then a client would reach it via svn://host.example.com/usr/local/repositories/project1 . To increase security, you can pass the -r option to svnserve, which restricts it to exporting only repositories below that path:
$ svnserve -d -r /usr/local/repositories …
Using the -r option effectively modifies the location that the program treats as the root of the remote filesystem space. Clients then use URLs that have that path portion removed from them, leaving much shorter (and much less revealing) URLs:
$ svn checkout svn://host.example.com/project1 …
When a client connects to an svnserve process, the following things happen:
The client selects a specific repository.
The server processes the repository's conf/svnserve.conf file, and begins to enforce any authentication and authorization policies defined therein.
Depending on the situation and authorization policies,
the client may be allowed to make requests anonymously, without ever receiving an authentication challenge, OR
the client may be challenged for authentication at any time, OR
if operating in “tunnel mode”, the client will declare itself to be already externally authenticated.
At the time of writing, the server only knows how to send a CRAM-MD5 [20] authentication challenge. In essence, the server sends a bit of data to the client. The client uses the MD5 hash algorithm to create a fingerprint of the data and password combined, then sends the fingerprint as a response. The server performs the same computation with the stored password to verify that the result is identical. At no point does the actual password travel over the network.
It's also possible, of course, for the client to be externally authenticated via a tunnel agent, such as SSH. In that case, the server simply examines the user it's running as, and uses it as the authenticated username. For more on this, see “SSH authentication and authorization”.
As you've already guessed, a repository's svnserve.conf file is the central mechanism for controlling authentication and authorization policies. The file has the same format as other configuration files (see “Runtime Configuration Area”): section names are marked by square brackets ([ and ]), comments begin with hashes (#), and each section contains specific variables that can be set (variable = value). Let's walk through this file and learn how to use them.
For now, the [general] section of the svnserve.conf has all the variables you need. Begin by defining a file which contains usernames and passwords, and an authentication realm:
[general] password-db = userfile realm = example realm
The realm is a name that you define. It tells clients which sort of “authentication namespace” they're connecting to; the Subversion client displays it in the authentication prompt, and uses it as a key (along with the server's hostname and port) for caching credentials on disk (see “Client Credentials Caching”.) The password-db variable points to a separate file that contains a list of usernames and passwords, using the same familiar format. For example:
[users] harry = foopassword sally = barpassword
The value of password-db can be an absolute or relative path to the users file. For many admins, it's easy to keep the file right in the conf/ area of the repository, alongside svnserve.conf. On the other hand, it's possible you may want to have two or more repositories share the same users file; in that case, the file should probably live in a more public place. The repositories sharing the users file should also be configured to have the same realm, since the list of users essentially defines an authentication realm. Wherever the file lives, be sure to set the file's read and write permissions appropriately. If you know which user(s) svnserve will run as, restrict read access to the user file as necessary.
There are two more variables to set in the svnserve.conf file: they determine what unauthenticated (anonymous) and authenticated users are allowed to do. The variables anon-access and auth-access can be set to the values none, read, or write. Setting the value to none restricts all access of any kind; read allows read-only access to the repository, and write allows complete read/write access to the repository. For example:
[general] password-db = userfile realm = example realm # anonymous users can only read the repository anon-access = read # authenticated users can both read and write auth-access = write
The example settings are, in fact, the default values of the variables, should you forget to define them. If you want to be even more conservative, you can block anonymous access completely:
[general] password-db = userfile realm = example realm # anonymous users aren't allowed anon-access = none # authenticated users can both read and write auth-access = write
Notice that svnserve only understands “blanket” access control. A user either has universal read/write access, universal read access, or no access. There is no detailed control over access to specific paths within the repository. For many projects and sites, this level of access control is more than adequate. However, if you need per-directory access control, you'll need to use either use Apache with mod_authz_svn (see “Per-Directory Access Control”) or use a pre-commit hook script to control write access (see “Påhakningsskript”). The Subversion distribution comes with commit-access-control.pl and the more sophisticated svnperms.py scripts for use in pre-commit scripts.
svnserve's built-in authentication can be very handy, because it avoids the need to create real system accounts. On the other hand, some administrators already have well-established SSH authentication frameworks in place. In these situations, all of the project's users already have system accounts and the ability to “SSH into” the server machine.
It's easy to use SSH in conjunction with svnserve. The client simply uses the svn+ssh:// URL schema to connect:
$ whoami harry $ svn list svn+ssh://host.example.com/repos/project harry@host.example.com's password: ***** foo bar baz …
In this example, the Subversion client is invoking a local ssh process, connecting to host.example.com, authenticating as the user harry, then spawning a private svnserve process on the remote machine running as the user harry. The svnserve command is being invoked in tunnel mode (-t) and its network protocol is being “tunneled” over the encrypted connection by ssh, the tunnel-agent. svnserve is aware that it's running as the user harry, and if the client performs a commit, the authenticated username will be attributed as the author of the new revision.
The important thing to understand here is that the Subversion client is not connecting to a running svnserve daemon. This method of access doesn't require a daemon, nor does it notice one if present. It relies wholly on the ability of ssh to spawn a temporary svnserve process, which then terminates when the network connection is closed.
When using svn+ssh:// URLs to access a repository, remember that it's the ssh program prompting for authentication, and not the svn client program. That means there's no automatic password caching going on (see “Client Credentials Caching”). The Subversion client often makes multiple connections to the repository, though users don't normally notice this due to the password caching feature. When using svn+ssh:// URLs, however, users may be annoyed by ssh repeatedly asking for a password for every outbound connection. The solution is to use a separate SSH password-caching tool like ssh-agent on a Unix-like system, or pageant on Windows.
When running over a tunnel, authorization is primarily controlled by operating system permissions to the repository's database files; it's very much the same as if Harry were accessing the repository directly via a file:/// URL. If multiple system users are going to be accessing the repository directly, you may want to place them into a common group, and you'll need to be careful about umasks. (Be sure to read “Supporting Multiple Repository Access Methods”.) But even in the case of tunneling, the svnserve.conf file can still be used to block access, by simply setting auth-access = read or auth-access = none.
You'd think that the story of SSH tunneling would end here, but it doesn't. Subversion allows you to create custom tunnel behaviors in your run-time config file (see “Runtime Configuration Area”.) For example, suppose you want to use RSH instead of SSH. In the [tunnels] section of your config file, simply define it like this:
[tunnels] rsh = rsh
And now, you can use this new tunnel definition by using a URL schema that matches the name of your new variable: svn+rsh://host/path. When using the new URL schema, the Subversion client will actually be running the command rsh host svnserve -t behind the scenes. If you include a username in the URL (for example, svn+rsh://username@host/path) the client will also include that in its command (rsh username@host svnserve -t.) But you can define new tunneling schemes to be much more clever than that:
[tunnels] joessh = $JOESSH /opt/alternate/ssh -p 29934
This example demonstrates a couple of things. First, it shows how to make the Subversion client launch a very specific tunneling binary (the one located at /opt/alternate/ssh) with specific options. In this case, accessing a svn+joessh:// URL would invoke the particular SSH binary with -p 29934 as arguments—useful if you want the tunnel program to connect to a non-standard port.
Second, it shows how to define a custom environment variable that can override the name of the tunneling program. Setting the SVN_SSH environment variable is a convenient way to override the default SSH tunnel agent. But if you need to have several different overrides for different servers, each perhaps contacting a different port or passing a different set of options to SSH, you can use the mechanism demonstrated in this example. Now if we were to set the JOESSH environment variable, its value would override the entire value of the tunnel variable—$JOESSH would be executed instead of /opt/alternate/ssh -p 29934.
It's not only possible to control the way in which the client invokes ssh, but also to control the behavior of sshd on your server machine. In this section, we'll show how to control the exact svnserve command executed by sshd, as well as how to have multiple users share a single system account.
To begin, locate the home directory of the account you'll be using to launch svnserve. Make sure the account has an SSH public/private keypair installed, and that the user can log in via public-key authentication. Password authentication will not work, since all of the following SSH tricks revolve around using the SSH authorized_keys file.
If it doesn't already exist, create the authorized_keys file (on Unix, typically ~/.ssh/authorized_keys). Each line in this file describes a public key that is allowed to connect. The lines are typically of the form:
ssh-dsa AAAABtce9euch.... user@example.com
The first field describes the type of key, the second field is the uuencoded key itself, and the third field is a comment. However, it's a lesser known fact that the entire line can be preceded by a command field:
command="program" ssh-dsa AAAABtce9euch.... user@example.com
When the command field is set, the SSH daemon will run the named program instead of the typical svnserve -t invocation that the Subversion client asks for. This opens the door to a number of server-side tricks. In the following examples, we abbreviate the lines of the file as:
command="program" TYPE KEY COMMENT
Because we can specify the executed server-side command, it's easy to name a specific svnserve binary to run and to pass it extra arguments:
command="/path/to/svnserve -t -r /virtual/root" TYPE KEY COMMENT
In this example, /path/to/svnserve might be a custom wrapper script around svnserve which sets the umask (see “Supporting Multiple Repository Access Methods”). It also shows how to anchor svnserve in a virtual root directory, just as one often does when running svnserve as a daemon process. This might be done either to restrict access to parts of the system, or simply to relieve the user of having to type an absolute path in the svn+ssh:// URL.
It's also possible to have multiple users share a single account. Instead of creating a separate system account for each user, generate a public/private keypair for each person. Then place each public key into the authorized_users file, one per line, and use the --tunnel-user option:
command="svnserve -t --tunnel-user=harry" TYPE1 KEY1 harry@example.com command="svnserve -t --tunnel-user=sally" TYPE2 KEY2 sally@example.com
This example allows both Harry and Sally to connect to the same account via public-key authentication. Each of them has a custom command that will be executed; the --tunnel-user option tells svnserve -t to assume that the named argument is the authenticated user. Without --tunnel-user, it would appear as though all commits were coming from the one shared system account.
A final word of caution: giving a user access to the server via public-key in a shared account might still allow other forms of SSH access, even if you've set the the command value in authorized_keys. For example, the user may still get shell access through SSH, or be able to perform X11 or general port-forwarding through your server. To give the user as little permission as possible, you may want to specify a number of restrictive options immediately after the command:
command="svnserve -t --tunnel-user=harry",no-port-forwarding,\
no-agent-forwarding,no-X11-forwarding,no-pty \
TYPE1 KEY1 harry@example.com
The Apache HTTP Server is a “heavy duty” network server that Subversion can leverage. Via a custom module, httpd makes Subversion repositories available to clients via the WebDAV/DeltaV protocol, which is an extension to HTTP 1.1 (see http://www.webdav.org/ for more information.) This protocol takes the ubiquitous HTTP protocol that is the core of the World Wide Web, and adds writing—specifically, versioned writing—capabilities. The result is a standardized, robust system that is conveniently packaged as part of the Apache 2.0 software, is supported by numerous operating systems and third-party products, and doesn't require network administrators to open up yet another custom port. [21] While an Apache-Subversion server has more features than svnserve, it's also a bit more difficult to set up. With flexibility often comes more complexity.
Much of the following discussion includes references to Apache configuration directives. While some examples are given of the use of these directives, describing them in full is outside the scope of this chapter. The Apache team maintains excellent documentation, publicly available on their website at http://httpd.apache.org. For example, a general reference for the configuration directives is located at http://httpd.apache.org/docs-2.0/mod/directives.html.
Also, as you make changes to your Apache setup, it is likely that somewhere along the way a mistake will be made. If you are not already familiar with Apache's logging subsystem, you should become aware of it. In your httpd.conf file are directives that specify the on-disk locations of the access and error logs generated by Apache (the CustomLog and ErrorLog directives, respectively). Subversion's mod_dav_svn uses Apache's error logging interface as well. You can always browse the contents of those files for information that might reveal the source of a problem that is not clearly noticeable otherwise.
To network your repository over HTTP, you basically need four components, available in two packages. You'll need Apache httpd 2.0, the mod_dav DAV module that comes with it, Subversion, and the mod_dav_svn filesystem provider module distributed with Subversion. Once you have all of those components, the process of networking your repository is as simple as:
getting httpd 2.0 up and running with the mod_dav module,
installing the mod_dav_svn plugin to mod_dav, which uses Subversion's libraries to access the repository, and
configuring your httpd.conf file to export (or expose) the repository.
You can accomplish the first two items either by compiling httpd and Subversion from source code, or by installing pre-built binary packages of them on your system. For the most up-to-date information on how to compile Subversion for use with the Apache HTTP Server, as well as how to compile and configure Apache itself for this purpose, see the INSTALL file in the top level of the Subversion source code tree.
Once you have all the necessary components installed on your system, all that remains is the configuration of Apache via its httpd.conf file. Instruct Apache to load the mod_dav_svn module using the LoadModule directive. This directive must precede any other Subversion-related configuration items. If your Apache was installed using the default layout, your mod_dav_svn module should have been installed in the modules subdirectory of the Apache install location (often /usr/local/apache2). The LoadModule directive has a simple syntax, mapping a named module to the location of a shared library on disk:
LoadModule dav_svn_module modules/mod_dav_svn.so
Note that if mod_dav was compiled as a shared object (instead of stati