Dieser Text befindet sich gegenwärtig in Bearbeitung, unterliegt ständigen Änderungen und kann dadurch nicht stets akkurat irgendeine freigegebene Version der Software Apache™ Subversion® beschreiben. Das Speichern dieser Seite als Lesezeichen oder andere auf diese Seite zu verweisen, ist keine so gute Idee. Besuchen Sie http://www.svnbook.com/, um stabile Versionen dieses Buchs zu erhalten.

Grundlegendes Zusammenführen

Nun arbeiten Sie und Sally auf parallelen Zweigen des Projektes: Sie arbeiten auf einem privaten Zweig, und Sally arbeitet auf dem Stamm (Trunk) oder dem Hauptzweig der Entwicklung.

Bei Projekten mit einer großen Zahl von Mitarbeitern besitzen die meisten gewöhnlich Arbeitskopien vom Stamm. Sobald jemand eine langwierige Änderung machen muss, die wahrscheinlich den Stamm stören würde, ist die Vorgehensweise Standard, einen Zweig zu erzeugen und die Änderungen bis zum Abschluss der Arbeiten nach dorthin zu übertragen.

Die gute Nachricht ist also, dass Sie und Sally sich nicht in die Quere kommen. Die schlechte Nachricht ist, dass es sehr leicht ist, zu weit auseinander zu treiben. Erinnern Sie sich, dass eins der Probleme bei der Strategie sich in ein Loch verkriechen darin bestand, dass es zu dem Zeitpunkt, an dem Sie mit dem Zweig fertig sind, fast unmöglich sein kann, Ihre Änderungen ohne eine riesige Zahl an Konflikten auf den Stamm zurückzuführen.

Stattdessen könnten Sie und Sally fortfahren, während der Arbeit Änderungen gemeinsam zu verwenden. Es liegt an Ihnen, zu entscheiden, welche Änderungen teilenswert sind; Subversion bietet Ihnen die Fähigkeit, Änderungen selektiv zwischen Zweigen zu kopieren. Und wenn Sie mit Ihrem Zweig vollständig fertig sind, kann die gesamte Menge Ihrer Änderungen vom Zweig auf den Stamm zurück kopiert werden. In der Terminologie von Subversion heißt der allgemeine Vorgang, Änderungen von einem Zweig auf einen anderen zu übertragen Zusammenführung (Merge) und wird durch verschiedene Aufrufe des Unterbefehls svn merge durchgeführt.

In den folgenden Beispielen gehen wir davon aus, dass sowohl auf Ihrem Subversion-Client als auch auf dem Server Subversion 1.8 (oder neuer) läuft. Falls einer von beiden älter als Version 1.5 ist, wird es komplizierter: Das System wird Änderungen nicht automatisch mitverfolgen, so dass Sie schmerzhafte manuelle Methoden anwenden müssen, um ähnliche Resultate zu erzielen. Dass heißt, dass Sie stets die detaillierte Syntax beim Zusammenführen verwenden müssen, um bestimmte Revisionsintervalle zu übertragen (siehe „Merge-Syntax: Die vollständige Offenlegung“ weiter unten in diesem Kapitel), und besonders sorgfältig verfolgen müssen, was bereits zusammengeführt ist und was nicht. Aus diesem Grund empfehlen wir Ihnen dringend, sicherzustellen, dass Ihr Client und Server mindestens die Version 1.5 haben.

Änderungsmengen

Bevor wir weitermachen, sollten wir Sie warnen, dass Sie auf den kommenden Seiten viele Erörterungen zum Thema Änderungen erwarten. Viele mit Versions-Kontroll-Systemen erfahrene Leute benutzen die Begriffe Änderung und Änderungsmenge (Changeset) austauschbar, so dass wir klären sollten, was Subversion unter einer Änderungsmenge versteht.

Jeder scheint eine etwas unterschiedliche Definition für den Begriff Änderungsmenge zu haben oder zumindest eine unterschiedliche Erwartung darüber, was es für ein Versions-Kontroll-System bedeutet, so etwas zu besitzen. Für unsere Zwecke reicht es aus, zu sagen, dass eine Änderungsmenge lediglich eine Sammlung von Änderungen mit einem eindeutigen Namen ist. Die Änderungen können aus der Bearbeitung an Textdateien, Modifizierungen an der Baumstruktur oder Justierungen an Metadaten bestehen. In einfachen Worten ist eine Änderungsmenge einfach ein Patch mit einem Namen, auf den Sie sich beziehen können.

In Subversion bezeichnet eine globale Revisionsnummer N einen Baum im Projektarchiv: Sie beschreibt das Aussehen des Projektarchivs nach der N-ten Übertragung. Sie ist auch der Name einer impliziten Änderungsmenge: Wenn Sie den Baum N mit dem Baum N-1 vergleichen, können Sie genau den Patch ableiten, der übertragen wurde. Daher ist es einfach, sich Revision N nicht nur als Baum sondern auch als Änderungsmenge vorzustellen. Falls Sie ein Fehlerverwaltungssystem verwenden, können Sie die Revisionsnummern benutzen, um auf bestimmte Patches zu verweisen, die Fehler beheben – zum Beispiel: Dieser Fehler wurde durch r9238 behoben. Dann kann jemand svn log -r 9238 aufrufen, um den Protokolleintrag zu genau der Änderungsmenge zu lesen, die den Fehler behoben hat, und sich mit svn diff -c 9238 den eigentlichen Patch ansehen. Und (wie Sie bald sehen werden) auch der Subversion Befehl svn merge kann Revisionsnummern verwenden. Sie können bestimmte Änderungsmengen von einem Zweig mit einem anderen zusammenführen, indem sie in den Argumenten zum entsprechenden Kommando benannt werden: Die Übergabe von -c 9238 an svn merge würde das Änderungsmenge r9238 mit Ihrer Arbeitskopie zusammenführen.

Einen Zweig synchron halten

