Diese Dokumentation wurde zur Beschreibung der Serie 1.7.x von Apache™ Subversion® erstellt. Falls Sie eine unterschiedliche Version von Subversion einsetzen, sei Ihnen dringend angeraten, bei http://www.svnbook.com/ vorbeizuschauen und stattdessen die zu Ihrer Version von Subversion passende Version dieser Dokumentation heranzzuiehen.

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 es Standard, einen Zweig zu erzeugen und die Änderungen bis zum Abschluss der Arbeiten nach dorthin zu übergeben.

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.7 (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 Enthüllung“ 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 Versionskontrollsystemen 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 Versionskontrollsystem 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 Übergabe. 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 übergeben 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 auch (wie Sie bald sehen werden) 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 begonnen haben, auf Ihrem privaten Zweig zu arbeiten. Ihre Arbeit ist noch nicht beendet, jedoch wissen Sie, dass gleichzeitig andere Leute in Ihrem Team weiterhin wichtige Änderungen im /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 eine 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.

[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 r345 bis r356 in ».«:
U    button.c
U    integer.c 
-- Aufzeichnung der Informationen für Zusammenführung von r345 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[28], 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       button.c
M       integer.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 entweder angelegt oder modifiziert wurde. Das 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 an das Projektarchiv übergeben:


$ svn commit -m "Die letzten Änderungen von trunk mit my-calc-branch zusammengeführt."
Sende          .
Sende          button.c
Sende          integer.c
Übertrage Daten ..
Revision 357 ü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 übergeben, 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 [\
357:378], 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 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 (siehe „Mergeinfo und Vorschauen“ zu Details). Diese Einschränkungen bedeuten, dass Merges in Arbeitskopien aus gemischten Revisionen unerwartete Text- und Baumkonflikte hervorrufen können.[29] Wir möchten keine unnötigen Konflikte, also aktualisieren wir die Arbeitskopie und versuchen erneut die Zusammenführung.

$ svn up
Updating '.':
At revision 380.

$ svn merge ^/calc/trunk 
--- Zusammenführen von r357 bis r380 in ».«:
U    integer.c
U    Makefile
A    README 
-- Aufzeichnung der Informationen für Zusammenführung von r357 bis r380 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 übergeben.

[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 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 Enthüllung“). 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:[30]

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

$ # build, test, ...

$ svn commit -m "Final merge of trunk changes to my-calc-branch."
Sending        .
Sending        button.c
Sending        README
Transmitting file data ..
Committed revision 390.

Verwenden Sie jetzt svn merge mit der Option --reintegrate , um Ihre Änderungen vom Zweig zurück auf den Stamm zu überführen. Sie benötigen eine Arbeitskopie von /trunk. Sie bekommen sie entweder durch svn checkout, indem Sie von irgendwo auf Ihrer Platte eine alte Arbeitskopie vom Stamm hervorholen, oder den Befehl svn switch (siehe „Zweige durchlaufen“) verwenden. 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 der Verwendung der Option --reintegrate 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  # (make sure the working copy is up to date)
Updating '.': 
Revision 390.

$ svn merge --reintegrate ^/calc/branches/my-calc-branch 
-- Zusammenführen der Unterschiede zwischen Projektarchiv-URLs in ».«:
U    button.c
U    integer.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          button.c
Sende          integer.c
Sende          Makefile
Übertrage Daten ..
Revision 391 übertragen.

Gratulation! Ihre Änderungen vom Zweig sind nun zurück in die Hauptentwicklungslinie überführt worden. Beachten Sie, dass dieses Mal die Option --reintegrate verwendet wurde. Diese Option ist kritisch, wenn Änderungen von einem Zweig in die ursprüngliche Entwicklungslinie reintegriert werden – vergessen Sie sie nicht! Sie wird benötigt, da diese Art der Rücküberführung 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 345:356 vom Stamm auf den Zweig überführte; später fuhr es mit dem nächsten verfügbaren aufeinanderfolgenden Intervall 356:380 fort. Wenn Sie die letzte Synchronisierung machen, wird es das Intervall 380:385 zusammenführen.

Wenn Sie jedoch den Zweig auf den Stamm 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 die Option --reintegrate angeben, 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 die Option --reintegrate im Gegensatz zur allgemeineren Natur der meisten Optionen von Subversion-Unterbefehlen doch recht spezialisiert ist. Sie unterstützt den oben beschriebenen Anwendungsfall, hat daneben jedoch wenig Verwendungsmöglichkeiten. Wegen dieses engen Einsatzgebietes sowie der Tatsache, das eine aktualisierte Arbeitskopie[31] ohne gemischte Revisionen erforderlich ist, 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, als die 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."
Revision 392 übertragen.

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 /branches sichtbar ist, seine Existenz gleichwohl ein unveränderbarer Teil der Geschichte des Projektarchivs ist. Ein einfacher Befehl svn log auf dem /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“).

Sobald eine Zusammenführung mit --reintegrate vom Zweig auf den Stamm durchgeführt wurde, kann der Zweig nicht mehr für weitere Arbeiten verwendet werden. Er kann weder Änderungen vom Stamm korrekt absorbieren, noch kann er ordentlich auf den Stamm zurückintegriert werden. Aus diesem Grund sollten Sie ihn zerstören und erneut aus dem Stamm erzeugen, wenn Sie weiter auf dem Zweig arbeiten wollen:

$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \ 
      -m "my-calc-branch löschen, zurückgeführt auf Stamm."
Revision 392 übertragen.

$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch 
      -m "my-calc-branch erneut von trunk@HEAD abgezweigt."
Revision 393 übertragen.

Es gibt eine weitere Möglichkeit, wie der Zweig nach der Zurückführung wiederverwendbar gemacht werden kann, ohne ihn zu löschen. Siehe „Einen reintegrierten Zweig am Leben erhalten“.

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 Eigenschaft ansehen, wie jede andere auch:

$ cd my-calc-branch
$ svn propget svn:mergeinfo .
/trunk:341-390
$
[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 /trunk der Ursprungspfad der zusammengeführten Änderungen und /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 stimmt das nicht mehr, da es verschiedene neue Mitteilungen vorhält, 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 Enthüllung“), sondern Änderungen zur Buchhaltung, um festzuhalten, was zusammengeführt wurde.

Subversion stellt auch den Unterbefehl, svn mergeinfo zur Verfügung, der dabei hilfreich ist, nicht nur die in ein Verzeichnis eingeflossenen Änderungsmengen anzuzeigen, sondern auch, welche Änderungsmengen noch für einen Abgleich bereit stehen. Das ergibt eine Art Vorschau der nächsten Änderungsmengen, die svn merge auf Ihren Zweig abgleichen wird.

$ cd my-calc-branch

# Welche Änderungen wurden bereits vom Stamm auf den Zweig abgeglichen?
$ svn mergeinfo ^/calc/trunk
r341
r342
r343
…
r388
r389
r390

# Welche Änderungen kommen für einen Abgleich vom Stamm auf den Zweig noch in Frage?
$ svn mergeinfo ^/calc/trunk --show-revs eligible
r391
r392
r393
r394
r395
$

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 /branches/mybranch zu erhalten.

Mit dem Erscheinen von Subversion 1.7 kann der Unterbefehl svn mergeinfo auch Teilbaum-Mergeinfo und nichterbliche Mergeinfo berücksichtigen. Teilbaum-Mergeinfo werden durch die Optionen --recursive oder --depth berücksichtigt, während nichterbliche standardmäßig beachtet wird.

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

$ svn propget svn:mergeinfo --recursive -v 
# Nichterbliche Mergeinfo
Eigenschaften zu ».«
  svn:mergeinfo
    /trunk:651-652,758* 
# Teilbaum-Mergeinfo
Eigenschaften zu »doc/INSTALL«
  svn:mergeinfo
    /trunk/doc/INSTALL:651-652,958,1060

Aus den obigen Informationen geht hervor, dass r758 nur in die Wurzel des Zweigs eingepflegt wurde, aber in keins der Kindverzeichnisse. Ferner ist erkennbar, dass sowohl r958 als auch r1060 nur in die Datei doc/INSTALL eingearbeitet wurde. Wenn wir svn mergeinfo mit der Option --recursive verwenden, um nachzusehen, was von ^/trunk in diesen Zweig eingepflegt wurde, erkennen wir zwei Revisionen, die mit der Markierung * versehen sind:

$ svn mergeinfo --show-revs=merged ^/trunk . --recursive
651
652
758*
958*
1060

* 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 r758 oder r958 von ^/trunk einzupflegen, mehr Änderungen angefallen wären. Ebenso wissen wir, da r1060 nicht mit * markiert ist, dass nur doc/INSTALL betroffen ist, und der Versuch, sie einzupflegen, keine Auswirkung hätte.[32]

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

$ svn merge ^/calc/trunk --dry-run 
-- Zusammenführen von r391 bis r395ld in »branch«:
U    integer.c

$ 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 übergeben.

Ä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 übergeben worden war. Nehmen wir einmal an, Sie arbeiten fröhlich in einer Arbeitskopie von /calc/trunk und entdecken, dass die damalige Änderung an integer.c in Revision 303 völlig falsch war. Sie hätte nie übergeben werden sollen. Sie können svn merge verwenden, um die Änderung in Ihrer Arbeitskopie zurückzunehmen, und dann die lokale Änderung an das Projektarchiv übergeben. Alles, was Sie hierfür tun müssen, ist, eine umgekehrte Differenz anzugeben. (Sie machen das durch die Angabe von --revision 303:302 oder durch das äquivalente --change -303.)

$ svn merge -c -303 ^/calc/trunk 
-- Rückwärtiges Zusammenführen von r303 in »integer.c«:
U    integer.c 
-- Aufzeichnung der Informationen für rückwärtiges Zusammenführen von r303 in »integer.c«:
 U   A-branch

$ svn status
 M      .
M       integer.c

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

$ svn commit -m "Änderung aus in r303 rückgängig machen."
Sende          integer.c
Übertrage Daten .
Revision 350 ü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 r303 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 303. Falls jemand eine Version des Projektes calc zwischen den Revisionen 303 und 349 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 übergeben.) 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.[33]

