Einführung in die Versionsverwaltung mit SVN

SVN wird für OpenSLX und verwandte Projekte nicht mehr eingesetzt. Stattdessen wird GIT verwendet ...

Grundlagen

Versionierung

Der Grundlegende Aufbau einer Versionsnummer:

<major release>.<minor release>.<patch level>

Wann wird welcher Teil der Versionsnummer erhöht?

<major_release> gravierende Änderungen (z.B. API Änderungen)
<minor_release> normale funktionale Erweiterungen
<patch_level> für reine Bugfix Releases vorbehalten

Aufbau eines SVN Repositories

 /myapp
   |---branches
   |---tags
   `---trunk
trunk Hauptentwicklungszweig
tags Entwicklungsstände / Versionen
branches Nebenentwicklungszweige

Branches

Vor komplexen Änderungen erzeugt man einen sog. Branch, also eine Verzweigung, auf der die Entwicklung fortgesetzt wird, bis die komplexe Änderung fertig ist; dabei kann auf diesem Branch committet werden, ohne dass die Hauptentwicklungslinie (der sog. Trunk) davon beeinflusst wird. Abschließend erfolgt üblicherweise die Reintegration in den Trunk, der sog. Merge

Verzweigunsmuster

An dieser Stelle sollen die zwei gängigsten Verwendungszwecke des Branchens kurz Vorgestellt werden.

Release-Zweige

Die meiste Software hat einen typischen Lebenszyklus: Erstellung, Test, Freigabe und wieder von vorne. Bei diesem Prozess gibt es zwei Probleme. Erstens müssen Entwickler neue Funktionen schreiben, während das Qualitätssicherungsteam sich Zeit zum Testen der vermeintlich stabilen Software nimmt. Die Arbeit kann allerdings nicht liegenbleiben während die Software getestet wird. Zweitens muss das Team fast immer ältere, bereits an den Kunden herausgegebene Software unterstützen; falls im neuesten Quelltext ein Fehler entdeckt wird, besteht der Fehler wahrscheinlich auch in der herausgegebenen Version. Die Kunden möchten dann eine Fehlerbehebung, ohne auf ein größeres, neues Release zu warten.

Hier kann Versionskontrolle helfen. Die typische Vorgehensweise ist wie folgt:

  1. Entwickler übergeben alles Neue an den Stamm. Tägliche Änderungen werden an /trunk übergeben: neue Funktionen, Fehlerbehebungen usw.
  2. Der Stamm wird in einen „Release“-Zweig kopiert. Wenn das Team der Auffassung ist, dass die Software reif für eine Freigabe ist (z.B. Release 1.0 ), kann /trunk nach /branches/1.0 kopiert werden.
  3. Die Teams arbeiten parallel. Ein Team beginnt, den Release-Zweig sorgfältig zu testen, während ein anderes Team mit der Arbeit (z.B. für Release 2.0) in /trunk fortfährt. Falls hier oder dort Fehler entdeckt werden sollten, werden die Fehlerbehebungen nach Bedarf hin oder her kopiert. Zu einem gegebenen Zeitpunkt hört jedoch sogar dieser Prozess auf. Der Zweig wird für die Abschlusstests vor der Freigabe „eingefroren“.
  4. Der Zweig wird markiert und freigegeben. Nach dem Abschluss der Tests wird /branches/1.0 als Momentaufnahme nach /tags/1.0.0 kopiert. Das Tag wird paketiert und an den Kunden ausgeliefert.
  5. Der Zweig wird gepflegt. Während die Arbeit für Version 2.0 in /trunk weitergeht, werden weiterhin Fehlerbehebungen von /trunk nach /branches/1.0 portiert. Wenn sich ausreichend Fehlerbehebungen angesammelt haben, könnte sich das Management entschließen, ein Release 1.0.1 herauszugeben: /branches/1.0 wird nach /tags/1.0.1 kopiert, und das Tag wird paketiert und freigegeben.

Der gesamte Prozess wiederholt sich während die Software reift: Wenn die Arbeit an 2.0 fertig ist, wird ein neuer 2.0 Release-Zweig erstellt, getestet, markiert und schließlich freigegeben. Nach einigen Jahren füllt sich das Repository mit einer Anzahl von Release-Zweigen, die weiterhin „gepflegt“ werden, und einer Zahl von Tags, die den endgültigen, ausgelieferten Versionen entsprechen.

Funktions-Zweige

Im Regelfall sollte trunk stets lauffähig bleiben. Stehen also große Änderungen an oder es sollen alternative Lösungen erprobt werden, lohnt es sich einen zusätzlichen Branch zu erstellen und auf diesem zu entwickeln bis man zu einem stabilen Stand kommt, der dem Hauptzweig wieder zugeführt werden kann.

Tags

Ein Tag kennzeichnet einen konkreten Entwicklungsstand, z. B. eine zur Auslieferung bestimmte Version; er erhält einen Namen, über den er später noch verfügbar ist. Auf einem Tag finden keine Änderungen statt; stattdessen erzeugt man für einen neuen Versionsstand ein neues Tag.

Der Weg zum Release..

Das Vorgehen verläuft analog zu dem schon im Abschnitt Grundlagen - Release Zweig vorgestellten Verfahren.

Ausgangspunkt

Wir wollen von einer Applikation (im folgenden Beispiel: myapp) ein Release herausbringen.

 /myapp
   |---branches
   |---tags
   `---trunk

