Nun arbeiten Sie und Sally auf parallelen Zweigen des Projektes: Sie arbeiten auf einem privaten Zweig, und Sally arbeitet auf dem Stamm oder dem Hauptzweig der Entwicklung.
Bei Projekten mit einer großen Zahl von Mitarbeitern haben die meisten gewöhnlich Arbeitskopien vom Stamm. Sobald jemand eine langwierige Änderung machen muss, die wahrscheinlich den Stamm stören würde, ist die Standardvorgehensweise, 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 zu verkriechen“ war, 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ühren (Merging) und wird durch verschiedene Aufrufe des Befehls 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.5 (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 gemacht haben. Es
ist in Ihrem Interesse, diese Änderungen in Ihren Zweig zu
übernehmen, um sicherzustellen, dass sie sich gut mit Ihren
Änderungen vertragen. Dies ist tatsächlich eine der besten
Vorgehensweisen: 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 die letzten, aktuellsten Änderungen vom Stamm auf Ihren Zweig zu bringen, 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 http://svn.example.com/repos/calc/trunk --- Zusammenführen von r345 bis r356 in ».«: U button.c U integer.c
Diese einfache Syntax – svn merge
– fordert
Subversion auf, alle neuen Änderungen von dem URL mit dem
aktuellen Arbeitsverzeichnis (welches typischerweise das
Wurzelverzeichnis Ihrer Arbeitskopie ist)
zusammenzuführen. 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:URL
$ 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 http://svn.example.com/repos/calc/trunk --- Zusammenführen von r357 bis r380 in ».«: U integer.c U Makefile A README
Subversion weiß, welche Änderungen Sie bereits 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.
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:
$ svn merge http://svn.example.com/repos/calc/trunk --- Zusammenführen von r381 bis r385 in ».«: U button.c U README $ # bauen, testen, ... $ svn commit -m "Letzte Zusammenführung der Änderungen von trunk changes in my-calc-branch." Sende . Sende button.c Sende README Übertrage Daten .. Revision 390 übertragen.
Nun verwenden Sie svn merge, um Ihre
Änderungen vom Zweig zurück auf den Stamm zu überführen. Sie
benötigen eine aktuelle 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. Wie auch
immer Sie Ihre Arbeitskopie bereitstellen, denken Sie daran,
dass Sie die Überführung in einer Arbeitskopie durchführen,
die keine lokalen Änderungen beinhaltet
und jüngst aktualisiert wurde (d.h., keine Mischung aus
lokalen Revisionen ist). Falls Ihre Arbeitskopie nicht
„sauber“ in diesem Sinn ist, könnte es Ihnen
einige unnötige konfliktbezogene Kopfschmerzen bereiten, und
svn merge wird wahrscheinlich einen Fehler
ausgeben.
Sobald Sie eine saubere Arbeitskopie des Stamms haben, sind Sie bereit, Ihren Zweig damit zusammenzuführen:
$ pwd /home/user/calc-trunk $ svn update # (stellen Sie sicher, dass die Arbeitskopie aktuell ist) Revision 390. $ svn merge --reintegrate http://svn.example.com/repos/calc/branches/my-calc-branch -- Zusammenführen der Unterschiede zwischen Projektarchiv-URLs in ».«: U button.c U integer.c U Makefile U . $ # bauen, testen, überprüfen, ... $ svn commit -m "Merge my-calc-branch back into trunk!" Sende . Sende button.c Sende integer.c Sende Makefile Übertrage Daten .. Revision 391 übertragen.
Gratulation! Ihr Zweig ist 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ß
jedesmal, 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!)
Nachdem nun Ihr privater Zweig mit dem Stamm zusammengeführt wurde, können Sie ihn aus dem Projektarchiv löschen:
$ svn delete http://svn.example.com/repos/calc/branches/my-calc-branch \ -m "Remove my-calc-branch." 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 in Subversion 1.5 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." Revision 392 übertragen. $ svn copy http://svn.example.com/repos/calc/trunk \ http://svn.example.com/repos/calc/branches/new-branch -m "Einen neuen Zweig von trunk anlegen." Revision 393 übertragen. $ cd my-calc-branch $ svn switch http://svn.example.com/repos/calc/branches/new-branch Aktualisiert zu Revision 393.
Der letzte Befehl des vorangegangenen Beispiels – svn switch – ist eine Art, ein bestehendes Arbeitsverzeichnis auf ein unterschiedliches Projektarchiv-Verzeichnis zu aktualisieren. Wir werden das genauer in „Zweige durchlaufen“ besprechen.
Der grundsätzliche Mechanismus, den Subversion verwendet,
um Änderungsmengen zu verfolgen – d.h. welche Änderungen
auf welchen Zweig übertragen worden sind – besteht aus
dem Festhalten von Daten in Eigenschaften. Daten über das
Zusammenführen werden speziell in der Eigenschaft
svn:mergeinfo
vermerkt, die an Dateien und
Verzeichnissen hängt. (Falls Sie mit Subversion-Eigenschaften
nicht vertraut sind, ist es nun an der Zeit, „Eigenschaften“ zu überfliegen.)
Sie können sich die Eigenschaft ansehen, wie jede andere auch:
$ cd my-calc-branch $ svn propget svn:mergeinfo . /trunk:341-390
Es wird nicht empfohlen, dass Sie
selbst den Wert dieser Eigenschaft ändern, es sei denn, Sie
wissen wirklich, was Sie tun. Diese Eigenschaft wird
automatisch von Subversion gepflegt, sobald Sie den Befehl
svn merge ausführen. Ihr Wert gibt
Aufschluss darüber, welche Änderungen (von einem gegebenen
Pfad) mit dem in Frage kommenden Verzeichnis abgeglichen
wurden. In diesem Fall ist der Pfad
/trunk
, und das Verzeichnis, das die
bestimmten Änderungen erhalten hat, ist
/branches/my-calc-branch
.
Es gibt auch einen Unterbefehl, svn mergeinfo, der hilfreich dabei ist, nicht nur die Änderungsmengen anzuzeigen, die ein Verzeichnis absorbiert hat, sondern auch, welche Änderungsmengen für einen Abgleich noch in Frage kommen. 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 http://svn.example.com/repos/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 http://svn.example.com/repos/calc/trunk --show-revs eligible r391 r392 r393 r394 r395
Der Befehl svn mergeinfo erwartet einen
„Quell“-URL (woher die Änderungen kommen würden)
und einen optionalen „Ziel“-URL (wohin die
Änderungen abgeglichen würden). Falls kein Ziel-URL angegeben
ist, wird angenommen, dass das aktuelle Arbeitsverzeichnis das
Ziel ist. Weil wir im vorangegangenen Beispiel unser
Arbeitsverzeichnis vom Zweig abfragen, geht der Befehl davon
aus, dass wir daran interessiert sind, Änderungen für
/branches/mybranch
vom angegebenen
Stamm-URL zu erhalten.
Eine andere Methode, eine genauere Vorschau auf einen
Abgleich zu bekommen, ist die Verwendung der Option
--dry-run
:
$ svn merge http://svn.example.com/repos/calc/trunk --dry-run 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 Durchführen eines Abgleichs, aber vor der
Übergabe des Ergebnisses, können Sie |
Natürlich ist die beste Methode, eine Vorschau eines
Abgleichs 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.
Tipp | |
---|---|
Während es vollkommen in Ordnung ist, durch wiederholte Aufrufe von svn merge und svn revert mit Abgleichen zu experimentieren, könnte es allerdings sein, dass Sie über einige lästige (aber leicht zu umgehende) Fallstricke stolpern. Wenn zum Beispiel durch den Abgleich eine neue Datei hinzugefügt wird (d.h., sie wird zum Hinzufügen markiert), so wird svn revert sie nicht wirklich entfernen; es entfernt lediglich die Markierung zum Hinzufügen. Was übrig bleibt, ist eine unversionierte Datei. Wenn Sie dann den Abgleich erneut versuchen, könnten Sie einen Konflikt bekommen, weil die unversionierte Datei „im Weg steht“. Die Lösung? Nach dem Rückgängigmachen sollten Sie die Arbeitskopie aufräumen und unversionierte Dateien und Verzeichnisse entfernen. Die Ausgabe von svn status sollte so sauber wie möglich sein und idealerweise gar nichts anzeigen. |
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 http://svn.example.com/repos/calc/trunk --- Reverse-merging r303 into 'integer.c': -- Rückwärtiges Zusammenführen von r303 in »integer.c«: U integer.c $ 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 #303
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.
[21]
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 kann 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 (Fri, 26 Dec 2003) | 3 lines 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 http://svn.example.com/repos/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
ebensogut:
$ svn cat http://svn.example.com/repos/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 http://svn.example.com/repos/calc/trunk/real.c@807 \ http://svn.example.com/repos/calc/trunk/ \ -m "real.c aus Revision 807 wiederhergestellt." Revision 1390 übertragen. $ svn update A real.c Aktualisiert zu Revision 1390.
[21] 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.