Verwenden externer Vergleichs- und Zusammenführungsprogramme

Die Schnittstelle zwischen Subversion und externen Zwei- und Dreiwege-Vergleichsprogrammen geht zurück bis in eine Zeit, als sich die kontextabhängigen Vergleichsfähigkeiten von Subversion allein auf Aufrufe der GNU-diffutils-Werkzeuge stützten, insbesondere diff und diff3. Um das von Subversion benötigte Verhalten zu bekommen, wurden diese Werkzeuge mit mehr als einer handvoll Optionen und Parametern aufgerufen, von denen die meisten sehr werkzeugspezifisch waren. Einige Zeit später entwickelte Subversion seine eigene interne Vergleichsbibliothek, und als Ausfallsicherung wurden dem Subversion-Kommandozeilen-Client die Optionen --diff-cmd und --diff3-cmd hinzugefügt, so dass Benutzer auf einfache Art mitteilen konnten, dass sie die GNU-Werkzeuge diff und diff3 gegenüber der neumodischen internen Vergleichsbibliothek bevorzugen. Wenn diese Optionen verwendet wurden, ignorierte Subversion einfach die interne Vergleichsbibliothek und benutzte die externen Programmen mit den langen Argumentlisten und dem ganzen Drumherum. Uns so ist es noch heute.

Es dauerte nicht lange, bis einige Leute feststellten, das diese einfachen Konfigurationsmechanismen zur Festlegung der Benutzung der externen GNU-Werkzeuge diff und diff3, die an einem bestimmten Ort im System liegen, auch für andere Vergleichswerkzeuge verwendet werden können. Schließlich hat Subversion nicht überprüft, ob die Werkzeuge zur Werkzeugkette der GNU diffutils gehören. Der einzige konfigurierbare Aspekt bei der Verwendung dieser externen Werkzeuge ist allerdings der Speicherort im System – weder die Menge der Optionen noch die Reihenfolge der Parameter usw. Subversion übergibt all diese GNU-Werkzeug-Optionen an Ihr externes Vergleichswerkzeug, ohne zu berücksichtigen, ob das Programm sie überhaupt versteht. Und hier hört es für die meisten Benutzer auf, intuitiv zu sein.

Der Schlüssel zur Benutzung externer Zwei- und Dreiwege-Vergleichsprogramme (natürlich anderer als GNU diff und diff3) mit Subversion besteht darin, die Werkzeuge in Skripte einzupacken, die die Eingaben von Subversion in etwas umwandeln, das Ihr Werkzeug versteht, und dann die Ausgabe Ihres Werkzeugs in ein von Subversion erwartetes Format umzuwandeln – das Format, das die GNU-Werkzeuge verwendet hätten. Die folgenden Abschnitte behandeln die Besonderheiten dieser Erwartungen.

[Anmerkung] Anmerkung

Die Entscheidung, wann ein kontextabhängiges Zwei- oder Dreiwege-Vergleichsprogramm als Teil einer größeren Operation von Subversion gestartet wird, obliegt allein Subversion und wird unter anderem dadurch beeinflusst, ob die Dateien nach Maßgabe der Eigenschaft svn:mime-type menschenlesbar sind. Das bedeutet beispielsweise, selbst falls Sie über das raffinierteste Vergleichs- oder Zusammenführungsprogramm des Universums verfügten, welches Microsoft Word versteht, würde es niemals von Subversion aufgerufen, solange Ihre versionierten Word-Dokumente einen MIME-Typen hätten, der sie als nicht-menschenlesbar kennzeichnet (so wie application/msword). Mehr über MIME-Type-Einstellungen unter „Datei-Inhalts-Typ“