Machen wir mit unserem Beispiel weiter und nehmen an, dass eine Woche vergangen ist seitdem Sie mit der Arbeit auf Ihrem privaten Zweig begonnen haben. Ihre Arbeit ist noch nicht beendet, jedoch wissen Sie, dass gleichzeitig andere Leute in Ihrem Team weiterhin wichtige Änderungen in /trunk des Projektes machen. Es ist in Ihrem Interesse, diese Änderungen in Ihren Zweig zu übernehmen, um sicherzustellen, dass sie sich gut mit Ihren Änderungen vertragen. Das wird durch einen Synchronisierungs-Merge erreicht – ein Merge zu dem Zweck, Ihren Zweig mit Änderungen zu aktualisieren, die seit der Erstellung Ihres Zweigs auf dem Ursprungszweig vorgenommen wurden. Ein automatischer Merge ist ganz einfach einer, bei dem Sie das absolute Minimum an benötigten Informationen für einen Merge angeben (d.h., eine einzelne Merge-Quelle und ein Arbeitskopie-Ziel) und Subversion entscheiden lassen, welche Änderungen zusammengeführt werden müssen – bei einem automatischen Merge werden keine Änderungsmengen mit -r oder -c an svn merge übergeben.

[Tipp] Tipp

Ihren Zweig regelmäßig mit der Hauptentwicklungslinie zu synchronisieren hilft, überraschende Konflikte zu vermeiden, wenn es an der Zeit ist, Ihre Änderungen zurück auf den Stamm zu bringen.

Subversion kennt die Geschichte Ihres Zweigs und weiß, wann Sie ihn vom Stamm abgezweigt haben. Um eine Synchronisierungs-Zusammenführung zu machen, sollten Sie zunächst sicherstellen, dass die Arbeitskopie des Zweigs sauber ist – dass sie keine lokalen Änderungen hat, die durch svn status angezeigt werden. Dann rufen Sie einfach die folgenden Befehle auf:

$ pwd
/home/user/my-calc-branch

$ svn merge ^/calc/trunk 
-- Zusammenführen von r341 bis r351 in ».«:
U    doc/INSTALL
U    src/real.c
U    src/button.c
-- Aufzeichnung der Informationen für Zusammenführung von r341 bis r356 in ».«:
 U   .
$

Diese einfache Syntax – svn merge URL – fordert Subversion auf, alle Änderungen von dem URL, die vorher noch nicht zusammengeführt wurden, mit dem aktuellen Arbeitsverzeichnis (welches typischerweise das Wurzelverzeichnis Ihrer Arbeitskopie ist) zusammenzuführen. Beachten Sie auch, dass wir die Syntax mit dem Zirkumflex (^) verwenden[35], um nicht den vollständigen /trunk-URL tippen zu müssen. Beachten Sie ebenfalls die Mitteilung Aufzeichnung der Informationen für Zusammenführung…. Das teilt Ihnen mit, dass durch den Merge die Eigenschaft svn:mergeinfo aktualisiert wird. Wir werden sowohl diese Eigenschaft als auch diese Mitteilungen später in diesem Kapitel besprechen, und zwar in „Mergeinfo und Vorschauen“.

[Tipp] Tipp

In diesem Buch und anderswo (Subversion Mailing-Listen, Artikeln über Merge-Tracking usw.) wird Ihnen oft der Begriff Mergeinfo begegnen. Das ist einfach die Abkürzung für die Eigenschaft svn:mergeinfo.

Nach dem Ausführen des vorangegangenen Beispiels enthält Ihre Arbeitskopie nun neue lokale Änderungen, die Nachbildungen all der Änderungen auf dem Stamm seit der Erstellung Ihres Zweiges sind:

$ svn status
 M      .
M       Makefile
M       doc/INSTALL
M       src/button.c
M       src/real.c

Zu diesem Zeitpunkt ist es weise, sich die Änderungen mithilfe von svn diff sorgfältig anzusehen, und anschließend die Software von Ihrem Zweig zu bauen und zu testen. Beachten Sie, dass auch das aktuelle Arbeitsverzeichnis (.) verändert wurde; svn diff zeigt an, dass seine Eigenschaft svn:mergeinfo angelegt wurde.

$ svn diff --depth empty .
Index: .
===================================================================
--- .   (Revision 351)
+++ .   (Arbeitskopie)

Eigenschaftsänderungen: .
___________________________________________________________________
Hinzugefügt: svn:mergeinfo
   Zusammengeführt /calc/trunk:r341-351

Diese neue Eigenschaft ist ein wichtiges Metadatum in Zusammenhang mit Zusammenführungen, das Sie nicht anfassen sollten, da es von künftigen svn merge-Befehlen benötigt wird. (Wir werden später in diesem Kapitel mehr über diese Metadaten erfahren.)

Nach der Übernahme kann es möglich sein, dass Sie noch einige Konflikte auflösen müssen – wie bei svn update – oder möglicherweise noch einige kleinere Bearbeitungen durchzuführen haben, damit alles wieder funktioniert. (Denken Sie daran, dass die Abwesenheit syntaktischer Konflikte nicht bedeutet, dass keine semantischen Konflikte vorhanden sind!) Falls ernsthafte Probleme auftauchen, können Sie jederzeit die lokalen Änderungen mit svn revert . -R wieder rückgängig machen und eine lange was geht hier eigentlich vor-Unterredung mit Ihren Mitarbeitern führen. Falls jedoch alles gut aussieht, können Sie die Änderungen zum Projektarchiv übertragen:


$ svn commit -m "Die letzten Änderungen von trunk mit my-calc-branch synchronisiert."
Sende              .
Sende              Makefile
Sende              doc/INSTALL
Sende              src/button.c
Sende              src/real.c
Übertrage Daten ..
Revision 352 übertragen.

An dieser Stelle ist Ihr Zweig synchron mit dem Stamm, und Sie können sich ruhig zurücklehnen in der Gewissheit, dass Sie sich nicht zu weit von der Arbeit aller anderen entfernen, während Sie isoliert weiterarbeiten.

Nehmen wir an, noch eine Woche sei ins Land gegangen. Sie haben weitere Änderungen an Ihren Zweig übertragen, und Ihre Kollegen haben damit weitergemacht, den Stamm zu verbessern. Nun möchten Sie mal wieder die letzten Änderungen vom Stamm mit Ihrem Zweig abgleichen, damit Sie wieder synchron sind. Starten Sie einfach noch einmal den svn merge-Befehl!

$ svn merge ^/calc/trunk 
Kann nicht in eine Arbeitskopie mit verschiedenen Revisionen zusammenführen [\
352:357], versuchen Sie erst zu aktualisieren
$

