Dieses Kapitel führt in die grundlegenden Konzepte von CVS ein und
gibt dann eine tiefer gehende Erläuterung des täglichen CVS-Einsatzes.
Nach dessen Lektüre sind Sie auf dem besten Wege, ein CVS-Guru zu
werden.
Wenn Sie noch nie CVS (oder ein anderes Versionsverwaltungssystem)
benutzt haben, kann es leicht sein, dass Sie durch einige der zu
Grunde liegenden Annahmen ins Stolpern geraten. Was anfänglich
anscheinend für die meiste Verwirrung sorgt ist, dass CVS für zwei
scheinbar unabhängige Aufgaben eingesetzt wird: Historienverwaltung
und Zusammenarbeit. Es zeigt sich jedoch, dass diese beiden
Funktionen eng miteinander verbunden sind.
Die Historienverwaltung wurde notwendig, weil Benutzer den momentanen
Zustand eines Programmes mit dem an einem bestimmten Zeitpunkt der
Vergangenheit vergleichen wollten. Zum Beispiel bringt ein
Programmierer während der Implementation einer neuen Funktion das
Programm in einen ziemlich fehlerhaften Zustand, in dem das Programm
auch wahrscheinlich erst einmal bleiben wird, bis diese neue Funktion
endgültig fertig implementiert ist. Unglücklicherweise ist genau dies
meist der Zeitpunkt, zu dem ein Benutzer die Beschreibung eines
Fehlers in der letzten veröffentlichten Version einschickt. Um diesen
Fehler zu finden (der auch in der aktuellen Entwicklerversion
enthalten sein kann), muss das Programm zuerst wieder in einen
benutzbaren Zustand gebracht werden.
Diesen Zustand wieder herzustellen stellt dann kein Problem dar, wenn
die Historie zu den Quelltexten mit CVS verwaltet wird. Ein Entwickler
kann einfach sagen: »Gib mir den Quelltext, wie er vor drei Wochen
war«, oder vielleicht: »Gib mir den Quelltext so, wie er war, als wir
die letzte öffentliche Version freigegeben haben.« Wenn Sie bisher
noch nie einen so praktischen Zugriff auf frühere Versionen hatten,
werden Sie überrascht sein, wie schnell man davon abhängig werden
kann. Persönlich verwende ich eine Revisionskontrolle bei allen
meinen Programmierprojekten - dies hat mich schon oft gerettet.
Um zu verstehen, was dies mit der Unterstützung der Zusammenarbeit zu
tun hat, müssen wir zunächst die Mechanismen etwas näher betrachten,
mit denen CVS es ermöglicht, dass mehrere Personen zusammen an einem
Projekt arbeiten. Doch zuvor sehen wir uns einen Mechanismus an, den
CVS nicht bietet (oder der zumindest nicht zu empfehlen ist):
Dateisperren. Wenn Sie bereits andere Versionsverwaltungssysteme
benutzt haben, werden Sie schon mit dem Entwicklungsmodell
Sperren-Ändern-Freigeben vertraut sein, bei dem ein Entwickler zuerst
den exklusiven Schreibzugriff auf die zu bearbeitende Datei (eine
Sperre) bekommen muss, die Veränderungen vornimmt und dann die Sperre
wieder freigibt, damit andere Entwickler auf diese Datei zugreifen
können. Wenn jemand anderes bereits eine Sperre für diese Datei
gesetzt hat, so muss er diese zuerst wieder freigeben, bevor man
selbst eine Sperre setzen und Veränderungen anbringen kann. (In
manchen Implementationen kann man diese Sperre auch stehlen,
was
aber für den anderen eine böse Überraschung und außerdem kein guter
Stil ist!)
Dieses System ist dann brauchbar, wenn sich die Entwickler kennen,
wissen, wer was zu einem bestimmten Zeitpunkt machen möchte, und, im
Falle von Zugriffskonflikten, schnell miteinander kommunizieren
können. Wenn jedoch die Entwicklergruppe zu groß wird oder zu
weiträumig verstreut ist, knabbert die Verwaltung der Sperren an der
eigentlichen Arbeitszeit; dies wird zu einem ständigen Problem und
entmutigt viele, wirkliche Arbeit zu leisten.
CVS verfolgt einen ausgereifteren Ansatz. Anstatt von den Entwicklern
zu verlangen, sich gegenseitig zu koordinieren, erlaubt CVS den
Entwicklern gleichzeitiges Arbeiten, übernimmt die Integration der
Veränderungen und behält mögliche Konflikte im Auge. Dieser Prozess
benutzt das Kopieren-Modifizieren-Zusammenfassen-Modell, das
wie
folgt funktioniert:
Entwickler A fordert eine Arbeitskopie von CVS an (ein
Verzeichnisbaum, der alle Dateien eines Projektes enthält). Dies wird
auch Checking out einer Arbeitskopie genannt, wie das Ausleihen
eines Buches aus einer Bibliothek.
Entwickler A arbeitet frei an seiner Arbeitskopie. Zum gleichen
Zeitpunkt können auch andere Entwickler an ihren eigenen Arbeitskopien
fleißig sein. Weil alle Kopien unabhängig voneinander sind, gibt es
auch keine Konflikte - es ist so, als hätten alle Entwickler ihre
eigene Kopie des gleichen Buches aus der Bibliothek, und sie alle
schreiben, unabhängig voneinander, Kommentare an die Ränder oder
bestimmte Seiten vollständig neu.
Entwickler A beendet seine Veränderungen und sendet diese mit einer
Log-Nachricht, also einem Kommentar, der beschreibt, was der
Zweck
der Veränderungen war, an den CVS-Server (commit). Dies ist
damit
vergleichbar, die Bibliothek darüber zu informieren, welche
Veränderungen gemacht wurden und warum. Die Bibliothek lässt diese
wiederum in eine Hauptkopie einfließen, wo sie damit für alle Zeit
aufgezeichnet werden.
In der Zwischenzeit können andere Entwickler CVS dazu veranlassen, die
Bibliothek abzufragen, um herauszufinden, ob die Hauptkopie in
jüngster Zeit verändert wurde. Ist dem so, aktualisiert CVS
automatisch deren Arbeitskopie. (Dieser Teil grenzt an Magie und ist
einfach wunderbar, ich hoffe, Sie wissen dies zu schätzen. Stellen
Sie sich vor, wie die Welt wäre, wenn echte Bücher so
funktionieren würden!)
Soweit es CVS betrifft, sind alle Entwickler eines Projektes gleich.
Zu entscheiden, wann ein Commit oder eine Aktualisierung
durchgeführt
wird, ist eine Sache der persönlichen Einschätzung oder
der Projektregeln. Eine übliche Strategie bei Programmierprojekten ist es,
immer eine Aktualisierung zu machen, bevor die Arbeit an größeren
Veränderungen begonnen wird, und einen Commit erst dann zu
machen,
wenn die Veränderungen vollständig und getestet sind, sodass die
Hauptkopie immer in einem funktionsfähigen Zustand ist.
Vielleicht fragen Sie sich, was passiert, wenn die Entwickler A und B
in ihren Arbeitskopien unterschiedliche Veränderungen an dem gleichen
Stück (Quell-)Text vornehmen und beide ihre Veränderungen mittels
Commit abschicken? Dies wird Konflikt genannt und von CVS
entdeckt,
sobald Entwickler B, versucht seine Veränderungen abzuschicken.
Anstatt Entwickler B zu erlauben fortzufahren, gibt CVS bekannt, dass
es einen Konflikt gefunden hat, und setzt Konfliktmarkierungen
(leicht zu erkennende Marken im Text) an die in Konflikt stehenden
Stellen im Text seiner Kopie. Diese Stellen beinhalten beide
Veränderungen und sind derart angeordnet, dass sie leicht verglichen
werden können. Entwickler B muss sich nun alles noch einmal ansehen
und eine neue Version abschicken, die den Konflikt auflöst.
Vielleicht müssen die beiden Entwickler miteinander reden, um die
Sache zu klären. CVS alarmiert nur die Entwickler über die Konflikte;
es ist an den Menschen, diese tatsächlich zu lösen.
Was ist nun mit der Hauptkopie? In der offiziellen CVS-Terminologie
wird diese das Archiv (Repository) eines Projektes genannt. Das Archiv
ist schlicht nur ein Datei-/Verzeichnisbaum, der auf einem Server
gespeichert ist. Ohne zu stark in die Tiefe der Struktur zu gehen
(siehe jedoch Kapitel 4), werfen wir einen Blick darauf, was das
Archiv leisten muss, um den Anforderungen des
Checkout-Commit-Aktualisieren-Zyklus gerecht zu werden.
Stellen Sie sich folgendes Szenario vor:
Zwei Entwickler, A und B, führen gleichzeitig einen Checkout
des
gleichen Projektes aus. Das Projekt befindet sich noch am
Ausgangspunkt - es wurden noch von niemandem Veränderungen
per Commit an das Archiv geschickt, sodass sich noch alle
Dateien in
ihrem ursprünglichen Zustand befinden.
Entwickler A beginnt sofort mit seiner Arbeit und führt schon bald den
ersten Commit seiner Änderungen aus.
In der Zwischenzeit sieht Entwickler B fern.
Entwickler A arbeitet, als ob es kein Morgen gäbe, und führt einen
zweiten Commit für einen weiteren Satz von Veränderungen aus.
Das
Archiv enthält nun die Originaldateien, gefolgt von As erstem Satz
von Veränderungen, gefolgt von diesem Satz an Veränderungen.
In der Zwischenzeit spielt Entwickler B Videospiele.
Plötzlich schließt sich Entwickler C dem Projekt an und macht einen
Checkout einer Arbeitskopie aus dem Archiv. Die Kopie von
Entwickler C
enthält As erste zwei Sätze von Veränderungen, weil diese schon im
Archiv enthalten waren, als C für seine Arbeitskopie einen
Checkout
gemacht hat.
Entwickler A arbeitet weiter wie von Geistern besessen, vollendet
seinen dritten Satz an Veränderungen und führt abermals einen
Commit
aus.
Zu guter Letzt, nichts ahnend von der jüngsten rasanten Aktivität,
entscheidet Entwickler B, dass es Zeit wird, an die Arbeit zu gehen.
Er kümmert sich nicht darum, eine Aktualisierung seiner Arbeitskopie
durchzuführen; er fängt an, Dateien zu bearbeiten, von denen einige
jene Dateien sein könnten, an denen A gearbeitet hat. Kurz darauf
führt Entwickler B seinen ersten Commit dieser Veränderungen
aus.
An diesem Punkt können nun zwei Dinge passieren. Wenn keine der von
Entwickler B bearbeiteten Dateien von A bearbeitet wurde, dann ist der
Commit erfolgreich. Wenn CVS jedoch merkt, dass einige der
Dateien
von B verglichen mit den aktuellen Dateien des Archivs veraltet sind
und diese auch von B in seiner Arbeitskopie verändert wurden,
informiert CVS B darüber, dass er eine Aktualisierung durchführen
muss, bevor ein Commit durchgeführt werden kann.
Wenn Entwickler B die Aktualisierung durchführt, fügt CVS alle
Veränderungen von A in Bs lokale Kopien der Dateien ein. Einige von As
Veränderungen können mit Bs noch nicht abgeschickten Veränderungen in
Konflikt geraten, manche nicht. Die Teile, welche nicht in Konflikt
stehen, werden einfach ohne weitere Komplikationen in Bs Kopie
eingefügt; die in Konflikt stehenden müssen zuerst von B bereinigt
werden, bevor der Commit durchgeführt werden kann.
Wenn Entwickler C nun eine Aktualisierung durchführt, bekommt er
mehrere Sätze an Veränderungen aus dem Archiv: den dritten
Commit von
A, den ersten von B und vielleicht den zweiten von B (wenn B die
Konflikte aufgelöst hatte).
Damit CVS Veränderungen in der richtigen Reihenfolge an die Entwickler
verteilen kann, deren Arbeitskopien unter Umständen unterschiedlich
stark veraltet sind, muss das Archiv alle Commits seit
Projektbeginn
aufzeichnen. In der Praxis speichert das CVS-Archiv diese als
aufeinander folgende Diffs. Daher kann CVS auch noch für sehr
alte
Arbeitskopien den Unterschied zwischen den Dateien der Arbeitskopien
und dem aktuellen Stand des Archivs bestimmen und dadurch die
Arbeitskopie effizient wieder auf den aktuellen Stand bringen. Für
Entwickler ist es dadurch einfach, die Historie des Projektes
einzusehen und zu jedem Zeitpunkt sogar sehr alte Arbeitskopien
wieder zum Leben zu erwecken.
Obwohl das Archiv genau genommen das gleiche Resultat mit anderen
Methoden erreichen könnte, ist das Abspeichern der Diffs eine
einfache
und intuitive Methode, die notwendige Funktionalität zu
implementieren. Dieser Prozess hat den zusätzlichen Vorteil, dass CVS
durch die korrekte Anwendung von patch jeden vorangegangenen
Zustand
des Verzeichnisbaumes wiederherstellen und damit jede Arbeitskopie von
einem Zustand in einen anderen überführen kann. Es erlaubt jedem,
einen Checkout. Daher des Projektes in einem womöglich
vergangenen Zustand
zu machen. Es kann ebenso die Unterschiede im diff-Format
zwischen
zwei Zuständen des Projektes aufzeigen, ohne dabei irgendeine
Arbeitskopie zu beeinflussen.
Daher sind genau diese Funktionen, die den vernünftigen Zugriff auf
die Historie eines Projektes zulassen, auch dafür nützlich, es einer
dezentralen, unkoordinierten Entwicklergruppe zu ermöglichen, an
einem Projekt zusammenzuarbeiten.
Die Details, wie ein Archiv angelegt wird, Benutzerzugriffe
administriert werden und CVS-spezifische Dateiformate gehandhabt
werden (diese werden in Kapitel 4 beschrieben), können Sie erst
einmal außer Acht lassen. Im Augenblick konzentrieren wir uns darauf,
wie Veränderungen an einer Arbeitskopie durchgeführt werden können.
Doch zuerst noch eine kurze Übersicht der Terminologie:
Revision - Eine Veränderung an einer Datei oder Menge von Dateien, die
durch einen Commit abgeschlossen wurde. Eine Revision ist eine
Momentaufnahme eines sich ständig verändernden Projektes.
|
Archiv - Die Hauptkopie, in der CVS die vollständige Revisionshistorie
eines Projektes speichert. Jedes Projekt hat genau ein Archiv.
|
Arbeitskopie - Die Kopie, mit der gearbeitet wird und die tatsächlich
verändert wird. Es kann mehrere Arbeitskopien eines bestimmten
Projektes geben; im Allgemeinen hat jeder Entwickler seine eigene
Kopie.
|
Checkout - Eine Arbeitskopie von dem Archiv anfordern. Die
angeforderte Kopie stellt den Zustand des Projektes zu dem Zeitpunkt
dar, zu dem die Kopie angefordert wurde; wenn Sie oder andere
Entwickler Veränderungen vornehmen, müssen commit und update
durchgeführt werden, um die eigenen Veränderungen zu
»veröffentlichen« und die der anderen Mitentwickler sehen zu können.
|
Commit - Senden der eigenen Veränderungen zum zentralen Archiv. Auch
Check-in genannt.
|
Log-Nachricht - Ein Kommentar der bei einem Commit einer Revision
angehängt wird und die vorgenommenen Veränderungen beschreibt. Andere
Entwickler können durch die Log-Nachrichten blättern und erhalten so
die Antwort auf die Frage, was in dem Projekt passiert ist.
|
Aktualisierung (update) - Veränderungen von anderen Entwicklern vom
Archiv in die eigene Arbeitskopie aufnehmen und aufzeigen, ob die
eigene Arbeitskopie noch nicht durch commit abgeschickte
Veränderungen enthält.
|
Konflikt - Situation, in der zwei Entwickler Veränderungen im gleichen
Teil der gleichen Datei per commit abzuschicken versuchen. CVS bemerkt
solche Konflikte und benachrichtigt die Entwickler, aber die
Entwickler müssen diese selbst auflösen.
|
|