Subversion 1.5 führt die interaktive Auflösung von Konflikten ein (in „Konflikte auflösen (Änderungen anderer einarbeiten)“ beschrieben), und eine den Benutzern angebotene Option ist die Fähigkeit, ein Zusammenführungsprogramm eines Drittanbieters zu starten. Wenn dieses Vorgehen gewählt wird, ermittelt Subversion über die Laufzeitoption merge-tool-cmd den Namen eines externen Zusammenführungsprogramms und startet dies mit den entsprechenden Eingabedateien, sofern es gefunden wird. Dies ist in vielerlei Hinsicht ein Unterschied zum konfigurierbaren Dreiwege-Vergleichsprogramm. Erstens wird das Vergleichsprogramm stets verwendet, um Dreiwege-Vergleiche vorzunehmen, wohingegen das Zusammenführungsprogramm nur dann angewendet wird, falls das Dreiwege-Vergleichsprogramm einen Konflikt entdeckt hat. Zweitens ist die Schnittstelle sehr viel sauberer – Ihr konfiguriertes Zusammenführungsprogramm braucht nur vier Pfadangaben als Kommandozeilenparameter zu akzeptieren: die Basisdatei, die fremde Datei (die die Änderungen aus dem Projektarchiv enthält), die eigene Datei (die lokale Änderungen enthält) und den Pfad der Datei, in der der endgültige Inhalt nach Konfliktauflösung gespeichert werden soll.

Externes diff

Subversion ruft externe diff-Programme mit Parametern auf, die für GNU diff passen und erwartet lediglich, dass das externe Programm mit einem Erfolg signalisierenden Rückgabewert zurückkommt. Für die meisten alternativen diff-Programme sind nur die Argumente an sechster und siebter Stelle interessant – die Pfade der Dateien, die die linke bzw. rechte Seite des Vergleichs repräsentieren. Beachten Sie, dass Subversion das diff-Programm jeweils einmal pro modifizierter Datei aufruft, die die Subversion-Operation berührt, falls Ihr Programm also asynchron läuft (oder als Hintergrundprozess), könnte es sein, dass mehrere Instanzen gleichzeitig ausgeführt werden. Schließlich erwartet Subversion, dass Ihr Programm den Rückgabewert 1 liefert, falls es Unterschiede entdeckt hat, oder 0, falls nicht – jeder andere Rückgabewert wird als fataler Fehler angesehen. [48]

Beispiel 7.2, „diffwrap.py“ und Beispiel 7.3, „diffwrap.bat“ sind Verpackungs-Vorlagen für externe diff-Werkzeuge in den Skriptsprachen Python bzw. Windows-Batch.

Beispiel 7.2. diffwrap.py

#!/usr/bin/env python
import sys
import os

# Geben Sie hier Ihr bevorzugtes diff-Programm an.
DIFF = "/usr/local/bin/my-diff-tool"

# Subversion liefert die benötigten Pfade als die letzten beiden Parameter.
LEFT  = sys.argv[-2]
RIGHT = sys.argv[-1]

# Aufruf des diff-Befehls (ändern Sie die folgende Zeile passend für
# Ihr diff-Programm).
cmd = [DIFF, '--left', LEFT, '--right', RIGHT]
os.execv(cmd[0], cmd)

# Rückgabewert 0 falls keine Unterschiede, 1 falls doch.
# Jeder andere Rückgabewert wird als fatal betrachtet.

Beispiel 7.3. diffwrap.bat

@ECHO OFF

REM  Geben Sie hier Ihr bevorzugtes diff-Programm an.
SET DIFF="C:\Program Files\Funky Stuff\My Diff Tool.exe"

REM Subversion liefert die benötigten Pfade als die letzten beiden Parameter.
REM Das sind die Parameter 6 und 7 (außer Sie benutzen svn diff -x, dann
REM ist alles möglich).
SET LEFT=%6
SET RIGHT=%7

REM Aufruf des diff-Befehls (ändern Sie die folgende Zeile passend für
REM Ihr diff-Programm).
%DIFF% --left %LEFT% --right %RIGHT%

REM Rückgabewert 0 falls keine Unterschiede, 1 falls doch.
REM Jeder andere Rückgabewert wird als fatal betrachtet.

Externes diff3