Oh, das war jetzt aber unerwartet! Nachdem Sie während der letzten Woche Änderungen auf Ihrem Zweig gemacht haben, sehen Sie sich nun einer Arbeitskopie gegenüber, die eine Mischung von Revisionen enthält (siehe „Arbeitskopien mit gemischten Revisionen“). Mit Subversion 1.7 und neuer verhindert der Unterbefehl svn merge standardmäßig die Zusammenführung in Arbeitskopien mit gemischten Revisionen. Ohne auf Einzelheiten einzugehen, liegt das an Einschränkungen der Art und Weise, wie Merges durch die Eigenschaft svn:mergeinfo verfolgt werden (für Details, siehe „Mergeinfo und Vorschauen“). Diese Einschränkungen bedeuten, dass Merges in Arbeitskopien aus gemischten Revisionen unerwartete Text- und Baumkonflikte hervorrufen können.[36] Wir möchten keine unnötigen Konflikte, also aktualisieren wir die Arbeitskopie und versuchen erneut die Zusammenführung.

$ svn up
Aktualisiere ».«:
Revision 361.

$ svn merge ^/calc/trunk
--- Zusammenführen von r352 bis r361 in ».«:
U    src/real.c
U    src/main.c
-- Aufzeichnung der Informationen für Zusammenführung von r352 bis r361 in ».«:
 U   .

Subversion weiß, welche Änderungen Sie vorher mit Ihrem Zweig abgeglichen haben, so dass es sorgfältig nur die Änderungen berücksichtigt, die Sie noch nicht haben. Einmal mehr müssen Sie bauen, testen und die lokalen Änderungen an Ihren Zweig mit svn commit übertragen.

Teilbaum-Merges und -Mergeinfo

In den meisten Beispielen dieses Kapitels liegt das Ziel einer Zusammenführung im Wurzelverzeichnis eines Zweigs (siehe „Was ist ein Zweig?“). Obwohl es sich dabei um eine empfohlene Vorgehensweise handelt, kann es vorkommen, dass Sie gelegentlich einen Merge direkt in irgendein Kindverzeichnis der Wurzel des Zweigs vornehmen müssen. Diese Art der Zusammenführung wird Teilbaum-Merge genannt, und die aufgezeichneten Informationen dazu Teilbaum-Mergeinfo. Es gibt weder Besonderheiten bei Teilbaum-Merges noch bei Teilbaum-Mergeinfo. Es gibt hierbei tatsächlich nur einen wichtigen Punkt bei diesen Konzepten zu beachten: die vollständigen Aufzeichnungen über Zusammenführungen auf einen Zweig müssen nicht notwendigerweise in den Informationen der Wurzel des Zweigs vorliegen. Für eine vollständige Übersicht müssen Sie alle Teilbaum-Mergeinfos berücksichtigen. Glücklicherweise erledigt das Subversion für Sie, so dass Sie sich in den seltensten Fällen selbst darum kümmern müssen. Ein kurzes Beispiel hilft bei der Erklärung:

# Wir müssen r958 von trunk und branches/proj-X/doc/INSTALL
# zusammenführen, doch beeinflusst diese Revision auch main.c, das
# wir jedoch nicht zusammenführen wollen:
$ svn log --verbose --quiet -r 958 ^/
------------------------------------------------------------------------
r958 | bruce | 2011-10-20 13:28:11 -0400 (Do, 20. Okt 2011)
Geänderte Pfade:
   M /trunk/doc/INSTALL
   M /trunk/src/main.c
------------------------------------------------------------------------

# Kein Problem. Wir führen einen Teilbaum direkt auf der Datei INSTALL
# zusammen, doch merken uns zunächst, welche
# Mergeinfo für die Wurzel des Zweigs vorhanden
# ist:
$ cd branches/proj-X

$ svn propget svn:mergeinfo --recursive
Eigenschaften zu ».«:
  svn:mergeinfo
    /trunk:651-652

# Nun führen wir den Teilbaum zusammen; beachten Sie, dass sowohl
# Quelle als auch Ziel auf  INSTALL zeigen:
$ svn merge ^/trunk/doc/INSTALL doc/INSTALL -c 958
-- Zusammenführen von r958 in »doc/INSTALL«:
U    doc/INSTALL
-- Aufzeichnung der Informationen für Zusammenführung von r958 in »doc/INSTALL«:
 G   doc/INSTALL

# Sobald die Zusammenführung abgeschlossen ist, gibt es Mergeinfo
# an INSTALL:
$ svn propget svn:mergeinfo --recursive
Eigenschaften zu ».«:
  svn:mergeinfo
    /trunk:651-652
Eigenschaften zu »doc/INSTALL«:
  svn:mergeinfo
    /trunk/doc/INSTALL:651-652,958

# Was, wenn wir und dann entscheiden, alles von r958 haben zu wollen?
# Ganz einfach, alles, was wir tun müssen, ist, die Zusammenführung
# dieser Revision zu wiederholen, diesen mal allerdings auf der Wurzel
# des Zweigs. Subversion erkennt die Mergeinfo des Teilbaums an
# INSTALL und versucht nicht, irgendwelche Änderungen
# hineinzubringen, sondern es führt nur die Änderungen an main.c
# zusammen:
$ svn merge ^/subversion/trunk . -c 958
-- Zusammenführen von r958 in ».«:
U    src/main.c
-- Aufzeichnung der Informationen für Zusammenführung von r958 in ».«:
 U   .
-- Entfernung der Zusammenführungsinformationen von »doc/INSTALL«:
 U   doc/INSTALL

Sie haben sich vielleicht gefragt, warum im obigen Beispiel INSTALL Mergeinfo für r651-652 besitzt, wenn wir nur r958 zusammengeführt haben. Das liegt an der Vererbung der Mergeinfo, die wir in Vererbung von Mergeinfo behandeln. Beachten Sie weiterhin, dass die Teilbaum-Mergeinfo von doc/INSTALL entfernt wurde. Das nennt sich Entfernung von Mergeinfo und passiert immer dann, wenn Subversion redundante Teilbaum-Mergeinfo entdeckt.

[Tipp] Tipp

