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.
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.
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.
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 | |
---|---|
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
– 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
(URL
^
) 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“.
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.
In den meisten Beispielen dieses Kapitels ist das Ziel einer Zusammenführung das 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 untersuchen. 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, oder „ausgelassen“ wurde. Das nennt
sich Auslassung von Mergeinfo und
passiert immer dann, wenn Subversion redundante
Teilbaum-Mergeinfo entdeckt.
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. |
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“.
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 | |
---|---|
Obwohl es möglich ist, |
Tipp | |
---|---|
Der Umfang von |
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 | |
---|---|
Nach dem Merge, aber vor der Übergabe des Ergebnisses,
können Sie |
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.
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]
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.