Versionsverwaltung mit SVN
Im Verlauf eines Softwareprojektes entstehen viele Zeilen Code und Daten, die sinnvoll verwaltet werden wollen. So mag es bei einem Ein-Mann-Projekt noch möglich sein, in regelmäßigen Abständen Sicherungen anzulegen, um auf frühere Versionstände zurückzugreifen. Doch diese Variante der Versionsverwaltung läßt jeglichen Komfort vermissen und stellt ordentliche Anforderungen an Festplattenkapazitäten. Eine Versionsverwaltung muss her.
Subversion (SVN) ist eine solches Versionsverwaltungssystem. Dabei läuft auf einem Rechner im Netzwerk ein SVNServer, der gesicherten Zugriff auf ein Repository bietet. Ein Repository ist nicht anderes als eine Art Datenbank, in der alle Informationen für die Versionsverwaltung gespeichert werden. Das Repository kann in jeden beliebigen Ordner gespeichert werden.
Bevor der Entwickler die erste Zeile schreibt, benötigt er auf seinem Rechner einen SVN-Client, ein Programm das die Verbindung zu einem existierenden Repository herstellt. Dann muss auf dem eigenen Rechner ein leeres Verzeichnis erstellt werden, in das daraufhin eine lokale Arbeitskopie ausgecheckt (SVNCheckOut) wird. Diese Working-Copy (WC) kann nach Lust und Laune verändert werden ohne das sich dabei das Repository verändert. Erst bei einem 'Committed' werden die Änderungen, die in der eigenen WC gemacht wurden, in das Repository zurückgeschrieben. Das nachfolgende Bild zeigt, wie aus einer WC ausgecheckt und zweimal eingecheckt wurde. Dazwischen hat der Benutzer die Dateien auf seinem Rechner mehrmals verändert und gespeichert.


Jedes mal wenn der Benutzer committed wird die Revision im Repository hochgezählt. Dabei werden aber immer nur die Veränderungen seit dem letzten Commit gespeichert. Dadurch bleibt das Repository relativ klein. Es wird empfohlen bei jedem Commit einen sinnvollen Kommentar in einzugeben. Damit ist es später möglich sich in der 'History' einen schnellen Überblick zu gewinnen, was zwischen jedem Commit verändert wurde. Der Benutzer kann sich bei Bedarf (oder falls er sich vertan hat) eine ältere Revision aus dem Repository (anstatt der aktuellsten 'HEAD'-Revision) holen.


Es kann zu jeder eingecheckten Version einer Datei zurückgegangen werden, selbst wenn die Datei irgendwann einmal gelöscht wird. Denn SVN löscht niemals eine Datei im Repository. In der Version vor dem Löschen ist die Datei nachwievor vorhanden und kann immer zurückgeholt werden.

Merging
Natürlich arbeitet ein Entwickler zumeist nicht allein, sondern im Team. Daher werden mehrere Personen auf einer jeweils lokalen WC arbeiten. Im einfacheren Fall arbeitet jeder im Team an einer anderen Datei, sodass sich diese nicht in die Quere kommen. Doch sobald zwei Entwickler gleichzeitig eine Datei in Ihrer WC verändern, muss beim Commit in das Repository ein 'Merge' durchgeführt werden. Im nachfolgenden Beispiel verändern 'A' und 'B' eine Datei. Jeder fügt beispielsweise eine neue Funktion hinzu. Entwickler A kann seine Datei ohne Probleme einchecken. Entwickler B erhält eine Fehlermeldung, dass sich die Datei verändert hat:


Was hat 'B' vergessen? Richtig, er hätte seine WC mittels eines 'Updates' auf den aktuellsten Stand des Repositories bringen müssen. Dabei werden die Änderungen aus dem Repository in seine WC übernommen, ohne das sein eigener Code beschädigt wird. Dies gelingt dem SVN-Client meisten vollautomatisch (3-Way-Merge).


Das bedeutet, bevor die Teammitglieder ihre Ergebnisse in das Repository einchecken, müssen (sollten) sie immer ein Update ihrer WC auf den aktuellen Stand machen. Dann kann der Entwickler prüfen, ob der eigene Code mit den fremden Änderungen noch lauffähig ist und checkt letztendlich ein.