Vor Subversion 1.7 haben Zusammenführungen bedingungslos die gesamte Teilbaum-Mergeinfo unter dem Ziel aktualisiert, um den Merge zu beschreiben. Für Anwender mit umfangreicher Teilbaum-Mergeinfo bedeutete das, dass auch relativ einfache Zusammenführungen (z.B., bei der ein Diff auf eine einzige Datei angewendet wurde) Änderungen der Mergeinfo in jedem Teilbaum nach sich zog, selbst bei denen, die keine Eltern des betroffenen Pfades waren. Dies führte zu einiger Verwirrung und Frustration. Subversion 1.7 und neuer geht dieses Problem an, indem es nur die Mergeinfo an den Teilbäumen aktualisiert, die Eltern der vom Merge betroffenen Pfade sind (d.h., durch die Anwendung der Differenz geänderte, hinzugefügte oder gelöschte Pfade, siehe „Merge-Syntax: Die vollständige Offenlegung“). Die einzige Ausnahme von diesem Verhalten betrifft das eigentliche Ziel der Zusammenführung; dessen Mergeinfo wird stets aktualisiert, um den Merge zu beschreiben, auch wenn die angewandte Differenz keine Änderung nach sich zog.

Reintegration eines Zweigs

Was passiert jedoch, wenn Sie schließlich Ihre Arbeit abgeschlossen haben? Ihre neue Funktion ist fertig, und Sie sind bereit, die Änderungen von Ihrem Zweig zurück auf den Stamm zu überführen (so dass Ihr Team die Früchte Ihrer Arbeit genießen kann). Die Vorgehensweise ist einfach. Zunächst synchronisieren Sie Ihren Zweig noch einmal mit dem Stamm, wie Sie es bisher gemacht haben:[37]


$ svn up # (make sure the working copy is up to date)
Updating '.':
At revision 378.

$ svn merge ^/calc/trunk
--- Zusammenführen von r362 bis r378 in ».«:
U    src/main.c
-- Aufzeichnung der Informationen für Zusammenführung von r381 bis r385 in ».«:
 U   .

$ # bauen, testen, ...

$ svn commit -m "Endgültiger Merge der trunk-Änderungen nach my-calc-branch."
Sende              .
Sende              src/main.c
Übertrage Daten .
Revision 379 übertragen.

Verwenden Sie den Unterbefehl svn merge, um Ihre Änderungen automatisch zurück auf den Stamm zu replizieren. Diese Art von Merge wird automatischer Reintegrations-Merge genannt. Sie benötigen eine Arbeitskopie von /calc/trunk. Sie bekommen sie entweder durch svn checkout, indem Sie von irgendwo auf Ihrer Platte eine alte Arbeitskopie vom Stamm hervorkramen, oder den Befehl svn switch (siehe „Zweige durchlaufen“) verwenden.

[Tipp] Tipp

Der Begriff Reintegration kommt von der merge-Option --reintegrate. Diese Option gilt in Subversion 1.8 (das automatisch erkennt, wann ein Reintegrations-Merge notwendig ist) als veraltet, wird jedoch für Clients von Subversion 1.5 bis 1.7 benötigt, wenn Reintegrations-Merges ausgeführt werden.

Ihre Arbeitskopie darf keine lokalen Änderungen beinhalten oder aus gemischten Revisionen bestehen (siehe „Arbeitskopien mit gemischten Revisionen“). Obwohl es sich dabei normalerweise um die bewährten Vorgehensweisen beim Zusammenführen handelt, sind sie bei automatischen Reintegrations-Merges zwingend notwendig.

Sobald Sie eine saubere Arbeitskopie des Stamms haben, sind Sie bereit, Ihren Zweig damit zusammenzuführen:

$ pwd
/home/user/calc-trunk

$ svn update
Aktualisiere ».«:
Revision 390.

$ svn merge ^/calc/branches/my-calc-branch 
-- Zusammenführen der Unterschiede zwischen Projektarchiv-URLs in ».«:
U    src/real.c
U    src/main.c
U    Makefile 
-- Aufzeichnung der Informationen für Zusammenführung zwischen Projektarchiv-URLs in ».«:
 U   .

$ # bauen, testen, überprüfen, ...

$ svn commit -m "my-calc-branch mit Stamm zusammenführen!"
Sende              .
Sende              Makefile
Sende              src/main.c
Sende              src/real.c
Übertrage Daten ..
Revision 380 übertragen.

Gratulation! Ihre Änderungen vom Zweig sind nun zurück in die Hauptentwicklungslinie überführt worden. Beachten Sie, dass der automatische Reintegrations-Merge etwas anderes ist, als was Sie bisher gemacht haben. Vorher haben wir svn merge aufgefordert, die nächste Änderungsmenge von einer Entwicklungslinie (dem Stamm) zu holen und sie mit einer anderen (Ihrem Zweig) abzugleichen. Das ist recht überschaubar, und Subversion weiß jedes Mal, wo es wieder ansetzen soll. Bei unseren vorangehenden Beispielen können Sie sehen, dass es erst die Intervalle 341:351 von /calc/trunk nach /calc/branches/my-calc-branch überführte; später fuhr es mit dem nächsten verfügbaren aufeinanderfolgenden Intervall 351:361 fort. Wenn Sie die letzte Synchronisierung machen, wird es das Intervall 361:378 zusammenführen.

Wenn Sie jedoch /calc/branches/my-calc-branch auf /calc/trunk zurückführen, sehen die dem zugrundeliegenden Berechnungen ganz anders aus. Ihr Zweig ist nun ein Mischmasch aus abgeglichenen Änderungen vom Stamm und privaten Änderungen auf dem Zweig, so dass es kein einfaches, aufeinanderfolgendes Intervall mit Revisionen zum Herüberkopieren gibt. Indem Sie einen automatischen Merge verwenden, fordern Sie Subversion auf, sorgfältig nur die Änderungen von Ihrem Zweig zu replizieren. (Und tatsächlich macht es das so, dass es die letzte Version auf dem Stamm mit der letzten Version auf dem Zweig vergleicht: Der Unterschied macht genau die Änderung auf dem Zweig aus!)

Beachten Sie, dass automatische Reintegrations-Merges nur die oben beschriebenen Anwendungsfälle unterstützen. Wegen dieses engen Einsatzgebietes sowie der vorher erwähnten Voraussetzungen (aktualisierte Arbeitskopie[38] ohne gemischte Revisionen, umgeschaltete Pfade oder lokale Änderungen), wird diese Option nicht mit den meisten anderen Optionen von svn merge funktionieren. Sie werden einen Fehler bekommen, falls Sie andere nicht-globale Optionen verwenden, außer den folgenden: --accept, --dry-run, --diff3-cmd, --extensions oder --quiet.

