Site Logo

IzzySoft


Das inoffizielle Android-HandbuchDas inoffizielle Android-Handbuch
Für 16,99 € bei Amazon kaufen
Das inoffizielle Android-SystemhandbuchDas inoffizielle Android-Systemhandbuch
Für 6,99 € bei Amazon kaufen
Die besten Android-Apps: Android-Systemtools. Fotografie & Freizeit. Büro-Tools, Schule und StudiumDie besten Android-Apps: Android-Systemtools. Fotografie & Freizeit. Büro-Tools, Schule und Studium
Für 2,94 € bei Amazon kaufen
Stand: 2024-03-28 04:07
Preis & Verfügbarkeit können sich geändert haben.
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:

Sicher: Wo es Licht gibt, ist auch Schatten. Dazu fiel mir nicht ganz so viel auf:

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

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:

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:

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 …
2018-12-16