Software Liste | Dokumentationen | Demos | Artikel |
Versionsverwaltung mit Subversion: Installation, Konfiguration, Benutzung. Einschließlich der Einrichtung von Trac.
Einleitung
Subversion ist ein Versions-Control-System (VCS), welches CVS ähnelt (in der Tat sind viele Kommandos sogar identisch). Wozu braucht man nun also ein neues VCS, wo wir doch CVS bereits seit Jahren haben? Neben dem Stichwort der "Vielfalt" ist ein weitaus gewichtigeres Argument, dass SVN einige Vorteile bietet, die einen Umstieg zumindest nachdenkenswert machen (eine kurze Gegenüberstellung findet sich u. a. hier. Beim Lesen immer daran denken, dass das eine oder andere Argument mittlerweile durch den Entwicklungsprozess überholt sein könnte). Ich möchte hier nur ein paar Punkte aufzählen, die mir wichtig erscheinen:
- ein symbolischer Link bleibt auch im Repository ein symbolischer Link (er wird also nicht, wie bei CVS, beim Check-In dereferenziert)
- Dateien und Verzeichnisse können umbenannt und/oder verschoben werden, ohne dass die "Change History" dabei verloren geht
- Repositories lassen sich auch mit dem Kommandozeilen-Client browsen
- der Zugriff auf ein Repository kann mit vielen verschiedenen Methoden
erfolgen: Lokal (
file://
), per SSH oder RSH, und sogarhttp://
/https://
per WebDav - Dank WebDav lassen sich die Repositories sogar komfortabel mit dem Lieblings-Web-Browser einsehen - sofern WebDav konfiguriert und der Zugriff gestattet wurde
- und etliches mehr
Sicher: Wo es Licht gibt, ist auch Schatten. Dazu fiel mir nicht ganz so viel auf:
- der Platzverbrauch auf der Platte ist etwa doppelt so hoch
- will man Keywords wie Id, Author, Revision in einer Datei verwenden, muss man das für jede Datei einzeln explizit konfigurieren
- bezüglich mancher Features gehen die Meinungen auseinander (siehe o.g. Vergleiche)
Worum es in diesem Artikel geht
Beim Studium dieses Artikels lernt der "geneigte Leser", wie er/sie einen Subversion Server (und Client) unter Linux einrichtet und konfiguriert, Repositories aufsetzt, und mit Subversion arbeitet. Beispiele und Skripte (die sich auch direkt aus der Seite herunterladen und für die eigene Nutzung anpassen lassen) veranschaulichen das Vorgehen. Der hier eingerichtete Server lässt sich via SSH lesend und schreibend nutzen, WebDav wird nur für den (anonymen) Lesezugriff freigeschaltet.
Installation und Konfiguration liegt bei diesem Artikel Ubuntu 6.06 "Dapper Drake" zugrunde – die Schritte sollten aber weitgehend auch unter den meisten anderen Distributionen identisch oder zumindest sehr ähnlich sein (ggf. andere Paketnamen oder Pfade). Auch bei Mac OS X sollte dies noch erkennbar sein. Für Nutzer gewisser Betriebssysteme aus Redmond bleibt da oft nur ein "Sorry" – diese müssen ggf. ein wenig mehr experimentieren …
Worum es in diesem Artikel NICHT geht
Dieser Artikel ist weder eine komplette Referenz, ein kompletter Guide, noch ein komplettes Manual. Für all diese Dinge verweise ich auf die Homepage des Projektes selbst, ein SVN Handbuch an anderer Stelle, oder einen bekannten Dienstleister im Netz.
Inhalt
- Einleitung
- Einrichten des Servers
- Repositories erstellen
- Mit Subversion arbeiten
- Zu einem neuen Server migrieren
- Hilfreiche SVN Resources
Einrichten des Servers
Bevor wir irgend etwas konfigurieren, müssen wir natürlich zunächst die
Software installieren. Auf jeden Fall benötigen wir den Subversion Server sowie
den Client – bei Ubuntu findet sich beides im selben Paket namens
"subversion", und installiert ist sie ruck-zuck mit einem einfachen
sudo apt-get install subversion
.
In unserem Artikel wollen wir außerdem WebDav für den Lesezugriff einrichten,
wofür wir bei Ubuntu das Paket "libapache2-svn" benötigen – also:
sudo apt-get install libapache2-svn
. Abhängigkeiten von anderen
Paketen (oh ja, die gibt es – ohne Webserver kein WebDav, hier brauchen wir
daher z.B. Apache, wie der Paketname richtig vermuten lässt) werden dabei,
wie gewohnt, automatisch aufgelöst.
SVN konfigurieren
Bereits unmittelbar nach der Installation ist die Software bereits teilweise einsatzbereit (der Client tut schon das, was man von ihm erwartet) – dennoch kommen wir um ein wenig Konfigurationsarbeit nicht herum. Wäre ja auch zu einfach. Doch bevor wir zu den Einzelheiten kommen, erst einmal eine kurze Erklärung, wohin wir wollen:
Was wir erreichen wollen
Wir wollen hier keinen 0-8-15 Server aufsetzen – dafür finden sich schon genügend
Artikel im Netz. Hier geht es um etwas "raffinierteres": Wir sind ein wenig
paranoid, was den Schreibzugriff angeht - dafür setzen wir also ein "sicheres
Protokoll", nämlich SSH, ein – für die Konfiguration von SSL sind wir zu faul
(haben keinen Bock, uns ein gültiges Zertifikat für den Webserver zuzulegen, und
für SSL konfigurieren wollen wir ihn auch nicht. Das bedarf also zusätzlich eines
SSH Daemons auf dem Server - den die meisten dort ohnehin bereits laufen haben
(für die anderen: sudo apt-get install openssh-server
). Damit wir
dennoch nicht für jeden Entwickler einen separaten Account einrichten müssen,
tricksen wir ein wenig, und richten einen "shared account" ein. Keine Bange: Wer
da was gemacht hat, ist dennoch gut ersichtlich.
Für ausgewählte Projekte möchten wir einen anonymen Lesezugriff ermöglichen. Dafür nutzen wir WebDav (so dass wir den Code auch gleich in die Website des Projektes mit integrieren können). Darüber können sich Besucher die letzte Version des Codes ansehen, oder auch jede beliebige Version "auschecken" – nicht jedoch ins Repository schreiben. Wir brauchen dann nicht mehr wegen jedem kleinen Fix extra eine Release zu machen (oder separate Patches zur Verfügung stellen) – wer immer "Up-to-Date" sein will, kann sich dann hier bedienen.
Overhead? Mag ja sein. Aber wer das eine oder andere Feature nicht benötigt, kann den entsprechenden Absatz einfach überspringen.
svnserve
Zuerst einmal benötigen wir einen "User Account" auf dem Server, der sich um
SVN kümmert: Ihm werden die Repositories gehören, und er ist für die Wartung
zuständig. Damit der eine oder andere "Administrator" mit eigenem Account auf
dem Server da entsprechend eingreifen kann, richten wir auch eine passende
Benutzergruppe ein. Also gilt es zunächst, sich zum Superman – hoppla –
SuperUser (aka "root") zu machen, und für weitere Schritte gleich solcher zu
bleiben: sudo - su
. Gleich danach: adduser --group svn
. Damit haben wir
Benutzer und Gruppe – beide mit dem Namen "svn" – angelegt. Wichtig ist, bei
"adduser" nicht die Option --system
zu verwenden! "svn" braucht eine reale
Shell, damit wir per SSH auf ihn zugreifen können (--system
setzt die Shell
auf /bin/false
)!
Dann basteln wir unserem neuen User auch noch schnell einen Satz SSH Keys:
sudo -H -u svn ssh-keygen -q -t dsa -f /home/svn/.ssh/id_dsa -N ""
.
Wie bereits weiter oben erwähnt, sollen usere Entwickler sich einen Account
teilen – jepp, das ist der, den wir gerade angelegt haben. Dennoch wollen wir
letztendlich wissen, wer welche Änderung durchgeführt hat. Doch das muss kein
Widerspruch sein – es gibt da nämlich einen netten Trick mit der Datei
~svn/.ssh/authorized_keys
. Wie vielleicht bereits bekannt ist, werden in
dieser Datei die "public keys" derjenigen gespeichert, die auf diesen Account
(ohne Kenntnis dessen Passworts) zugreifen dürfen. Pro Key ist das eine Zeile
in dieser Datei. Einigen ist vielleicht darüber hinaus noch bekannt, dass man
auch Kommentare hier einpflegen kann (indem man die entsprechende Zeile mit
einem #
beginnt, kennzeichnet man sie als Kommentarzeile). Und die "richtigen
Eingeweihten" wissen auch schon, was jetzt kommt: Man kann unmittelbar vor
einem "public key" auch noch ein Kommando definieren, welches beim Login dieses
Users ausgeführt werden soll. Das sieht dann ungefähr so aus:
# Dies ist ein sehr vertrauenswürdiger Benutzer:
command="svnserve -t -r /usr/local/svn --tunnel-user=izzy" <SSH Key>
# Ein kleiner Entwickler, den wir weniger gut kennen:
command="svnserve -t -r /usr/local/svn --tunnel-user=peter",no-port-forwarding,no-agent-forwarding,no-x11-forwarding,no-pty <SSH Key>
Hierbei ist /usr/local/svn
das Verzeichnis, unterhalb dessen sich
unsere Repositories befinden. Loggt sich also jetzt einer der beiden User ein,
wird im Hintergrund svnserve
im Tunnel-Modus (-t
)
gestartet. Durch Angabe der Repository-Base verkürzt sich entsprechend der Pfad
zum Repository, den dieser User angeben muss (die Base kann nämlich dann
weggelassen werden). Müssen wir irgendwann einmal unsere Repositories an andere
Stelle umziehen, kann dies somit "transparent" für die User geschehen. Durch
Angabe des --tunnel-user
weiß SVN dann auch, wer da gerade kommt.
Für Peter sind wir noch einen Schritt weiter gegangen (ich schrieb ja bereits,
dass wir paranoid sind). Peter darf zwar SVN benutzen (schließlich soll er an
unserem Projekt mitarbeiten). Sonst hat er jedoch auf dem Server "nichts zu
suchen" – also verbieten wir ihm sicherheitshalber so einiges. U. a. bekommt
er nicht einmal eine Shell, sollte er dies versuchen – das unterbindet
no-pty
.
<SSH Key>
steht in obigem Beispiel für den eigentlichen "öffentlichen
Schlüssel" des jeweiligen Benutzers. Den kenne ich hier nicht, also kann ich
ihn auch nicht posten :) Wer jetzt jedoch noch Bedenken hat, es könnte sich ja
jemand ohne Key einloggen: Nee, geht nicht – da wir dem User svn
kein
Passwort verpasst haben. Wer natürlich ganz auf "Nummer sicher" gehen will,
kann den SSH Zugang ja in der /etc/ssh/sshd_config
so einstellen, dass man
nur per Key reinkommt (PasswordAuthentication No).
WebDav
Wer keinen Zugriff über WebDav (also mittels des http://
Protokolls)
einrichten möchte, kann diesen Teil überspringen.
Zumindest Ubuntu installiert für WebDav auch gleich eine Beispieldatei. Dort ist zwar alles auskommentiert (zum Glück ;) – aber wir wissen damit wenigstens, welche Keywords wir brauchen, und wie das Ganze ungefähr aussieht. Anpassen müssen wir es dennoch selbst – und da gibt es verschiedene Möglichkeiten:
Einfache Definition für ein einzelnes Repository
Fangen wir mit der einfachsten Variante an: Nur ein einzelnes Repository soll via WebDav angebunden werden. Großartigen Komfort wollen wir auch nicht – es soll so einfach wie möglich sein. Dafür eignet sich die o. g. Beispieldatei sehr gut; wir können sie sogar noch weiter kürzen:
<Location /repo/>
DAV svn
SVNPath /usr/local/svn/public/sample
# AuthType Basic
# AuthName "Subversion Repository"
# AuthUserFile /etc/apache2/svn-user-auth
# AuthzSVNAccessFile /usr/local/svn/sample/conf/authz
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
Require group svn
</LimitExcept>
</Location>
Die erste Zeile ist klar: Das ist die URL, unter der das Repository im Web-Browser
erreichbar sein soll. Der abschließende /
ist nicht unbedingt nötig, vermeidet
aber die eine oder andere Fehlermeldung. Wer einen separaten virtuellen Server für
SVN hernimmt, kann hier sogar einfach nur "/" eintragen. SVNPath
gibt den FileSystem-Pfad zu unserem Repository an. Die Authentifizierung haben
wir auskommentiert (wir wollen ja "nur lesen") – und das "Nur-Lesen" haben wir
dann abschließend auch für jeden - ohne Angabe von Benutzer und/oder Passwort –
freigegeben.
Stilvoll und mehrere Repositories
Bei einer größeren Anzahl an Repositories wäre es etwas zu viel des Guten,
jedes auch noch einzeln konfigurieren zu müssen. Das brauchen wir auch gar
nicht, denn neben SVNPath
gibt es auch noch die Direktive SVNParentPath
,
die das Verzeichnis "eine Ebene höher" bezeichnet. Das "Stilvolle" lässt sich
mit SVNIndexXSLT
erreichen – was manchmal jedoch nicht so ganz ohne ist (hat
mich eine ganze Weile gekostet, bis ich das mit mehreren Repositories
hinbekam). Damit andere weniger Mühe haben, hier ein passendes Beispiel mit den
zugehörigen Erklärungen.
Und einem kleinen "Vorwort" zu den "Voraussetzungen". In meinem Fall habe ich
einen virtuellen Server extra für SVN eingerichtet. Dessen "Document Root"
liegt außerhalb der Repositories (und umgekehrt), sagen wir in /var/www
. Dort
werden auch die Dateien svnindex.xsl
und svnindex.css
abgelegt,
die ich für dieses Beispiel von dieser
Seite herunter geladen habe, die
leider nicht mehr existiert. Die Konfiguration im Apache sieht dann etwa so
aus:
<Location /repo/>
DAV svn
SVNParentPath /usr/local/svn/public
SVNIndexXSLT "/svnindex.xsl"
# AuthType Basic
# AuthName "Subversion Repository"
# AuthUserFile /etc/apache2/svn-user-auth
# AuthzSVNAccessFile /usr/local/svn/sample/conf/authz
<LimitExcept GET PROPFIND OPTIONS REPORT>
Require valid-user
Require group svn
</LimitExcept>
</Location>
Sieht der einfachen Konfiguration recht ähnlich, hat aber ein paar
entscheidende Unterschiede vorzuweisen. So muss für diesen Fall die Location
etwas anderes als nur /
sein – da ja unsere Repositories außerhalb des
"Document Root" befindlich sind, und in der /
URL unsere SVNIndexXSLT
Dateien gehostet werden. Zweitens wurde die SVNPath
Direktive durch
SVNParentPath
ersetzt, welches "eine Ebene höher" zeigt. Und Drittens ist mit
SVNIndexXSLT
eine neue Direktive hinzugekommen, die auf unsere
XSLT Templates verweist.
Oh, es gibt noch schönere Templates – die sind aber meist auch etwas schwieriger zu konfigurieren. Ich möchte da nur auf diese hier verweisen, welche sich noch bei Archive.Org finden.
Authentication
Um auch den Schreibzugriff per WebDav zu ermöglichen, müssen die vier
auskommentierten Zeilen angepasst (und das Kommentarzeichen an ihrem Anfang
entfernt) werden. AuthUserFile ist dabei eine Passwort-Datei, die mit dem Tool
htpasswd
(von Apache) erstellt und gewartet wird. Die Datei
AuthzSVNAccessFile hingegen muss auf die conf/authz
Datei des
Repositories zeigen – was bei mehreren Repositories natürlich ein wenig
komplizierter wird. Da wir dies aber in unserem Beispiel gar nicht wollen,
gehe ich hier auch nicht weiter darauf ein – und lasse die Zeilen auskommentiert.
Logging Enhancements
Wer will, kann auch noch die LogFiles frisieren. Anstelle des üblichen
CustomLog logs/access_log combined
ließe sich
CustomLog logs/svn_logfile "%t %u %{SVN-ACTION}e" env=SVN-ACTION
verwenden. Oder, für beide Logfile Formate gleichzeitig:
CustomLog logs/svn_logfile "%t %u %{SVN-ACTION}e" env=SVN-ACTION
TransferLog Log logs/access_log
Da bei TransferLog das LogFormat nicht direkt angegeben werden kann, muss es ggf.
unmittelbar davor mit LogFormat
nochmals spezifiziert werden. Details
dazu bitte ich der Apache Dokumentation zu entnehmen.
Skripte für die beschriebenen Schritte
SVN User anlegen
Dieses Skript legt den SVN User an und erstellt dessen SSH Keys. Es sollte als
SuperUser (root) ausgeführt werden – also z. B. sudo ./svn_create_user.sh
:
#!/bin/bash # Create the SVN user #===================================================[ The SVN user account ]=== # Here we create a user (and group) "svn". This account will also be used for # shared access by the developers (so not everybody needs an own OS account), # so it needs a real shell (i.e. don't use "adduser --system", as this assigns # "/bin/false", which will result in "svn: Connection closed unexpectedly" # (svn: Netzwerkverbindung wurde unerwartet geschlossen) when accessing the # repo with "svn+ssh://svn@server:/repo" adduser --group svn sudo -H -u svn ssh-keygen -q -t dsa -f /home/svn/.ssh/id_dsa -N "" |
Repositories erstellen
Die Software ist installiert und konfiguriert – es wird Zeit, dass wir uns um die Repositories selbst kümmern. Dabei geht es nicht nur um das Anlegen derselben, sondern auch darum, wie die Daten hineinkommen. Drei Möglichkeiten gäbe es dafür:
- jede Datei einzeln hinzufügen mit
svn add
- ein Verzeichnis mit
svn import
importieren - ein CVS Repository migrieren
Nummer 1 schenken wir uns an dieser Stelle. Das mag etwas für einzelne Dateien sein – nicht jedoch für ganze Projekte. Bleiben die zwei anderen Möglichkeiten, auf die wir hier auch näher eingehen wollen. Für Nummer 3 konstruieren wir uns auch gleich noch ein Hintertürchen, falls uns SVN irgendwann einmal nicht mehr gefällt – sodass wir wieder zu unserem automatisch weitergepflegten CVS Repository zurückkehren können. Was natürlich wieder einmal optional ist.
Vorab sei gesagt: Wer nicht mit svn import
seinen Code importieren,
sondern lediglich CVS Repositories migrieren will, kann sich jeweils den Schritt
svnadmin create
sparen. Alles andere wird jedoch in beiden Fällen
gebraucht - ebenso wie das Arbeiten als SuperUser "root" sudo - su
.
Basis-Verzeichnisse
Zunächst einmal erstellen wir uns – optional – unterhalb unseres "Basis Verzeichnisses" zwei Ordner: "private" für die Repositories, an die außer uns selbst niemand ran soll, und "public" für die anderen. Wer nur das eine oder das andere braucht, kann das entsprechen auch direkt im Basis-Verzeichnis machen. Mit Unter-Ordner hält man sich jedoch die Hinter-Tür offen, seine Meinung später zu revidieren.
In unserem Beispiel erstellen wir nur ein einziges "privates" Repository, und schaffen Raum für zahlreiche "öffentliche" Projekte:
sudo -H -u svn mkdir -p /usr/local/svn/private
sudo -H -u svn svnadmin create --fs-type fsfs /usr/local/svn/private
sudo -H -u svn mkdir -p /usr/local/svn/public/sample
sudo -H -u svn svnadmin create --fs-type fsfs /usr/local/svn/public/sample
Zugriffsrechte
Als nächstes gilt es, sich um die Zugriffs-Berechtigungen zu kümmern. Wie bereits erwähnt, wollen wir noch einen lokalen Benutzer zur Administration heranziehen (wer das nicht will, kann diesen Schritt wiederum überspringen). Damit dieser entsprechend Zugriff hat, weisen wir ihm die zuvor erstellte Gruppe "svn" zu:
# Den Benutzer "admin" in die Gruppe "svn" aufnehmen
adduser admin svn
# Der Gruppe "svn" Schreibzugriff auf die Repositories geben
chmod -R g+w /usr/local/svn
Nochmals: Existiert auf dem Server kein "passender" lokaler User, oder soll kein solcher "zum Einsatz kommen", kann dieser Schritt übersprungen werden. Die Wartung kann auch von "root" mittels "sudo - svn" durchgeführt werden.
Allerdings müssen wir in jedem Fall noch die Zugriffsrechte für unsere "SSH
User" einstellen. Dies geschieht für jedes Repository separat in der jeweiligen
conf/authz
Datei desselben – in unserem Beispiel wären das also
/usr/local/svn/private/conf/authz
für das private, und
/usr/local/svn/public/sample/conf/authz
für das öffentliche "sample"
Repository.
Fangen wir mit dem privaten an. Hier soll es nur einen geben, der vollen Zugriff hat – nennen wir ihn einmal "john". Niemand sonst soll hier auch nur lesen können – alles streng geheim, und zumindest uns ist hier die Privatsphäre noch wichtig. Das sollte dann in etwa so aussehen:
[/]
john = rw
* =
Für unser "öffentliches" Repository sieht das ein wenig anders aus. Die Entwickler sollen hier lesend und schreibend zugreifen können – während alle anderen zumindest lesen dürfen. Der Einfachheit halber legen wir daher für unsere Entwickler gleich mal eine Gruppe an:
[group]
devel = john,peter
[/]
devel = rw
* = r
Dank der "group" Funktion brauchen wir nicht für jeden Entwickler eine eigene Zeile eintragen (wir sind ja schließlich faul), sondern fügen ihn einfach der Gruppe hinzu. Außer Lesen ("r") und Schreiben ("w") gibt es keine weiteren Berechtigungen, die man hier einstellen könnte.
Füllen der Repositories
So, jetzt ist also wirklich alles konfiguriert – kann's nun endlich losgehen? Wie kommen die Daten hinein? Achja, Nummer 2+3: Importieren oder Migrieren:
Importieren eines Verzeichnisses
Diese Methode eignet sich für "neuen Code", der noch in keinem Repository war. Oder der einen "Neuanfang" machen soll, wobei das "Alte" ruhig der Vergessenheit anheim fallen kann. In unserem Beispiel, wollen wir ein Verzeichnis mit "brisantem Material" (Konfigurationsdateien zum Beispiel) in das private Repository importieren:
cd <directory-above-configuration-dir>
sudo -H -u svn svn import <name> file:///usr/local/svn/private/<name> -m "Initial Import"
Wir wechseln also in das Verzeichnis direkt oberhalb von demjenigen, welches
wir importieren wollen - und rufen dann das Import-Kommando zu Hilfe. Wurde
zuvor ein "lokaler Admin" eingerichtet, erübrigt sich für
diesen natürlich das "sudo -H -u svn
". Der Begriff <name>
steht für den
Verzeichnisnamen – den wir hier auch als Namen für das Repository verwenden.
Damit ist der Import auch schon erledigt. Jetzt kann an eine andere Stelle auf
der Platte gewechselt werden - z.B. cd ~/work
- und wir holen uns
eine Arbeitskopie: svn co file:///usr/local/svn/private/<name>
.
Migrieren eines CVS Repositories
Wie bereits erwähnt: Wenn wir von einem CVS Repository
migrieren wollen, brauchen wir zuvor kein SVN Repository dafür anlegen. Also
gehen wir jetzt auch davon aus, dass lediglich das Verzeichnis für das
Repository existiert, welches wir nun in der "öffentlichen Area" anlegen wollen.
Sofern noch nicht geschehen, muss noch das Paket "cvs2svn" installiert werden
(sudo apt-get install cvs2svn
).
sudo -H -u svn cvs2svn --trunk-only --encoding=utf8 --username=john --fs-type fsfs -s /usr/local/svn/public/demo /usr/local/cvs/demo
Kurz erläutert, was wir da gerade getan haben: Das CVS Repository (nicht die
ausgecheckte Arbeitskopie!) unseres Projektes "demo", welches sich im
Verzeichnis /usr/local/cvs/demo
befindet, haben wir in ein SVN
Repository gleichen Namens im Verzeichnis /usr/local/svn/public/demo
migriert. Mit --trunk-only
haben wir dafür gesorgt, das nur der
"Stammbaum" – nicht aber evtl. existierende Branches – dabei übernommen wurde
(sollen diese auch mit migriert werden, muss dieser Parameter entsprechend
weggelassen werden). Die Log-Dateien sollen als Zeichensatz UTF8 verwenden
(Default wäre ASCII), damit auch Sonderzeichen wie deutsche Umlaute, französische
Akzente oder kyrillische Buchstaben, gemeinsam dort "residieren" können. Auch
wenn das Standard ist, kann es nicht schaden: Als Dateisystem soll das neuere
FSFS (und nicht Berkley DB) zum Einsatz kommen. Mit -s
geben wir
an, wo unser Subversion Repository angelegt werden soll – und schließlich folgt
als letzter Parameter unser "gutes altes" CVS Repository.
Implementieren eines "Hooks" für die automatische CVS Pflege
Wieder einmal ein optionaler Schritt für diejenigen, die sich gern ein "Hintertürchen" offen halten wollen. Der hier besprochene "Hook" sorgt dafür, dass jedes "Commit" in unserem neuen SVN Repository automatisch noch an das alte CVS Repository weitergegeben wird. Wer das nicht brauch kann, wie üblich, diesen Schritt überspringen, und zum entsprechenden Absatz gehen – um sich die Skripte anzuschauen.
Noch hier? OK. Bevor wir den Hook scharfschalten, sollten ein paar Dinge sichergestellt werden:
- Der Hook sollte unmittelbar nach der Migration scharfgeschaltet werden. In anderen Worten: Keine Commits in eines der Repositories zwischendurch.
- Ein Backup des CVS Repositories kann nicht schaden.
- Außer diesem Hook sollte niemand mehr schreibend auf das CVS Repository zugreifen!
Um unser Ziel zu erreichen, benötigen wir zunächst ein passendes Skript. Dieses
findet sich – zusammen mit einer recht ausführlichen Beschreibung – auf dieser
Seite zum Download. Nach
dem Herunterladen sollte es entweder in ein im $PATH
befindliches Verzeichnis
kopiert werden - oder das Hook-Skript selbst (siehe unten) muss so verändert
werden, dass das Skript stets mit vollem Pfad aufgerufen wird. Unser
Hook-Skript selbst sieht etwa so aus - sofern svn_cvsinject sich im $PATH
befindet:
#!/bin/sh REPOS="$1" REV="$2" CVSDIR=/path/to/cvs/repo svn_maillog "$REPOS" "$REV" "svn@localhost" "sam@localhost" svn_cvsinject -r "$REV" "$REPOS" "$CVSDIR" -a "1.0/v1_0" -a "2.0/v2_0" & exit 0 |
Hier muss jetzt noch die CVSDIR
Variable so angepasst werden, dass sie auf
das CVS Repository zeigt. Dann müssen wir das Skript ausführbar machen (chmod
u+x,g+x post-commit
), es dem user sowie der Gruppe "svn" übereignen (chown
svn:svn post-commit
), und es im "hooks/
" Unterverzeichnis des SVN
Repositories ablegen.
Großartig – alles fertig! Sicherheitshalber sollte der Text auf der Webseite,
von der svn_cvsinject
heruntergeladen wurde, gut durchgelesen
werden – um sich über Einschränkungen, Beschreibung, etc.pp. zu informieren.
Für obige Schritte benutzbare Skripte
Repositories erstellen/migrieren
Folgendes Skript erstellt das oben beschriebene "private Repository", und
migriert existierende CVS Repositories. Bitte vor Gebrauch gründlich prüfen
und die entsprechenden Einstellungen anpassen – ich übernehme für nicht
Garantie! Um es schließlich auszuführen, sollte man wieder einmal "root" sein
(sudo ./svn_create_repos.sh
):
#!/bin/bash # With this script you can initially create your repositories. Run it as # root - e.g. "sudo svn_create_repos.sh" - after you checked it and made # your changes, where necessary. #==========================================================[ Configuration ]=== # Sudo command for executing things as svn repo owner sudo="su - svn" # Base directory below which all your repos reside svnbase=/usr/local/svn # local user with full access to all SVN functions - this is the one and # only to access the private repository admin="john" # users with read+write access to all repositories admingroup="john,peter" # comma separated list for the conf/authz file # where is your old cvsroot (for all your projects to be migrated here) cvsroot=/usr/local/cvsroot # which projects to migrate 1:1 (space separated list) conv_projects="demo sample whatever" # which projects to migrate and rename conv_oldname="beans frogs" conv_newname="jbean ifrog" #--------------------------------------[ authz file for private repository ]--- # This function creates the conf/authz files for the private repository. It # accepts and requires one parameter: The SVN repository directory function authz { local file=$1/conf/authz mv $file ${file}.bak cat >$file<<EndAuth [/] $admin = rw * = EndAuth } #-------------------------------------[ authz file for public repositories ]--- # This function creates the conf/authz files for each repository. It accepts # and requires one parameter: The SVN repository directory. Optional second # parameter defines whether the original "example outfile" should be removed # (set it to "del") or not (ommit the parameter). function authz { local file=$1/conf/authz mv $file ${file}.bak cat >$file<<EndAuth [group] admins = $admingroup [/] admins = rw * = r EndAuth [ "$2" = "del" ] && rm ${file}.bak } #-------------------------------------------------------[ post-commit hook ]--- # This creates the post-commit hook to feed the old CVS repo. Comment in the # svn_maillog if you have that script and want to use it # Parameters: <SVN repository directory> <CVS repository directory> function cvshook { svndir=$1 cvsdir=$2 hookfile=$svndir/hooks/post-commit cat >$hookfile<<EndHook #!/bin/sh REPOS="\$1" REV="\$2" #svn_maillog "\$REPOS" "\$REV" "svn@localhost" "sam@localhost" svn_cvsinject -r "\$REV" "\$REPOS" "$cvsdir" -a "1.0/v1_0" -a "2.0/v2_0" & exit 0 EndHook chown svn:svn $hookfile chmod u+x,g+x $hookfile } #----------------------------------------------[ Migrate projects from CVS ]--- # This function migrates a group of repositories which reside in the same # main dir on CVS side, keeping their names as-is. # Parameters: <List of modules> <CVS root dir> ["del"] # The SVN base is taken from above configuration. If "del" is specified, the # example authz file created by svn will be replaced by our own. Otherwise # it will remain as authz.bak function migrate_cvs { local pnames=$1 local cvsroot=$2 for project in $pnames; do $sudo "cvs2svn --trunk-only --encoding=utf8 --username=izzy --fs-type fsfs -s $svnbase/public/$project $cvsroot/$project" authz $svnbase/public/$project del cvshook $svnbase/public/$project $cvsroot/$project done } # This function migrates a CVS repository and changes its name. Good for # projects which have been renamed - best time to rename the repository ;) # Parameters: <CVS module name> <SVN module name> <CVS root directory> ["del"] # Behaviour as described for the previous function. function migrate_renamed { local oldname=$1 local newname=$2 local cvsroot=$3 $sudo "cvs2svn --trunk-only --encoding=utf8 --username=izzy --fs-type fsfs -s $svnbase/public/$newname $cvsroot/$oldname" authz $svnbase/public/$newname del cvshook $svnbase/public/$newname $cvsroot/$oldname } #===========================================================[ Repositories ]=== # Create base directory for repositories and own it to the svn user - just in # case this is not yet done mkdir -p $svnbase chown svn:svn $svnbase #--------------------------------------------[ Now create the repositories ]--- # private one for site maintenance: $sudo "mkdir -p $svnbase/private" $sudo "svnadmin create --fs-type fsfs $svnbase/private" # public one for OpenSource projects: $sudo "mkdir -p $svnbase/public" # 1:1 migration migrate_cvs "$conv_projects" "${cvsroot}" # projects to rename typeset -i i=0 typeset -i max=`echo $conv_oldname|wc -w` while [ $i -lt $max ]; do let i=$i+1 oldname=`echo $conv_oldname|cut -d " " -f $i` newname=`echo $conv_newname|cut -d " " -f $i` migrate_renamed $oldname $newname "$cvsroot" done |
Mit Subversion arbeiten
Der SVN Kommandozeilen-Client ähnelt in seiner Bedienung sehr stark dem von
CVS. Wer also bislang CVS gewöhnt ist, hat es bei der Umstellung nicht sehr
schwer: Viele Befehle sind fast identisch. Dennoch: Da SVN zusätzliche
Funktionalitäten bietet, kommen naturgemäß weitere Kommandos hinzu. Für alle
von ihnen gibt es eine kurze Hilfe, wenn man svn help [command]
aufruft: Ohne
die Option "command" werden alle verfügbaren Befehle aufgelistet, ansonsten die
für das jeweilige Kommando verfügbaren Optionen.
Grundlegende Befehle
Im Folgenden ein paar grundlegende Kommandos. Nein, die Liste ist definitiv nicht vollständig – es sind nur die aus meiner Sicht für den Anfang wichtigsten Kommandos aufgeführt. Zuerst die, welche uns bereits von CVS vertraut sind; dann noch ein paar "neue":
Befehl | Beschreibung |
---|---|
CVS ähnliche Befehle | |
svn add <files> |
Datei(en) zum Repository hinzufügen |
svn checkout |
Code aus einem Repository auschecken |
svn commit [-m "message"] |
Änderungen im Repository "festschreiben" |
svn diff <file> |
Unterschiede zwischen Arbeitskopie und Repository-Version anzeigen |
svn import <directory> |
Code ins Repository importieren |
svn update |
Arbeitskopie aktualisieren |
Weitere Befehle | |
svn copy <source> <target> |
Eine Datei (oder ein Verzeichnis) innerhalb des Repositories kopieren |
svn mkdir <directory> |
Ein Verzeichnis im Repository anlegen |
svn move <source> <target> |
Eine Datei (oder ein Verzeichnis) im Repository verschieben/umbenennen |
Ein Repository "initialisieren"
Im Unterschied zu CVS, werden einige Dinge hier nicht ganz automatisch erledigt. Wer es gewohnt ist, mit Keywords wie Id, Author oder Revision zu arbeiten, wird schnell feststellen, dass SVN sich zunächst gar nicht darum schert. Dem kann mit folgendem kleinen Skript Abhilfe geschaffen werden:
#!/bin/bash # Initialize a SVN repository we just imported from CVS # (converting .cvsignore and set the keywords to use) #==========================================================[ Configuration ]=== # Keywords we want to use keywords="Id Author Rev Revision" # Parameters passed to the script REPO="$1" WORKDIR="$2" #=================================================[ Process the given Repo ]=== #---------------------------------------[ CheckOut from the SVN repository ]--- svn checkout $REPO $WORKDIR cd $WORKDIR #-----------------------------------------------------[ Convert .cvsignore ]--- find -name .cvsignore | while read file; do svn propset svn:ignore "`cat "$file"`" "`echo "$file" | sed 's,/[^/]*$,,'`" done #--------------------------------------------[ Set the keywords to be used ]--- find . -type f -a '(' -path '*/.*' -prune -o -print ')' | while read file; do for keyword in $keywords; do if grep -q "\$$keyword:" "$file" && ! svn propget svn:keywords "$file" | grep -q "^$keyword\$"; then svn propset svn:keywords $keyword "$file"; fi done done #-----------------------------------------------------[ Commit the changes ]--- svn commit -m "imported svn:ignore and svn:keywords properties" |
P.S.: bei neu hinzugefügten Dateien muss man svn propset svn:keywords
leider jedesmal wieder explizit für selbige ausführen. Kann echt nervig sein, gelle?
Zu einem neuen Server migrieren
Eines Tages muss das SVN Repository vielleicht auf einen neuen Server umziehen. Meist beginnt man erst dann, über diese Möglichkeit nachzudenken. Oder man möchte einfach ein zusätzliches Backup zur Hand haben, für den Fall des Falles.
Nun mag man denken, eine einfache Kopie der entsprechenden Verzeichnisse sollte dafür ausreichend sein (was wahrscheinlich sogar zutrifft). Die meisten Quellen im Web raten davon jedoch ab. Stattdessen empfehlen sie, das Repository zu "dumpen" – und aus dem Dump wieder herzustellen. So auch die Quelle, auf die ich meine Migration aufgebaut habe (ein bei Binary-Zone.Com gefundenes PDF). Da ich die notwendigen Schritte jedoch nicht für jedes Repository von Hand durchführen wollte, nutzte ich stattdessen die Information zur Erstellung eines passenden Skriptes zur Automatisierung des Ganzen.
Repositories sichern
Der erste Schritt ist also die Erstellung eines Dumps – was sich mit dem Befehl
svnadmin dump -q </path/to/repo> <repo-name>.svndump
erledigen lässt
(optional gefolgt von einem bzip2 -9 <repo-name>.svndump
, um den Dump
platzsparend zu verpacken). Dieser Dump enthält den gesamten Code mit all
seinen Revisions in einer Form, mit der sich alle Commits als "Replay" erneut
ausführen lassen. Womit es sich sowohl für ein Backup als auch für die
Migration auf einen neuen Server eignet: Das "Replay" kann auf einem beliebigen
Rechner erfolgen.
Einen solchen Dump kann jeder User erstellen, der auf das Repository Zugriff hat.
Das ist jedoch nur ein Teil des Kuchens: Der Dump enthält zwar den gesamten
Code, nicht aber die Konfiguration des Repos. Die Verzeichnisse conf/
sowie
hooks/
sollten daher ebenfalls gesichert werden, wofür ein einfaches tar cjf
<repo-name>-conf.tar.bz2 </path/to/repo>/conf </path/to/repo>/hooks
genügt.
Die Daten auf den neuen Server übertragen
Dazu gibt es eigentlich nicht viel zu sagen: Einfach die (Archiv-)Dateien
kopieren, wie man es sonst auch täte. Also etwa mittels scp
, rsync
oder
auch ftp
. Sogar ein USB-Stick ließe sich dafür verwenden.
Das Backup wiederherstellen
Jetzt kommt also die "Replay" Aktion – nachdem man einige Vorbereitungen
getroffen hat: Ein leeres Repo muss zunächst erstellt werden. Am besten nimmt
man dazu die Dienste des users svn
in Anspruch, wie es unter Repositories
erstellen bereits beschrieben wurde. Hier die Kurzfassung – die
voraussetzt, dass man bereits als svn
angemeldet ist:
# Ein leeres Repo für "foobar" erstellen
svnadmin create "/opt/svn/foobar"
# Den Dump auspacken
bzip2 -d --keep "/opt/svndumps/foobar.svndump.bz2"
# Das "Replay" durchführen
svnadmin load --force-uuid "/opt/svndumps/foobar.svndump" "/opt/svn/foobar"
# Aufräumen
rm -f "/opt/svndumps/foobar.svndump"
Nachdem das erledigt ist, muss sich noch um die Verzeichnisse conf/
und
hooks
gekümmert werden. Entweder stellt man die einzelnen Dateien hier von
Hand wieder her (besonders dann sinnvoll, wenn die Migration einen
Versionssprung beinhaltet, bei dem sich "Dinge" geändert haben können) – oder
man entpackt einfach den entsprechenden Tarball, den man beim
Backup erstellt hat.
Verbleibt noch das .ssh/
Setup des svn
Users, sowie ggf. WebDAV. Die
passenden Informationen finden sich unter Einrichten des Servers.
Für obige Prozedur verwendetes Skript
Dieses Skript kümmert sich um verschiedene Dinge. Zunächst geht es davon aus, dass es mehrere Verzeichnisbäume (z. B. für verschiedene Projekte) gibt. Ist dies nicht der Fall, kann es natürlich entsprechend angepasst werden. Schließlich handelt es sich um ein Beispiel-Skript – wennauch ein funktionierendes. Es geht alle Projekt-Verzeichnisse durch, und behandelt jedes Unterverzeichnis als SVN-Repository.
Für jedes dieser Repos führt es die o. g. Schritte durch: Erstellen des Dumps,
einpacken mit bzip2
. Parallel dazu erstellt es ein Skript für die
Wiederherstellung, welches später zum "Replay" verwendet werden kann (Erstellen
der Repos, auspacken der Dumps, "Replay" durchführen, Dump wieder löschen,
Archiv behalten). Am Anfang des erzeugten Skripts finden sich einige
grundlegenden Hinweise, falls man sie später benötigt.
Am Anfang des Skriptes befindet sich ein Abschnitt für die[ Configuration ]
.
#!/bin/bash # Dump all SVN repositories # Idea taken from http://www.binary-zone.com/Projects/howto-svn-migration.pdf # See also: http://forums.debian.net/viewtopic.php?t=4933 (how to setup svn) # # This script will dump all repos, compress them using bzip2, and prepare a # restore script. Simply mirror the entire BACKUP_DIR to the new machine, # make the restore script executable, and run it there. Pre-condition is # the new server uses the same directory structure. # # Remember that this way only the repositories are safely "migrated" to the # new server; you must still take care for the configs, hooks, etc. in # their respective subdirectories. #--=[ Configuration ]=-- SVN_BASEDIR=/opt/svn BACKUP_DIR=/opt/svndumps RESTORE_SCRIPT="${BACKUP_DIR}/svn_restore.sh" #--[ /Configuration ]=-- function svn_backup() { REPO=$1 BNAME=$2 FNAM=$(basename "$REPO") # Backup: echo "Processing ${REPO}" svnadmin dump -q "$REPO" >"${BNAME}/${FNAM}.svndump" bzip2 -9 "${BNAME}/${FNAM}.svndump" # Preparing restore: echo >>"${RESTORE_SCRIPT}" echo "# Restoring ${REPO}" >>"${RESTORE_SCRIPT}" echo "svnadmin create \"${REPO}\"" >>"${RESTORE_SCRIPT}" echo "bzip2 -d --keep \"${BNAME}/${FNAM}.svndump.bz2\"" >>"${RESTORE_SCRIPT}" echo "svnadmin load --force-uuid \"${REPO}\" \"${BNAME}/${FNAM}.svndump\"" >>"${RESTORE_SCRIPT}" echo "rm -f \"${BNAME}/${FNAM}.svndump\"" >>"${RESTORE_SCRIPT}" } echo "#!/bin/bash" > "${RESTORE_SCRIPT}" echo "# Restore SVN repositories from the scratch" >>"${RESTORE_SCRIPT}" echo "#" >>"${RESTORE_SCRIPT}" echo "# Restore should be done using the svn user, so we have to create it first:" >>"${RESTORE_SCRIPT}" echo "# adduser svn --gid 120 --uid 120 --home /home/svn --shell /bin/bash --system --disabled-password" >>"${RESTORE_SCRIPT}" echo "# mkdir -f \"${SVN_BASEDIR}\"" >>"${RESTORE_SCRIPT}" echo "# chown svn:svn \"${SVN_BASEDIR}\"" >>"${RESTORE_SCRIPT}" echo "# Now remember to run the following as svn user, i.e. 'sudo su - svn' first." echo "#" >>"${RESTORE_SCRIPT}" for master in $(ls "$SVN_BASEDIR"); do [ ! -d "${BACKUP_DIR}/${master}" ] && mkdir "${BACKUP_DIR}/${master}" if [ "$master" = "slw" ]; then svn_backup "${SVN_BASEDIR}/${master}" "${BACKUP_DIR}/${master}" else for slave in $(ls "${SVN_BASEDIR}/${master}"); do [ -d "${SVN_BASEDIR}/${master}/${slave}" ] && svn_backup "${SVN_BASEDIR}/${master}/${slave}" "${BACKUP_DIR}/${master}" done fi done |
Hilfreiche SVN Resources
Handbücher, Tutorials und HowTos
URL | Beschreibung |
---|---|
Subversion Home | Subversion home page – Informationen und Resourcen aus erster Hand: Handbuch, FAQ und mehr. |
HowTo WebDav | How To Configure Web Access To Subversion Repositories Using Apache. Enthält auch ein post-commit Hook-Skript, um über Commits per Mail zu informieren. |
SVN manual | Complete manual for SVN as of version 1.4. For other versions and languages, look here. |
CVS2SVN HowTo | HOWTO: smooth CVS to SVN migration (and back again) |
Subversion HowTo | A Subversion HowTo (German) |
SVN and Trac | Ein Tutorial zu Installation, Konfiguration und Administration eines Subversion- und Trac- Servers unter Linux. |
SVN Tutorial | Englischsprachiges, detailiertes Tutorial zur Benutzung des SVN Kommandozeilen-Clients - mit Links zu Serverinstallation und -konfiguration |
Apache and SVN | HOWTO Apache2 mit Subversion SVN und DAV (basierend auf Gentoo Linux) |
Wikipedia | Wikipedia Artikel über Subversion |
Comparisions
URL | Beschreibung |
---|---|
SVN vs CVS | The pros and cons. Vielleicht nicht mehr ganz aktuell, und auch nicht vollständig. Aber immerhin. |
Misc Versioning tools | Comparing miscellaneous versioning tools: CVS, SVN, Aegis, Arch and SVK |
Git vs. SVN | Git's Major Features Over Subversion |
Software
Graphical FrontEnds
URL | Beschreibung | Lizenz | OS |
---|---|---|---|
SmartSVN | Java basierendes grafisches FrontEnd für SVN | Commercial w/ Trial | Alles was Java kann |
KDESVN | Grafischer Client für KDE | GPL | Linux |
RapidSVN | Ein schnelles, in C++ geschriebenes grafisches FrontEnd | GPL | Linux, Windows |
KSVN | PlugIn für Konqueror (analog zu Tortoise für den Windows-Explorer) | GPL | Linux |
TortoiseSVN | Windows Shell Extension - aus dem Windows-Explorer mit SVN arbeiten | GPL | Windows |
SyncroSVN | Multiplatform Subversion FrontEnd | Commercial w/ Trial | Linux, Mac, Windows |
GSVN | Gnome FrontEnd für SVN | GPL | Linux |
Other SVN tools
URL | Beschreibung | Lizenz | OS |
---|---|---|---|
SVN2Log | ChangeLogs automatisch aus CommitLogs von Subversion erstellen | AFL | Alles was Python kann |
Commitfilter | Erstellt eine "Commit-Mailing-Liste", zu der man sich "subscriben" kann | GPL | Alles was Perl kann |
Easy SVN | Web FrontEnd für SVN (früher als WebSVN bekannt). Benötigt lediglich Perl und den SVN Kommandozeilen-Client | GPL | Linux, Mac, Windows |
ViewSVN | PHP basierendes Web-FrontEnd | GPL | Alles was PHP kann |
SVN::Web | Perl basierendes SVN Web FrontEnd | Perl | Alles was Perl kann |
Subversive | Eclipse PlugIn für SVN | Eclipse | Alles was Java kann |
Miscellaneous
URL | Beschreibung |
---|---|
ReposStyle.COM | XSLT Styles zum Browsen des Repositories via WebDav mittels eines Internet-Browsers |
SVN2CVS | Falls jemand wieder zurück will … |