Nachdem nun Ihr privater Zweig mit dem Stamm zusammengeführt wurde, können Sie ihn aus dem Projektarchiv löschen:

$ svn delete ^/calc/branches/my-calc-branch \ 
      -m "my-calc-branch entfernt, auf Stamm zurückgeführt in r391."
…

Aber halt! Ist die Geschichte des Zweigs nicht wertvoll? Was, wenn jemand sich eines Tages die Evolution Ihrer Funktion ansehen möchte und hierfür auf die Änderungen des Zweiges schauen möchte? Keine Sorge! Denken Sie daran, dass, obwohl Ihr Zweig nicht mehr im Verzeichnis /calc/branches sichtbar ist, seine Existenz gleichwohl ein unveränderbarer Teil der Geschichte des Projektarchivs ist. Ein einfacher Befehl svn log auf dem /calc/branches URL wird die gesamte Geschichte des Zweiges anzeigen. Ihr Zweig kann eines Tages sogar wiederbelebt werden, sollten Sie dieses wünschen (siehe „Zurückholen gelöschter Objekte“).

Falls Sie sich entscheiden, den Zweig nach der Reintegration auf den Stamm nicht zu löschen, können Sie mit Synchronisierungs-Merges vom Stamm fortfahren und dann den Zweig erneut Reintegrieren.[39] Falls Sie das machen, werden nur die Änderungen, die Sie nach dem ersten Reintegrieren auf dem Zweig vorgenommen haben, mit dem Stamm zusammengeführt.

Mergeinfo und Vorschauen

Der grundsätzliche Mechanismus, den Subversion verwendet, um Änderungsmengen zu verfolgen – d.h., welche Änderungen auf welchen Zweig übertragen worden sind – besteht aus der Datenspeicherung in versionierten Eigenschaften. Namentlich werden Daten die das Zusammenführen betreffen in der Eigenschaft svn:mergeinfo festgehalten, die mit Dateien und Verzeichnissen verknüpft ist. (Falls Sie mit Subversion-Eigenschaften nicht vertraut sind, siehe „Eigenschaften“.)

Sie können sich die mergeinfo-Eigenschaft ansehen, wie jede andere versionierte Eigenschaft:

$ cd my-calc-branch

$ svn pg svn:mergeinfo -v
Properties on '.':
  svn:mergeinfo
    /calc/trunk:341-378
[Warnung] Warnung

Obwohl es möglich ist, svn:mergeinfo zu ändern wie jede andere versionierte Eigenschaft, raten wir dringend davor ab, es sei denn, Sie wissen wirklich, was Sie da machen.

[Tipp] Tipp

Der Umfang von svn:mergeinfo auf einem einzelnen Pfad kann recht groß werden, so wie die Ausgabe eines svn propget --recursive oder svn proplist --recursive falls man es mit umfangreicher Mergeinfo auf Teilbäumen zu tun hat, siehe „Teilbaum-Merges und -Mergeinfo“. Die durch die Option --verbose erzeugte formatierte Ausgabe mit jedem dieser Unterbefehle ist in diesen Fällen oft sehr hilfreich.

Die Eigenschaft svn:mergeinfo wird automatisch von Subversion gepflegt, sobald Sie den Befehl svn merge ausführen. Ihr Wert gibt Aufschluss darüber, welche Änderungen an einem gegebenen Pfad mit dem in Frage kommenden Verzeichnis abgeglichen wurden. Im vorangegangenen Beispiel ist /calc/trunk der Ursprungspfad der zusammengeführten Änderungen und /calc/branches/my-calc-branch das Zielverzeichnis. Frühere Versionen von Subversion pflegten die Eigenschaft svn:mergeinfo im Hintergrund. Sie konnten die Änderungen nach einem abgeschlossenen Merge mit svn diff oder svn status zwar entdecken, doch gab die eigentliche Zusammenführung keinerlei Hinweis, dass sie die Eigenschaft svn:mergeinfo geändert hat. In Subversion 1.7 und später trifft das nicht mehr zu, da es verschiedene Mitteilungen gibt, um Sie zu benachrichtigen, wenn ein Merge die Eigenschaft svn:mergeinfo ändert. Diese Mitteilungen beginnen alle mit -- Aufzeichnung der Informationen für Zusammenführung und tauchen am Ende einer Zusammenführung auf. Im Gegensatz zu anderen Mitteilungen bei Merges beschreiben diese kein Anwenden eines Diffs auf eine Arbeitskopie (siehe „Merge-Syntax: Die vollständige Offenlegung“), sondern Änderungen zur Buchhaltung, um festzuhalten, was zusammengeführt wurde.

Subversion stellt auch den Unterbefehl svn mergeinfo zur Verfügung, der dabei hilfreich ist, die Zusammenführungen zwischen zwei Zweigen zu zeigen; insbesondere welche Änderungsmengen in ein Verzeichnis eingeflossen sind oder welche noch für einen Abgleich bereit stehen. Letzteres ergibt eine Art Vorschau, welche Änderungsmengen ein folgender svn merge-Befehl auf Ihren Zweig replizieren wird. Standardmäßig zeigt svn mergeinfo eine graphische Übersicht der Beziehungen zwischen zwei Zweigen an. Wenn wir auf unser früheres Beispiel zurückkommen, verwenden wir den Unterbefehl, um die Beziehung zwischen /calc/trunk und /calc/branches/my-calc-branch zu untersuchen:

$ cd my-calc-branch

$ svn mergeinfo ^/calc/trunk
    letzter gemeinsamer Vorfahr
    |         letzte vollständige Zusammenführung
    |         |        Spitze des Zweigs
    |         |        |         Projektarchiv-Pfad
    340                382
    |                  |
  -------| |------------         calc/trunk
     \          /
      \        /
       --| |------------         calc/branches/my-calc-branch
              |        |
              379      382

Das Diagramm zeigt, dass /calc/branches/my-calc-branch von /calc/trunk@340 herüber kopiert wurde und der letzte automatische Merge der Reintegrations-Merge war, den wir vom Zweig zum Stamm in r380 gemacht haben. Beachten Sie, dass das Diagramm die vier automatischen Synchronisierungs-Merges nicht zeigt, die wir in den Revisionen 352, 362, 372 und 379 gemacht haben. Gezeigt wird lediglich der letzte automatische Merge, egal in welche Richtung.[40] Diese Standard-Ausgabe ist als Übersicht über die Merges zwischen zwei Zweigen brauchbar, jedoch sollten Sie die Option --show-revs=merged verwenden, um die zusammengeführten Versionen genau zu sehen:

