Nachdem ich in der letzten Zeit häufiger den Aufbau und die klassischen Arbeitsabläufe bei der Codeverwaltung eines mittelmäßig komplexen PHP Projekts zum Besten gegeben habe, stellte sich beim Betrachten der klassischen Struktur in einem svn Repository doch häufiger die Frage: Trunk, Branches, Tags … wass’n das und wofür brauch ich das überhaupt und was passiert eigentlich beim branchen, tagen, mergen, etc. ?

Dieser Artikel soll als praktischer Einstieg in die Codeverwaltung anhand eines svn Repositorys unter Verwendung des subclipse Plugins unter Eclipse dienen. Vorweg anzumerken ist natürlich, dass der von mir vorgestellte Arbeitsablauf sicher nicht der einzig „richtige“ Weg ist, sondern lediglich den Weg beschreibt, der sich für mich als praktikabel erwiesen hat und der, wie ich finde, für den Einstieg die wenigsten Fallstricke bereithält.

Bevor wir allerdings zu den praktischen Handgriffen kommen, betrachten wir zuerst einmal unsere eigentliche Problemstellung anhand eines hypothetischen Projekts und vergessen wir vorerst all die o.g. Verzeichnisse, Strukturen, etc.:

Die Entwickler der FrickelNReload AG haben das Projekt TOLL 1.0 nicht nur laut Spezifikation abgeliefert, sondern, weil sie clever waren, das ganze Projekt auch in einem svn-Repository abgelegt, um Änderungen zu dokumentieren, selbige zurückdrehen zu können, etc. Tag X kommt und die Software wird vom erwartungsvollen Anwendern in Betrieb genommen! Aus Erfahrung weiß man: Es wird kaum 10 Minuten dauern, da ist schon der erste Featurerequest für das nächste Release geschrieben. Und, auch wenn die Entwickler einen anständigen Haufen Testcases geschieben haben, wird sich wohl auch bald der erste Bug finden…

Da der Releaseplan für das nächste Release sehr ambitioniert ist, ist unmittelbar nach dem releasen von TOLL 1.0 mit der Entwicklung von TOLL 1.1 begonnen worden und auch die ersten Featurerequests wurden aufgenommen. Kaum haben die fleißigen Entwickler die Arbeit für das kommende Release aufgenommen und grundlegende Änderungen in einigen Teilbereichen begonnen, trudelt der erste Bugreport ein. Da der Bug kritisch ist, duldet das fixen keinen Aufschub, es muss sofort ein neues Release her. Im Handumdrehen haben wir ein Problem: Unser Code befindet sich aufgrund der Entwicklung von TOLL 1.1 in einem nicht konsistenten Zustand, so dass wir nicht ohne weiteres besagten Bug fixen können ohne sämtliche für die Entwicklung von TOLL 1.1 gemachte Änderungen zurückzudrehen bzw. gemachte Änderungen herauszuziehen, hinterher wieder zurückzupatchen oder was man sich da sonst noch verrücktes einfallen lassen mag.

Zugegeben, natürlich lässt sich diese Situation mit konservativen Methoden der FrickelNReload AG retten, besser allerdings ist, sich von Anfang an der klassischen Struktur eines mit svn verwalteten Projekts zu orientieren, die aus den Verzeichnissen Trunk, Branches und Tags besteht.

Wenn wir mal die Tags vernachlässigen, zu denen wir später kommen, dann hätte sich zum Zeitpunkt des Release von TOLL 1.0 lediglich im Trunk der Code von TOLL 1.0 befunden. Um nun die Entwicklung von TOLL 1.1 zu starten, wäre es sinnvoll gewesen, für diese Entwicklung einen eigenen Branch anzulegen. Dieser liegt, wer hätte es gedacht, im Verzeichnis Branches und ist im ersten Schritt, also bevor die Enwicklung von TOLL 1.1 beginnt, nichts weiter als eine Kopie des Trunks.

Nachdem also halbwegs klar sein sollte, warum wir ein solches Feuerwerk abbrennen, ein Blick auf die Praxis. Im Folgenden gehe ich mal davon aus, dass das Anlegen eines neuen Projekts im svn-Repository samt dazugehöriger Ordnerstruktur keine besondere Hürde darstellt; sollte hier Interesse bestehen, bitte ich um einen kurzen Kommentar, dann werde ich hier entsprechendes ergänzen.

Sehen wir uns also im Detail an, wie man einen Branch unter Eclipse mit Hilfe des Subclipse Plugins anlegt, wenn wir davon ausgehen, dass wir uns auf einer working copy des Trunks befinden:

Zuerst über den Punkt Team des Kontextmenüs den Punkt ‚Branch/Tag…‘ wählen:

Branch mit subclipse Step 1

Branch mit subclipse Step 1

Im nächsten Schritt gilt es, den Namen bzw. daraus resultierend den Speicherort des Branches zu wählen, wobei der Speicherort(bzw. der Name) unter „Copy to URL“ anzugeben ist. Defaultmäßig ist dort der Speicherort des Trunks vorgegeben, den man dann am einfachsten per Hand anpasst. Die weiteren Einstellungen bleiben wie vorgegeben:

branch_location

Branch mit subclipse Step 2