Build-Break
Es kommt auch vor, dass ein Teammitglied trotz sorgfältiger Prüfung Quellcode eincheckt, der nicht kompilierfähig ist oder Fehler enthält. Es ist ebenfalls möglich das das Teammitglied vergisst, eine einzelne Datei unter die Versionsverwaltung zu stellen ('Add' vergessen). Sobald sich die Anderen diesen fehlerhaften Versionsstand per Update holen werden sie diesen Fehler bemerken und ein Teammitglied muss die Korrektur wieder einchecken. Dies nennt man einen 'Build-break': Die anderen Teammitglieder können erst weiterarbeiten, wenn der Fehler behoben wurde.
Jeder Entwickler macht Fehler - schlechte Entwickler machen mehr, gute weniger. Ab einer gewissen Teamgröße wird der Arbeitsfluss jedoch ständig von build-breaks unterbrochen, das Repository muss ständig mit Bugfixes versorgt werden. Kein Entwickler kann mehr effektiv arbeiten und es wird nur noch korrigiert.

Branching
Daher bietet SVN das Konzept der 'Branches' an. Bisher gab es nur einen zentralen Zweig in den jeder Entwickler eingecheckt hat. Dieser wird häufig 'trunk', 'main' oder auch baseline genannt. Nun wird von diesem eine 'virtuelle' Kopie gemacht. Virtuell daher, weil sich dabei das Repository kaum in der Größe ändert. Es werden nur ein paar Verwaltungsdaten hinzugefügt. Im der nachfolgenden Abbildung werden vom trunk zwei Branches erzeugt. Während Team-A aus zwei Entwickler für ein komplexeres Modul besteht, arbeitet nur ein Entwickler im Team-B an einem kleineren Modul (oder auch Feature).


Aus den neu entstandenem Branches können dann ein oder mehrere Entwickler aus- und ein-checken ohne das der trunk verändert wird. Ein Fehler hat nun nur noch innerhalb des Branches Auswirkungen. Im Übrigen gibt es trotz verschiedener Branches nur eine einzige Revisionsnummer über das gesamte Repository. Jedes mal wenn eingecheckt wird sollte daher beim Commit-'Kommentar' ein Vermerk auf den Branch dazugeschrieben werden. Im folgenden werden die zahlreichen Änderungen in der WC weggelassen und nur noch die Commits dargestellt. Denn am Ende zählt nur das, was sicher im Repository liegt .


Vorwärtsintegration
Nun ist es so dass ein Branch längere Zeit bestehen kann. Während dieser Zeit bringen bereits andere Teams ihre Änderungen zurück in den Trunk. Ohne dass das eine Team es merkt, entfernt es sich immer mehr vom Code des restlichen Teams. Irgendwann werden die Unterschiede derart groß, dass eine Rückführung des Branches in den Trunk (Re-Integrate) nur unter enormen Aufwand vonstatten geht. Daher ist es empfehlenswert den Branch durch Vorwärts-Integration (FI) von Zeit zu Zeit auf den aktuellen Stand zu bringen (Synchronisieren). In der nachfolgenden Abbildung holt sich Team-A zweimal den aktuellen Stand des Trunks in den Branch.


Bei der FI werden die Änderungen nicht direkt von einem Branch in den anderen übernommen. Vielmehr muss zuerst eine WC vom Ziel-Branch ausgecheckt werden und dann die Änderungen mittels 'Merge a Range of Revisions' in die WC ge-mergt werden. Im letzten Schritt werden die Änderungen (die aus dem trunk kommen) in den Branch eingecheckt. SVN merkt sich in den 'merge-infos', welche ChangeSets aus dem Trunk übernommen wurden, sodass bei der nächsten FI nur noch die neu dazugekommenen gemergt werden müssen. Es ist übrigens auch möglich einzelne ChangeSets vom Trunk zu übernehmen. Es muss nicht immer der eine Synchronisation von der letzten FI bis zur HEAD-Revision durchgeführt werden! Im folgenden Beispiel wurde ein Branch für das Team A erzeugt und recht intensiv benutzt. Einige Zeit später haben Team B und C ihre Ergebnisse in den Trunk re-integriert. Nun möchte Team A diesen neuen Quellcode ebenfalls verwenden und führt daher eine FI durch.