$ svn mergeinfo ^/calc/trunk --show-revs merged
r344
r345
r346
…
r366
r367
r368

Um auf ähnliche Art die Änderungen zu sehen, die für einen Merge vom Stamm auf den Zweig in Frage kommen, können wir die Option --show-revs=eligible verwenden:

$ svn mergeinfo ^/calc/trunk --show-revs eligible
r380
r381
r382

Der Befehl svn mergeinfo erwartet einen Quell-URL (woher die Änderungen kommen) und einen optionalen Ziel-URL (wohin die Änderungen abgeglichen werden). Falls kein Ziel-URL angegeben ist, wird das aktuelle Arbeitsverzeichnis als Ziel angenommen. Da wir im vorangegangenen Beispiel unser dem Zweig entsprechenden Arbeitsverzeichnis abfragen, geht der Befehl davon aus, dass wir daran interessiert sind, Änderungen vom angegebenen Stamm-URL für /calc/branches/my-calc-branch zu erhalten.

Seit Subversion 1.7 kann der Unterbefehl svn mergeinfo auch Teilbaum-Mergeinfo und nichterbliche Mergeinfo berücksichtigen. Mit den Option --recursive oder --depth wird Teilbaum-Mergeinfo berücksichtigt, während nicht vererbbare Mergeinfo standardmäßig beachtet wird.

Angenommen, wir haben einen Zweig sowohl mit Teilbaum- als auch nichterblichen Mergeinfo:

$ svn pg svn:mergeinfo -vR 
# Nichterbliche Mergeinfo
Eigenschaften zu ».«
  svn:mergeinfo
    /calc/trunk:354,385-388* 
# Teilbaum-Mergeinfo
Eigenschaften zu »doc/INSTALL«
  svn:mergeinfo
    /calc/trunk/Makefile:354,380

Aus den obigen Informationen geht hervor, dass r385-388 nur in die Wurzel des Zweigs eingepflegt wurde, aber in keins der Kindverzeichnisse. Ferner ist erkennbar, dass r380 nur in die Datei Makefile eingearbeitet wurde. Wenn wir svn mergeinfo mit der Option --recursive verwenden, um nachzusehen, was von /calc/trunk in diesen Zweig eingepflegt wurde, erkennen wir drei Revisionen, die mit der Markierung * versehen sind:

$ svn mergeinfo -R --show-revs=merged ^/calc/trunk .
r354
r380*
r385
r386
r387*
r388*

* markiert Revisionen, die nur teilweise in das in Frage kommende Ziel eingepflegt worden sind (die Bedeutung ist die gleiche wie bei der Überprüfung nach in Frage kommenden Revisionen). In diesem Beispiel bedeutet das, dass beim Versuch r380, r387 oder r388 von ^/trunk einzupflegen, mehr Änderungen angefallen wären. Ebenso wissen wir, da r354, r385 und r386 nicht mit * markiert ist, dass der Versuch, sie erneut einzupflegen, keine Auswirkung hätte.[41]

Eine andere Methode, eine genauere Vorschau auf einen Abgleich zu bekommen, ist die Verwendung der Option --dry-run:

$ svn merge ^/paint/trunk paint-feature-branch --dry-run 
-- Zusammenführen von r290 bis r383 in »paint-feature-branch«:
U    paint-feature-branch/src/palettes.c
U    paint-feature-branch/src/brushes.c
U    paint-feature-branch/Makefile

$ svn status 
#  es wird nichts ausgegeben, die Arbeitskopie ist unverändert

Die Option --dry-run macht tatsächlich überhaupt keine lokalen Änderungen an der Arbeitskopie. Sie zeigt nur Status-Codes, die ausgegeben würden, wenn ein echter Abgleich stattfände. Sie ist nützlich, um eine Vorschau für einen möglichen Abgleich auf hoher Ebene zu erhalten, falls svn diff zu detailliert wäre.

[Tipp] Tipp

Nach dem Merge, aber vor der Übergabe des Ergebnisses, können Sie svn diff --depth=empty /pfad/zum/abgleichs/ziel verwenden, um nur die Änderungen am unmittelbaren Ziel des Merges zu sehen. Falls das Ziel ein Verzeichnis war, werden nur Unterschiede von Eigenschaften angezeigt. Das ist eine praktische Methode, um sich die Änderungen an der Eigenschaft svn:mergeinfo anzusehen, die dort durch den Merge vermerkt wurden, und die Sie daran erinnern, was Sie eben abgeglichen haben.

Natürlich ist die beste Methode, eine Vorschau eines Merges zu erhalten, ihn zu machen! Denken Sie daran, dass der Aufruf von svn merge an sich nichts Riskantes ist (es sei denn, sie haben lokale Änderungen an Ihrer Arbeitskopie gemacht – aber wir haben bereits betont, dass Sie in eine derartige Umgebung nicht abgleichen sollten). Falls Ihnen das Ergebnis des Abgleichs nicht gefallen sollte, rufen Sie einfach svn revert . -R auf, um die Änderungen an Ihrer Arbeitskopie rückgängig zu machen, und versuchen Sie den Befehl erneut mit unterschiedlichen Optionen. Der Abgleich ist solange nicht endgültig, bis Sie mit svn commit das Ergebnis übertragen.

Änderungen rückgängig machen

Sehr häufig wird svn merge verwendet, um eine Änderung rückgängig zu machen, die bereits an das Projektarchiv übertragen worden war. Nehmen wir einmal an, Sie arbeiten fröhlich in einer Arbeitskopie von /calc/trunk und entdecken, dass die damaligen Änderungen an verschiedenen Quelltext-Dateien in Revision 392 völlig falsch waren. Sie hätten nie übertragen werden sollen. Sie können svn merge verwenden, um die Änderung in Ihrer Arbeitskopie zurückzunehmen, und dann die lokale Änderung an das Projektarchiv übertragen. Sie müssen hierfür nur eine umgekehrte Differenz angeben. (Sie machen das durch die Angabe von --revision 303:302 oder durch das äquivalente --change -303.)