Typischerweise wird man wohl einen Branch auf Basis des aktuellen Trunks fahren wollen, sprich man belässt es beim vorgewählten HEAD revision of repository. Zusätzlich werden noch 2 weitere Optionen geboten, wobei mir die zweite noch nie in der Praxis untergekommen ist. Die dritte Option, working copy, macht Sinn, wenn man während eines Bugfixes im Trunk bemerkt, dass es mit einer kleinen Änderung dann doch nicht getan ist und man sich mit Hilfe eines Branches mehr Flexiblität verschafft.

Branch mit subclipse Step 3

Branch mit subclipse Step 3

Anschließend sollte man seinem Branch unbedingt noch einen Namen in Form eines Kommentars geben, um diesen später ansteuern zu können. Gleichzeitig kann man seine working copy durch einen Haken bei „Switch working copy to new branch/tag“ auf das durch Kopie erzeugte neue Verzeichnis setzen. In der Praxis hat es sich für mich allerdings bewährt, im Zweifel lieber Branch und Trunk in zwei einzelne working copies auszuchecken, da ansonsten das Potenzial für Verwirrung recht groß ist:

Branch mit subclipse Step 4

Branch mit subclipse Step 4

Nach dem Klick auf Finish haben wir unseren Branch gefahren, was sich spannender anhört, als es wirklich ist, denn eigentlich haben nichts weiter getan, als eine Kopie unseres Trunk angelegt und dieses Anlegen der Kopie mit einer Revisionsnummer und einem Kommentar versehen.

Ab sofort kann also im Branch „myBranch“ fleißig entwickelt werden und der Trunk steht bereit für das Fixen von Bugs.

Prinzipiell gibt es jetzt 2 Möglichkeiten, wie es weitergeht:

  1. Im Trunk werden Bugs gefixt und unser Branch wird unabhängig davon weiterentwickelt und nach Fertigstellung in den Trunk zurückgemerged.
  2. Bugs, die im Trunk gefixt wurden, werden umgehend in den Branch gemerged um deren Fehlerbereinigung dort zur Verfügung zu haben.

Auch wenn der zweite Fall auf den ersten Blick sicher der interessantere ist, werden wir es für dieses Beispiel beim ersten Fall belassen. Der zweite Fall hält nämlich das interessante Phänomen der Reflective/Cyclic merges bereit, welches erschöpfend in einem Artikel des svn Entwicklers Mark Phillard beschrieben ist und für diese Einführung ein wenig zu weit geht.

Gehen wir also davon aus, dass wir unseren Branch erfolgreich weiterentwickelt haben und ihn nunmehr in den Trunk mergen wollen. Dazu stellen wir zuallererst sicher, dass wir uns auf einer working copy des Trunk befinden und wählen dann über den Punkt Team des Kontextmenüs den Punkt ‚Merge…‘ :

Merge subclipse step 1

Merge mit subclipse Step 1

Im nachfolgenden Wizard wählen wir dann bei From: den Speicherort unseres Branches aus und beim To:  belassen wir es beim Häken bei „Use From:“. Da wir alle Änderungen des Branches übernehmen wollen, wählen wir beim From die Revision des Branches(hier ist dann o.g. Kommentar hilfreich) und beim To:  den HEAD. Um zu sehen was unser Merge produzieren würde stehen die Optionen Dry Run und Unified Diff zur Verfügung anhand derer man schon vor dem eigentlichen Merge sehen kann, ob ein Merge z.B. Konflikte produzieren würde.

Merge mit subclipse Step 2

Merge mit subclipse Step 2

Sollten Konflikte aufgetreten sein, so müssen diese nun aufgelöst werden. Über den Punkt Team des Kontextmenüs einer Datei mit Konflikten gelangen wir zu den Punkten ‚Edit conficts‘ und ‚Mark as resolved‘:

svn Konflikte bearbeiten

Konflikte mit subclipse auflösen

Mit dem ersteren gelangen wir zu einem Editor, der uns die Konfliktstellen der betreffenden Datei übersichtlich nebeneinander darstellt, mit dem zweiteren sagen wir subclipse nach Auflösung der Konflikte, dass wir mit dem Auflösen so glücklich sind und nun die gemergte Version der Datei verwandt werden kann.

Zum Schluss nochmal zu den eingangs kurz erwähnten Tags. Tag werden zum „merken“ von Versionen zu definierten Zeitpunkten verwandt. Wenn man sagt, man taggt eine Version bedeutet das nichts weiter als eine Kopie des Trunk bzw. eines Branches im Ordner Tags abzulegen, um den Stand einzufrieren. Im einfachsten Fall sollte man Tags von allen Release-Versionen eine Projekts haben, um z.B. auf Codestände der einzelne Releases unmittelbar zugreifen zu können. Um die Funktion des Merkers zu erfüllen, ist es notwendig, Tags nicht zu verändern, was technisch natürlich möglich wäre, da ein Tag erst per Definition zum Tag wird und ansonsten wie ein Branch zum Zeitpunkt des branchens zu betrachten ist.

In der Hoffnung ein wenig Licht ins Dunkel gebracht zu haben, wünsche ich somit viel Spaß beim ausprobieren und keine Angst, der „Revert“- Button ist nur einen Kick weit weg!

Und falls es zur Vertiefung etwas Leküre fürs Regal sein soll, empfehle ich das folgende Buch: Versionskontrolle mit Subversion welches von den Entwicklern von svn geschrieben wurde.