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

Umgang mit Strukturkonflikten

Bis hierhin haben wir nur über Konflikte auf der Ebene von Dateiinhalten gesprochen. Wenn Sie und Ihre Mitarbeiter überlappende Änderungen innerhalb derselben Datei vornehmen, zwingt Sie Subversion dazu, diese Änderungen zusammenzuführen, bevor Sie sie übertragen können.[9]

Was passiert aber, wenn Ihre Mitarbeiter eine Datei verschieben oder löschen, an der Sie noch arbeiten? Vielleicht gab es ein Verständnisproblem oder die eine Person glaubt, die Datei soll gelöscht werden, während die andere Person noch Änderungen an der Datei übertragen will. Vielleicht haben Ihre Mitarbeiter ja auch etwas Refactoring betrieben und dabei Dateien umbenannt und Verzeichnisse verschoben. Falls Sie noch an diesen Dateien gearbeitet haben, müssten diese Änderungen auf die Dateien am der neuen Stelle angewendet werden. Derartige Konflikte äußern sich auf der Ebene der Verzeichnisstruktur statt des Dateiinhaltes und sind bekannt als Baumkonflikte.

Wie bei textuellen Konflikten verhindern Baumkonflikte eine Übergabe aus dem Konfliktzustand und geben dem Anwender die Gelegenheit, den Zustand der Arbeitskopie auf potenzielle Probleme, die aus dem Baumkonflikt entstehen könnten, zu überprüfen und vor der Übergabe aufzulösen.

Ein Beispiel für einen Baumkonflikt

Gegeben sei folgendes Softwareprojekt, an dem Sie gerade arbeiten:

$ svn list -Rv svn://svn.example.com/trunk/
     13 harry                 Sep 06 10:34 ./
     13 harry              27 Sep 06 10:34 COPYING
     13 harry              41 Sep 06 10:32 Makefile
     13 harry              53 Sep 06 10:34 README
     13 harry                 Sep 06 10:32 code/
     13 harry              54 Sep 06 10:32 code/bar.c
     13 harry             130 Sep 06 10:32 code/foo.c
$

Später, in Revision 14, benennt Ihr Kollege Harry die Datei bar.c in baz.c um. Sie merken das noch nicht. Wie sich herausstellt, sind Sie in Ihrer Arbeitskopie damit beschäftigt, eine Menge unterschiedlicher Änderungen vorzunehmen, von denen einige auch bar.c berühren:

$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c	(revision 13)
+++ code/foo.c	(working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 13)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$

Sie bemerken erst, dass jemand anderes bar.c geändert hat, als Ihr eigener Übertragungsversuch scheitert:

$ svn commit -m "Small fixes" 
Sende          code/bar.c
Übertrage Daten .
svn: E155011: Übertragen schlug fehl (Details folgen):
svn: E155011: Datei »/home/svn/project/code/bar.c« ist veraltet
svn: E160013: Datei nicht gefunden: Transaktion »14-e«, Pfad »/code/bar.c«
$

An dieser Stelle müssen Sie svn update aufrufen. Außer Ihre Arbeitskopie zu aktualisieren, so dass Sie Harrys Änderungen sehen können, markiert es auch einen Baumkonflikt, so dass Sie die Gelegenheit bekommen, die Situation abzuschätzen und entsprechend aufzulösen.

$ svn update
Updating '.':
   C code/bar.c
A    code/baz.c
U    Makefile 
Aktualisiert zu Revision 5.
Konfliktübersicht:
  Baumkonflikte: 1

In seiner Ausgabe zeigt svn update Baumkonflikte mit einem großen C in der vierten Spalte an. svn status enthüllt weitere Details zum Konflikt:

$ svn status
M       code/foo.c
A  +  C code/bar.c
      >   local edit, incoming delete upon update 
Konfliktübersicht:
  Baumkonflikte: 1
$

Beachten Sie, wie bar.c automatisch in Ihrer Arbeitskopie zum erneuten Hinzufügen vorgemerkt wird, was die Sache vereinfacht, sollten sie sich entscheiden, die Datei zu behalten.