Der aktuelle Entwicklungsstand befindet sich vollständig in trunk.

Welche Versionen werden gebranched?

Welche Versionen gebranched werden, ist immer eine Frage der Größe des Projekts. Bei OpenSLX gehen wir davon aus, dass alle major und minor Versionen gebranched werden.

Entwicklungsstand kopieren / Releasezweig erzeugen

 # svn cp svn+ssh://foo/svn/myapp/trunk \
          svn+ssh://foo/svn/myapp/branches/1.0 \
          -m "creating 1.0 branch" 

Hinweis: svn copy kann direkt auf dem Server durchgeführt werden. (Geht auch wesentlich schneller als locales copy + commit ;))

Ergebnis:

 /myapp
   |---branches
   |    `---1.0
   |---tags
   `---trunk

Das aktuelle Arbeitsverzeichnis auf den Releasezweig umstellen (im Arbeitsverzeichnis ausführen):

 # svn switch svn+ssh://foo/svn/myapp/branches/1.0

Release Vorbereiten

Nun ist es an der Zeit zu entscheiden, welche Features das Release enthalten soll. Etwaiger Entwicklungscode der nicht mehr in das Release gelangen soll, wird aus dem Branch gelöscht. Die für das Release geplanten Features werden nun vollendet (sollten sie zumindest ;)).

Testing & Release Candidates

Hat der Releasezweig einen Stand erreicht, von dem man ausgeht, dass er reif für die Welt ist, wird der aktuelle Stand der Dinge als so genannter Release Candidate "getagged". (Bei größeren Projekten stehen vor den Release Candidates noch pre-alpha, alpha und beta Versionen)

Das Tagging verläuft wie folgt:

 # svn cp svn+ssh://foo/svn/myapp/branches/1.0 \
          svn+ssh://foo/svn/myapp/tags/1.0.0-RC1 \
          -m "tagging 1.0.0-RC1 " 

Die Release Candidates werden nun getestet. Sind alle aufgetretenen Bugs behoben, wird der aktuelle Stand als der "eigentliche" Release getagged.

Sollten zu einem späteren Zeitpunkt weitere Bugs auftauchen und der Revisionzweig noch unterstützt werden, werden diese behoben und ein Patch-Release herausgebenen. (Mittels "tagging" - z.B. auf 1.0.1)

Bugfixes & "Interaktion" zwischen den Branches

Je nachdem, auf welches Verfahren sich die Entwickler geeinigt haben, werden Bugs entweder im Entwicklerzweig (trunk) oder im Releasezweig behoben. Um diese Änderungen dann auch im entsprechend anderen Branch zu aktualisieren, werden diese "gemerged".

Wir gehen im folgenden davon aus, dass die Bugs im Releasebranch gefixed werden. Somit bekommen wir keine Probleme mit Commits, die zusätzlich Änderungen von im Releasebranch nicht enthaltenen Features enthalten.

Beispiel: Wir haben im trunk einen Bug gefixed (Revision 42). Jetzt wollen wir diesen Bug im Releasezweig ebenfalls beheben.

 # svn merge -c 42 svn+ssh://foo/svn/myapp/trunk

The "big picture"

                                                                               ,-----..
                                                                              /
                                                                             /
 [initial]---[branch-1.0]--------------------[(contine-development)]-----[branch-2.0]------..
                   \                        /                       / 
                    \               [merge-bugfixes]   ...   [merge-bugfixes]
                     \                    /                       /   
                      `----[tag-1.0.0-rc1]---..---[tag-1.0.0-rcN]---[tag-1.0.0]---[tag-1.0.1]---..

  ------>
   time

Vendor-Branches

Sollten wir vielleicht hier auch nochmal kurz beschreiben.
Bis dahin siehe: http://svnbook.red-bean.com/en/1.1/ch07s05.html

Links

Quellen