$ svn merge ^/calc/trunk . -c-392 
-- Rückwärtiges Zusammenführen von r392 in ».«:
U    src/real.c
U    src/main.c
U    src/button.c
U    src/integer.c 
-- Aufzeichnung der Informationen für rückwärtiges Zusammenführen von r392 in ».«:
 U   .

$ svn st
M       src/button.c
M       src/integer.c
M       src/main.c
M       src/real.c

$ svn diff
… 
# überprüfen, ob die Änderung entfernt wurde
…

$ svn commit -m "Fehlerhafte Änderung aus r392 rückgängig machen."
Sende              src/button.c
Sende              src/integer.c
Sende              src/main.c
Sende              src/real.c
Sende              integer.c
Übertrage Daten ....
Revision 399 übertragen.

Wie wir früher bereits erwähnten, kann man eine Projektarchiv-Version als eine bestimmte Änderungsmenge betrachten. Bei Verwendung der Option -r wird svn merge aufgefordert, eine Änderungsmenge oder ein ganzes Intervall von Änderungsmengen auf Ihre Arbeitskopie anzuwenden. In unserem Fall, bei dem wir eine Änderung zurücknehmen, fordern wir svn merge auf, die Änderungsmenge r392 rückwärts auf unsere Arbeitskopie anzuwenden.

Merken Sie sich, dass ein solches Rückgängigmachen wie jeder andere svn merge-Vorgang ist, so dass Sie svn status und svn diff benutzen sollten, um sicherzustellen, dass Ihre Arbeit in dem Zustand ist, den Sie haben möchten, und verwenden Sie anschließend svn commit, um die endgültige Version in das Projektarchiv zu bringen. Nach der Übergabe wird sich diese bestimmte Änderungsmenge nicht mehr in der HEAD-Revision wiederfinden.

Nun denken Sie vielleicht: Gut, aber das hat doch nicht wirklich die Übergabe rückgängig gemacht, oder? Die Änderung besteht immer noch in Revision 392. Falls jemand eine Version des Projektes calc zwischen den Revisionen 392 und 398 auscheckt, wird doch trotzdem die fehlerhafte Änderung sichtbar, oder nicht?

Ja, das stimmt. Wenn wir davon sprechen, eine Änderung zu entfernen, sprechen wir eigentlich darüber, sie aus der HEAD-Revision zu entfernen. Die ursprüngliche Änderung besteht immer noch in der Geschichte des Projektarchivs. Für die meisten Situationen ist das ausreichend. Die meisten Leute sind sowieso nur am HEAD eines Projektes interessiert. Es gibt jedoch Spezialfälle, in denen Sie wirklich alle Beweise der Übergabe vernichten möchten. (Vielleicht hat jemand ein vertrauliches Dokument in das Projektarchiv übertragen.) Das ist leider nicht so einfach, da Subversion absichtlich so konstruiert wurde, dass es niemals Informationen verliert. Revisionen sind unveränderliche Bäume, die aufeinander aufbauen. Die Beseitigung einer Revision aus der Geschichte würde einen Dominoeffekt auslösen, Chaos in allen nachfolgenden Revisionen anrichten und möglicherweise alle Arbeitskopien ungültig machen.[42]

Zurückholen gelöschter Objekte

Das Tolle an Versions-Kontroll-Systemen ist, dass Informationen nie verlorengehen. Selbst wenn Sie eine Datei oder ein Verzeichnis löschen, ist es zwar nicht mehr in der HEAD-Revision vorhanden, jedoch noch in früheren Revisionen. Eine der häufigsten Fragen neuer Benutzer ist: Wie bekomme ich meine alte Datei oder mein altes Verzeichnis zurück?

Der erste Schritt ist es, genau zu definieren welches Objekt Sie zurückholen möchten. Hier ist eine nützliche Metapher: Sie können sich vorstellen, dass jedes Objekt im Projektarchiv in einem zweidimensionalen Koordinatensystem befindet. Die erste Koordinate ist ein bestimmter Revisionsbaum und die zweite Koordinate ist ein Pfad innerhalb dieses Baumes. So wird jede Version Ihrer Datei oder Ihres Verzeichnisses durch ein bestimmtes Koordinatenpaar definiert werden. (Erinnern Sie sich an die Syntax einer Peg-Revision – foo.c@224 – die in „Peg- und operative Revisionen“ erwähnt wurde.)

Zunächst sollten Sie svn log benutzen, um das exakte Koordinatenpaar zu ermitteln, das Sie zurückholen wollen. Eine gute Strategie ist es, svn log --verbose in einem Verzeichnis aufzurufen, in dem das gelöschte Objekt einmal enthalten war. Die Option --verbose (-v) gibt eine Liste aller geänderten Objekte in jeder Revision aus; Sie müssen nur noch die Revision finden, in der Sie die Datei oder das Verzeichnis gelöscht haben. Sie können das visuell tun oder ein Werkzeug zur Untersuchung der Protokollausgaben einsetzen (mit grep oder vielleicht durch eine inkrementelle Suche in einem Editor). Falls Sie wissen, dass das fragliche Objekt kürzlich gelöscht wurde, können Sie auch die Option --limit verwenden, um die Protokollausgabe für eine manuelle Untersuchung knapp genug zu halten.

$ cd calc/trunk

$ svn log -v --limit 3
------------------------------------------------------------------------ 
r401 | sally | 2013-02-18 23:15:44 -0500 (Di, 19. Feb 2013) | 1 Zeile
Geänderte Pfade:
   M /calc/trunk/src/main.c

Nachtrag zu r400: Tippfehler im Hifetext.
------------------------------------------------------------------------
r400 | bill | 2013-02-19 20:55:08 -0500 (Di, 19. Feb 2013) | 4 Zeilen
Geänderte Pfade:
   M /calc/trunk/src/main.c
   D /calc/trunk/src/real.c


* calc/trunk/src/main.c: Hilfetext aktualisiert.

* calc/trunk/src/real.c: Datei entfernt, keine der hier implementierten
  APIs werden noch irgendwo verwendet.
------------------------------------------------------------------------
r399 | sally | 2013-02-19 20:05:14 -0500 (Di, 19. Feb 2013) | 1 Zeile
Geänderte Pfade:
   M /calc/trunk/src/button.c
   M /calc/trunk/src/integer.c
   M /calc/trunk/src/main.c
   M /calc/trunk/src/real.c