Zurückholen gelöschter Objekte

Das Tolle an Versionskontrollsystemen 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).

$ cd parent-dir
$ svn log -v
…
------------------------------------------------------------------------ 
r808 | joe | 2003-12-26 14:29:40 -0600 (Fr, 26. Dez 2003) | 3 Zeilen
Geänderte Pfade:
   D /calc/trunk/real.c
   M /calc/trunk/integer.c

Schnelle Funktionen zur Fourier-Transformation zu integer.c hinzugefügt.
real.c gelöscht, da Code jetzt in double.c.
…

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 808 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 807 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 808 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 808 würde nicht nur real.c zum Hinzufügen markieren, sondern, wie aus den Protokollmeldungen hervorgeht, dass ebenso bestimmte Änderungen an integer.c zurücknehmen, was Sie aber nicht wollen. Sie können sicherlich Revision 808 rückwärts anwenden und dann mit svn revert die lokalen Änderungen an integer.c zurücknehmen; allerdings ist diese Technik nicht sehr effektiv. Was wäre, wenn 90 Dateien in Revision 808 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/real.c@807 ./real.c

$ svn status
A  +    real.c

$ svn commit -m "real.c aus Revision 807 wiederhergestellt, /calc/trunk/real.c."
Hinzufügen     real.c
Übertrage Daten .
Revision 1390 übertragen.

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 807 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/real.c@807 > ./real.c

$ svn add real.c
A         real.c

$ svn commit -m "real.c aus Revision 807 wiederhergestellt."
Hinzufügen     real.c
Übertrage Daten .
Revision 1390 übertragen.

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/real.c@807 ^/calc/trunk/ \ 
      -m "real.c aus Revision 807 wiederhergestellt."
Revision 1390 übertragen.

$ svn update
Updating '.':
A    real.c 
Aktualisiert zu Revision 1390.


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

[29] 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.

[30] Mit 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.

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

[32] Das wird häufig unwirksamer Merge genannt. Obwohl in diesem Beispiel das Zusammenführen von r1060 etwas bewirkte: es würde die Mergeinfo an der Wurzel des Zweigs aktualisieren, jedoch hinsichtlich der Anwendung eines Diffs unwirksam sein.

[33] 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.