Auch wenn sich der neue Quellcode nun im Branch befindet, müssen sicherlich noch einige Anpassungen am bisherigen Branch-Code vorgenommen werden. Oftmals arbeiten die neuen Codezeilen nicht optimal mit denen vom Branch zusammen. Daher sollte nun ein Teammitglied diese Anpassungen durchführen bevor die anderen Teammitglieder des Branches ein Update oder sogar einen kompletten (Neu-)Checkout des Branches durchführen.

Re-Integrate
Die Entwicklung geht für das Team A und ihren Branch/Feature in die finale Phase. Es kommt kein neuer Code hinzu, die Tests sind geschrieben und lauffähig. Nun erfolgt eine letzte FI, sodass sich Trunk und Branch nur noch durch die Codezeilen des Modules unterscheiden. Ein finaler Testlauf stellt sicher, dass das Modul immer noch korrekt arbeitet.


Im letzten Schritt wird eine saubere WC des Trunkes ausgecheckt. Dann werden die Änderungen von der ersten Revision des Branches bis zur letzten (also 1 und 2, von grün bis grün) in die WC des Trunkes gemergt (Re-Integrate Merge auswählen). Da Branch und Trunk zu diesem Zeitpunkt schon synchron waren und alle Änderungen (hier B und C) im Branch enthalten sind, verändert sich die WC des Trunkes nur um das, was das Team im Branch geschaffen hat. Die WC muss letztendlich nur noch eingecheckt werden.


Auf diese Art und Weise kommt mit einem Commit die gesamte Arbeit von Team A in den Trunk und es ist klar ersichtlich was das Team beigetragen hat. Es kann vorkommen, das der Entwickler die FI vor der RI 'vergisst'. Der Merge in den Trunk mag noch klappen, aber beim Commit hagelt es dann Konflikte. Der Quick-and-Dirty Way wäre, die notwendigen Bugfixes noch schnell in die WC des Trunks hineinzucodieren. Da dies aber letztendlich nur ein Weiterentwickeln im Trunk anstatt des Branches ist, fehlt dieser Bugfix dann im Branch. Es würde später so aussehen als wäre beim 'rüberkopieren' (ungetesteter) Code wie von Geisterhand dazugekommen. Es kann dann auch keine Revision angegeben, werden die getestet wurde, da der Commit in den Trunk erst NACH dem Testen erfolgen sollte. Was ist wenn der Entwickler bei seinem Bugfix einen Fehler der nicht gleich auffällt? Daher ist es ratsam eine FI immer durchzuführen.

Integrator
Die Rückwärtsintegration (letzte FI bis Commit) wird idealerweise immer nur von einer Person pro Zeit durchgeführt. Die Teams sollten ihre Branches immer nacheinander reintegrieren, denn nichts ist ärgerlicher als das der eigene Branch direkt nach einer FI schon wieder Out-Of-Date ist. In sehr großen Projekten mit unzähligen Modulen, reicht die Zeit für eine sequentielle Zurückführung nicht mehr aus. Daher gibt es die Methode 'Reintegrate-Merge', welche diesen Umstand berücksichtigt.

Serverhardware:
Der Quellcode besteht in der Regel aus einer Vielzahl kleiner Dateien von nur wenigen KByte Größe. Auf dem Arbeitsrechner als auch auf dem Server kann dies zu einem Engpass führen, da die Dateien über der Festplatte verstreut sind.
Daher ist der Zugriff meistens durch die Random-IO-Leistung der Festplatte begrenzt. Nachfolgend einige Tipps für den Server als auch für die Arbeitsrechner:
Server:
  • dedizierter Server(keine Virtualisierung, nur den SVN-Dienst)
  • Festplatte mit kurzen Zugriffszeiten und hoher Transferrate (Eventuell SSD)
  • Viel Arbeitsspeicher
  • schneller Prozessor für intelligente Kompression
Arbeitsrechner:
  • ausreichend Arbeitsspeicher für Cache-Prozess
  • schnelle Festplatte
  • Cacheprozess bekommt niedrigere Priorität


Tipps
  • Der Entwickler kann mehrere verschiedene Branches, oder auch nur Teile oder verschiedene Revisionen eines Branches auschecken um so in verschiedenen Teams teilzunehmen. So kann der Entwickler in einem Team die Implementierung durchführen, in einem anderen nur Reviews durchführen und in einem dritten die Tests vorbereiten.
  • SVN ist eine zentrale Versionsverwaltung und ist daher für die Entwicklung an einem Standort prädestiniert.