Da eine Verschiebung in Subversion als eine Kopie mit anschließender Löschung implementiert ist, und diese beiden Operationen sich bei einer Aktualisierung sich nicht einfach in Beziehung setzen lassen, kann Sie Subversion lediglich über eine hereinkommende Löschung einer lokal modifizierten Datei warnen. Diese Löschung kann Teil einer Verschiebung sein oder eine tatsächliche Löschung. Es ist wichtig, herauszufinden, welche semantische Änderung genau am Projektarchiv vorgenommen wurde – Sie sollten wissen, wie sich Ihre Änderungen in das Gesamtbild des Projektes einpassen lassen. Lesen Sie also die Protokokllnachrichten, halten Sie mit Ihren Kollegen Rücksprache, begutachten Sie die zeilenbasierten Änderungen – machen Sie, was auch immer zu tun ist – um Ihre nächsten Schritte zu bestimmen.

In diesem Fall verrät Ihnen die Protokollnachricht der Übertragung von Harry alles, was Sie wissen müssen.

$ svn log -r14 ^/trunk
------------------------------------------------------------------------ 
r14 | harry | 2011-09-06 10:38:17 -0400 (Di, 06. Sep 2011) | 1 Zeile
Geänderte Pfade:
   M /Makefile
   D /code/bar.c
   A /code/baz.c (from /code/bar.c:13)

Rename bar.c to baz.c, and adjust Makefile accordingly.
------------------------------------------------------------------------
$

svn info zeigt die URLs der am Konflikt beteiligten Objekte. Der linke URL zeigt die Quelle der lokalen Seite des Konfliktes, während die rechte URL die Quelle der hereinkommenden Seite des Konfliktes anzeigt. Diese URLs weisen darauf hin, wo Sie mit der Suche nach der mit Ihrer lokalen Änderung in Konflikt stehenden Änderung in der Vorgeschichte des Projektarchivs beginnen sollten.

$ svn info code/bar.c 
Pfad: code/bar.c
Name: bar.c
URL: http://svn.example.com/svn/repo/trunk/code/bar.c
… 
Baumkonflikt: lokal editiert, eingehend gelöscht bei Aktualisierung
  Quelle  links: (Datei) ^/trunk/code/bar.c@4
  Quelle  rechts: (nichts) ^/trunk/code/bar.c@5

$

bar.c heißt nun Opfer eines Baumkonfliktes. Sie kann nicht übertragen werden, bevor der Konflikt aufgelöst wird:

$ svn commit -m "Small fixes" 
svn: E155015: Übertragen schlug fehl (Details folgen):
svn: E155015: Übertragung abgebrochen: »/home/svn/project/code/bar.c« bleibt
im Konflikt
$

Um diesen Konflikt aufzulösen, müssen Sie entweder mit Harrys Vorgehen einverstanden sein oder nicht.

Falls Sie mit dem Vorgehen einverstanden sind, ist Ihr bar.c überflüssig. Sie können es löschen und den Baumkonflikt als aufgelöst markieren. Aber halt: Sie haben Änderungen an der Datei vorgenommen! Bevor Sie bar.c löschen, müssen Sie entscheiden, ob die von Ihnen gemachten Änderungen an einer anderen Stelle vorgenommen werden müssen, zum Beispiel an der neuen Datei baz.c, in der sich nun der Code aus bar.c befindet. Wir nehmen einmal an, dass Ihre Änderungen tatsächlich müssen der Verschiebung folgen müssen. Subversion ist nicht schlau genug, um diese Arbeit für Sie zu übernehmen[10], so dass Sie Ihre Änderungen manuell übertragen müssen.

In unserem Beispiel könnten Sie einfach Ihre Änderungen an bar.c noch einmal vornehmen – es war schließlich nur eine Änderung in einer Zeile. Das ist allerdings nicht immer der Fall, weshalb wir einen skalierbareren Ansatz zeigen. Zunächst erzeugen wir mit svn diff eine Patch-Datei. Dann bearbeiten wir die Kopfzeilen dieser Patch-Datei, so dass sie auf den neuen Namen der umbenannten Datei zeigen. Schließlich wenden wir den modifizierten Patch erneut auf unsere Arbeitskopie an.