Fehlerhafte Änderung aus r392 rückgängig gemeacht.
------------------------------------------------------------------------

In diesem Beispiel nehmen wir an, dass Sie nach der gelöschten Datei real.c suchen. Beim Durchsehen der Protokolle des Elternverzeichnisses haben Sie entdeckt, dass diese Datei in Revision 400 gelöscht wurde. Daher war die letzte Revision in der die Datei noch vorhanden war die unmittelbare Vorgänger-Revision. Die Schlussfolgerung: Sie möchten den Pfad /calc/trunk/real.c aus Revision 399 zurückholen.

Das war der schwierige Teil – die Nachforschung. Nun, da Sie wissen, was Sie wiederherstellen wollen, haben Sie die Wahl zwischen zwei verschiedenen Methoden.

Die eine Option ist, svn merge zu verwenden, um Revision 400 rückwärts anzuwenden. (Wir haben bereits in „Änderungen rückgängig machen“ besprochen, wie Änderungen rückgängig gemacht werden.) Das hätte den Effekt, real.c als lokale Änderung erneut hinzuzufügen. Die Datei würde zum Hinzufügen ins Projektarchiv markiert, und nach der Übergabe wäre die Datei wieder in HEAD vorhanden.

In diesem besonderen Beispiel ist das aber wahrscheinlich nicht die beste Strategie. Die Rückwärts-Anwendung von Revision 400 würde nicht nur real.c zum Hinzufügen markieren, sondern, wie aus den Protokollmeldungen hervorgeht, dass ebenso bestimmte Änderungen an main.c zurücknehmen, was Sie aber nicht wollen. Sie können sicherlich Revision 400 rückwärts anwenden und dann mit svn revert die lokalen Änderungen an main.c zurücknehmen; allerdings ist diese Technik nicht sehr effektiv. Was wäre, wenn 90 Dateien in Revision 400 geändert worden wären?

Eine zweite, zielorientiertere, Strategie ist es, den Befehl svn merge überhaupt nicht zu verwenden, sondern stattdessen svn copy. Kopieren Sie einfach das exakte Koordinatenpaar aus Revision und Pfad vom Projektarchiv in Ihre Arbeitskopie:

$ svn copy ^/calc/trunk/src/real.c@399 ./real.c
A         real.c

$ svn st
A  +    real.c

# Übertragung der Wiederbelebung
…

Das Plus-Zeichen in der Statusausgabe zeigt an, dass das Objekt nicht bloß zu Hinzufügen vorgemerkt ist, sondern zum Hinzufügen mit Geschichte. Subversion merkt sich, woher es kopiert wurde. Künftig wird beim Anwenden von svn log auf diese Datei die gesamte Geschichte, über das Zurückholen hinweg, inklusive der Geschichte vor Revision 399 durchlaufen. In anderen Worten, dieses neue real.c ist nicht wirklich neu; es ist ein direkter Nachfahre der ursprünglichen, gelöschten Datei. Dies ist normalerweise eine gute und nützliche Sache. Falls Sie jedoch die Datei ohne geschichtliche Verbindung zur alten Datei zurückholen wollen, funktioniert diese Technik ebenso gut:

$ svn cat ^/calc/trunk/src/real.c@399 > ./real.c

$ svn add real.c
A         real.c

# Übertragung der Wiederbelebung
…

Obwohl unser Beispiel zeigt, wie eine Datei zurückgeholt wird, sollten sie beachten, dass dieselben Techniken auch beim Wiederherstellen von gelöschten Verzeichnissen funktionieren. Beachten Sie auch, dass die Wiederherstellung nicht unbedingt in Ihrer Arbeitskopie passieren muss – sie kann auch vollständig im Projektarchiv ausgeführt werden:

$ svn copy ^/calc/trunk/src/real.c@399 ^/calc/trunk/src/real.c \ 
           -m "real.c aus Revision 399 wiederhergestellt."
Revision 402 übertragen.

$ svn up
Updating '.':
A    real.c 
Aktualisiert zu Revision 402.


[35] Diese wurde in svn 1.6 eingeführt,

[36] Die Option --allow-mixed-revisions des Unterbefehls svn merge erlaubt Ihnen, diese Einschränkung zu umgehen, jedoch sollten Sie das nur dann machen, wenn Sie die Auswirkungen verstehen und einen guten Grund dafür haben.

[37] Seit Subversion 1.7 brauchen Sie nicht unbedingt alle Ihre Synchronisierungs-Merges in die Wurzel Ihres Zweigs zu bringen wie in diesem Beispiel. Falls Ihr Zweig effektiv durch eine Reihe von Teilbaum-Merges synchronisiert ist, wird die Reintegration funktionieren, doch fragen Sie sich einmal, warum Sie Teilbäume zusammenführen, wenn doch der Zweig effektiv synchronisiert wird. Diese Vorgehensweise ist oft unnötig kompliziert.

[38] Automatische Reintegrations-Merges sind erlaubt, falls das Ziel ein flacher Checkout ist (siehe „Verzeichnis-Teilbäume“), doch sämtliche vom Diff betroffene, durch die Beschränkung der Arbeitskopie auf einen Teilbaum fehlende Pfade werden übersprungen, was wahrscheinlich nicht von Ihnen beabsichtigt ist!

[39] Diese Wiederverwendung eines Arbeitszweigs wird nur von Subversion 1.8 unterstützt. Frühere Versionen erfordern einige besondere Schritte, bevor ein Arbeitszweig mehr als einmal reintegriert werden kann. Siehe eine frühere Version dieses Kapitels für weitere Informationen: http://svnbook.red-bean.com/de/1.7/svn.branchmerge.basicmerging.html#svn.branchemerge.basicmerging.reintegrate

[40] Unter Richtung verstehen wir entweder Stamm-Zweig- (automatische Synchronisierung) oder Zweig-Stamm-Merges (automatische Reintegration).

[41] Das ist ein gutes Beispiel für inoperative Merge-Revisionen.

[42] Allerdings gibt es im Subversion-Projekt Pläne, eines Tages einen Befehl zu implementieren, der die Aufgabe erledigen würde, Informationen dauerhaft zu löschen. Bis dahin, siehe „svndumpfilter“ für einen möglichen Notbehelf.