Subversion ruft externe Zusammenführungsprogramme mit Parametern auf, die für das GNU diff3-Werkzeug passen und erwartet, dass das externe Programm einen Erfolg signalisierenden Rückgabewert liefert und der vollständige Inhalt als Ergebnis der beendeten Zusammenführung in den Standardausgabestrom geschrieben wird (damit Subversion diesen in die entsprechende Datei unter Versionskontrolle umleiten kann). Für die meisten alternativen Zusammenführungsprogramme sind nur die Argumente an neunter, zehnter und elfter Stelle interessant, die den Pfaden der Dateien entsprechen, die die Eingaben eigene, ältere bzw. fremde repräsentieren. Beachten Sie, dass Ihr Skript nicht beendet werden darf, bevor die Ausgabe an Subversion abgeliefert wurde, da Subversion auf die Ausgabe Ihres Zusammenführungsprogramms angewiesen ist. Wenn es schließlich beendet wird, sollte es einen Rückgabewert 0 im Erfolgsfall und 1 bei verbleibenden Konflikten zurückgeben – jeder andere Rückgabewert wird als fataler Fehler angesehen.

Beispiel 7.4, „diff3wrap.py“ und Beispiel 7.5, „diff3wrap.bat“ sind Verpackungs-Vorlagen für externe Zusammenführungsprogramme in den Skriptsprachen Python bzw. Windows-Batch.

Beispiel 7.4. diff3wrap.py

#!/usr/bin/env python
import sys
import os

# Geben Sie hier Ihr bevorzugtes diff-Programm an.
DIFF3 = "/usr/local/bin/my-merge-tool"

# Subversion liefert die benötigten Pfade als die letzten drei Parameter.
MINE  = sys.argv[-3]
OLDER = sys.argv[-2]
YOURS = sys.argv[-1]

# Aufruf des merge-Befehls (ändern Sie die folgende Zeile passend für
# Ihr merge-Programm).
cmd = [DIFF3, '--older', OLDER, '--mine', MINE, '--yours', YOURS]
os.execv(cmd[0], cmd)

# Nach der Zusammenführung muss dieses Skript den Inhalt der
# zusammengeführten Datei nach stdout schreiben. Machen Sie das, wie
# Sie wollen.
# Rückgabewert 0 bei erfolgreicher Zusammenführung, 1 falls noch
# unaufgelöste Konflikte vorhanden. Alles andere wird als fatal
# betrachtet,

Beispiel 7.5. diff3wrap.bat

@ECHO OFF

REM Geben Sie hier Ihr bevorzugtes diff-Programm an.
SET DIFF3="C:\Program Files\Funky Stuff\My Merge Tool.exe"

REM Subversion liefert die benötigten Pfade als die letzten drei Parameter.
REM Das sind die Parameter 9, 10 und 11. Allerdings haben wir zu einem
REM gegebenen Zeitpunkt nur Zugriff auf neun Parameter, so dass wir
REM unser Fenster aus neun Parametern zweimal verschieben, um das zu
REM bekommen, was wir benötigen.
SHIFT
SHIFT
SET MINE=%7
SET OLDER=%8
SET YOURS=%9

REM Aufruf des merge-Befehls (ändern Sie die folgende Zeile passend für
REM Ihr merge-Programm).
%DIFF3% --older %OLDER% --mine %MINE% --yours %YOURS%

REM Nach der Zusammenführung muss dieses Skript den Inhalt der
REM zusammengeführten Datei nach stdout schreiben. Machen Sie das, wie
REM Sie wollen.
REM Rückgabewert 0 bei erfolgreicher Zusammenführung, 1 falls noch
REM unaufgelöste Konflikte vorhanden. Alles andere wird als fatal
REM betrachtet,



[48] Das Handbuch zu GNU diff beschreibt es so: Ein Rückgabewert 0 bedeutet, dass keine Unterschiede gefunden wurden, 1 bedeutet, dass einige Unterschiede gefunden wurden und 2 bedeutet Ärger.