$ svn diff code/bar.c > PATCHFILE
$ cat PATCHFILE
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 14)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 } 
$ ### PATCHFILE bearbeiten, so dass es sich auf code/baz.c statt auf code/bar.c bezieht
$ cat PATCHFILE
Index: code/baz.c
===================================================================
--- code/baz.c	(revision 14)
+++ code/baz.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$ svn patch PATCHFILE
U         code/baz.c
$

Da nun die ursprünglich an to bar.c vorgenommenen Änderungen erfolgreich in baz.c reproduziert wurden, können Sie bar.c löschen und den Konflikt auflösen, indem Sie die Auflösungslogik anweisen, den aktuellen Inhalt der Arbeitskopie als das gewünschte Ergebnis zu akzeptieren.

$ svn delete --force code/bar.c
D         code/bar.c
$ svn resolve --accept=working code/bar.c 
Konflikt von »code/bar.c« aufgelöst
$ svn status
M       code/foo.c
M       code/baz.c
$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c  (revision 14)
+++ code/foo.c  (working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/baz.c
===================================================================
--- code/baz.c  (revision 14)
+++ code/baz.c  (working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
$

Falls Sie mit dem Vorgehen nicht einverstanden sind, können Sie stattdessen baz.c löschen, nachdem Sie sichergestellt haben, das alle nach der Umbenennung vorgenommenen Änderungen entweder bewahrt worden sind, oder verworfen werden können. Vergessen Sie nicht, die Änderungen zurückzunehmen, die Harry an Makefile gemacht hat. Da bar.c bereits zum neu Hinzufügen vorgemerkt ist, bleibt nichts mehr zu tun, und der Konflikt kann als aufgelöst markiert werden:

$ svn delete --force code/baz.c
D         code/baz.c
$ svn resolve --accept=working code/bar.c 
Konflikt von »code/bar.c« aufgelöst
$ svn status
M       code/foo.c
A  +    code/bar.c
D       code/baz.c
M       Makefile
$ svn diff
Index: code/foo.c
===================================================================
--- code/foo.c	(revision 14)
+++ code/foo.c	(working copy)
@@ -3,5 +3,5 @@
 int main(int argc, char *argv[])
 {
     printf("I don't like being moved around!\n%s", bar());
-    return 0;
+    return 1;
 }
Index: code/bar.c
===================================================================
--- code/bar.c	(revision 14)
+++ code/bar.c	(working copy)
@@ -1,4 +1,4 @@
 const char *bar(void)
 {
-    return "Me neither!\n";
+    return "Well, I do like being moved around!\n";
 }
Index: code/baz.c
===================================================================
--- code/baz.c	(revision 14)
+++ code/baz.c	(working copy)
@@ -1,4 +0,0 @@
-const char *bar(void)
-{
-    return "Me neither!\n";
-}
Index: Makefile
===================================================================
--- Makefile	(revision 14)
+++ Makefile	(working copy)
@@ -1,2 +1,2 @@
 foo: 
-	$(CC) -o $@ code/foo.c code/baz.c
+	$(CC) -o $@ code/foo.c code/bar.c

Sie haben nun Ihren ersten Baumkonflikt aufgelöst! Sie können Ihre Änderungen übertragen und Harry in der Kaffeepause erzählen, welche Mehrarbeit er Ihnen bereitet hat.



[9] Natürlich könnten Sie Dateien, die Konfliktmarkierungen enthalten, als konfliktfrei erklären und übertragen, wenn Sie es wirklich wollten, doch das wird in der Praxis kaum gemacht.

[10] In manchen Fällen hätten Subversion 1.5 und 1.6 das für Sie gemacht, doch ist diese Aufs-Geratewohl-Funktionalität in Subversion 1.7 entfernt worden.