![]() |
Institut für Informatik |
Dieses Dokument beschreibt die Grundlagen von CVS (Concurrent Version System)
CVS (Concurrent Version System) ist zum einen ein Tool zur Versionskontrolle, mit dem man die Entwicklung eines Software-Projekts aufzeichen kann, um somit beliebige Vorgängerversionen wiederherstellen zu können. Dazu werden alle Versionen einer Datei in einer sogenannten history Datei abgelegt. Um Speicherplatz zu sparen, werden dabei nur die inkrementellen Änderungen gespeichert. Zum anderen ist CVS auch in der Lage, die Bearbeitung eines Software-Projekts durch mehreren Entwicklern zu unterstützen. Dazu besitzt CVS einfache Mechanismen, um parallele Änderungen an einer Datei zu fusionieren - Überlappungen bzw. Konflikte müssen allerdings "manuell" aufgelöst werden.
CVS verwendet ein sogenanntes Repository, um alle Dateien, die sich unter Versionskontrolle befinden, abzulegen und zu verwalten. Bei den zu verwaltenden Dateien handelt es sich meist um ASCII-Dateien (z.B. Programmcode). Wie bereits erwähnt, werden die Dateien nicht in ihrer ursprünglichen Form, sondern als history Dateien gespeichert. Neben der aktuellen Versionen, die sich explizit in einer history Datei befinden, sind auch ältere Versionen implizit enthalten und können von CVS jederzeit rekonstruiert werden. Der Speicherbedarf für eine Folge von Versionen einer Datei ist äusserst gering, da nur relative Veränderungen von einer Version zur nächsten gespeichert werden.
Das Repository wird ausschliesslich als Ablage und zur Verwaltung
von Dateien verwendet. Das Übertragen von Dateien in das
Repository hinein und wieder hinaus geschieht ausschliesslich
mittles CVS Kommandos und niemals durch direktes Kopieren
bzw. Bewegen von Dateien!
Wird eine neue Version einer Datei im Repository abgelegt, muss zuvor eine Log Message spezifizieren werden, um die vorgenommenen Modifikationen zu beschreiben. Zusammen mit dem User-Namen des entsprechenden Entwicklers sowie dem Modifikationsdatum wird diese Log Message in der entsprechenden history Datei im Repository abgespeichert. Dadurch wird nachvollziehbar, wer zu welchem Zeitpunkt welche Änderung an einer Datei vorgenommen hat.
Mit der Aufnahme in ein Repository wird jede Datei mit einer Revisionsnummer versehen, die automatisch erhöht wird (z.B. von 1.3 auf 1.4), sobald eine neue Version in das Repository abgelegt wird. Somit kann auf ältere Versionen mit Hilfe der Revisionsnummer zugegriffen werden.
Bei grösseren Projekten kann es durchaus vorkommen, dass eine Datei gleichzeitig von mehreren Entwicklern bearbeitet wird. Dies birgt gewisse Probleme in sich, und die einfachste Methode, diese zu umgehen, besteht darin, nur genau eine Person eine Datei bearbeiten zu lassen (file locking bzw. reserved checkout Mechanismus).
Da bei CVS jeder Entwickler seine eigene lokale Arbeitskopie besitzt, kann es erst beim Ablegen neuer Versionen in das Repository zu Konflikten kommen. Der erste Entwickler, der eine neue Version in das Repository ablegen möchte, wird nichts davon merken, dass zur gleichen Zeit die Datei an anderer Stelle bearbeitet wird. Ein nachfolgender Entwickler hingegen wird beim Ablegen seiner neuen Versionen darüber informiert, dass diese nicht mehr auf der aktuellsten Version der entsprechenden Datei im Repository basiert. Er hat dann die Möglichkeit, seine lokale Version auf den neuesten Stand zu bringen, indem diese automatisch mit der neusten Version im Repository fusioniert wird. Kommt es dabei zu Konflikten, müssen diese "manuell" aufgelöst werden.
-d
direkt als Kommandozeilen-Parameter
angeben, z.B.
>cvs -d /usr/local/rescue/repository checkout server
CVSROOT
, die den absoluten Pfad zum Repository
enthält, z.B. durch folgende Zeile in der .login
Datei:
>setenv CVSROOT /usr/local/rescue/repository
scp
darauf zugegriffen werden.
Das repository wird dann mit dem Zusatz "LoginName@Rechnername"
spezifiziert, z.B.
>setenv CVSROOT student@mahlzahn:/usr/local/rescue/repository
cvs init
neu erzeugt.
Dies bewirkt, daß das als CVSROOT
oder mit der
-d
-Option spezifizierte Verzeichnis angelegt wird und in
einem Unterverzeichnis CVSROOT
einige Verwaltungsdateien
erzeugt werden, z.B.
>cvs -d /home/gruppe01/repository init >cd repository/CVSROOT/ Directory: /home/gruppe01/repository/CVSROOT >ls Emptydir config editinfo,v modules,v taginfo checkoutlist config,v history notify taginfo,v checkoutlist,v cvswrappers loginfo notify,v val-tags commitinfo cvswrappers,v loginfo,v rcsinfo verifymsg commitinfo,v editinfo modules rcsinfo,v verifymsg,v
myAgents
zu importieren,
einfach in das entsprechende Projektverzeichnis wechseln
(z.B. /home/gruppe01/repository/
) und dort das Kommando
>cvs import -m "Imported sources" myAgents v_tag r_tag
-m
kann man eine Log
Message spezifizieren, ohne dass für jede einzelne Datei ein
Editor für die Eingabe einer Log Message gestartet
wird. v_tag
und r_tag
sind ein
vendor tag bzw. ein release tag, die in diesem
Zusammenhang zwar keinen Sinn machen, aber von CVS verlangt
werden.
Nach dem ein Projekt unter Revisionskontrolle gestellt wurde, ist darauf zu achten, dass die Gruppe für die Projektdateien und -verzeichnisse entsprechend gesetzt wird. Führt man z.B. im CVS-Hauptverzeichnis (also hier /home/gruppe01/repository) den Befehl
>chrgp -R gruppe01 repository/
repository
die
Gruppe gruppe01
gesetzt.
Um ihr eigenes Arbeitsverzeichnis zu erstellen, wechseln sie in ihr
Projektverzeichnis (z.B. /home/student/sopra02
) und
stellen sie sicher, dass sich dort kein Verzeichnis
myAgents
befindet. Der Befehl
>cvs checkout myAgents
legt dann für sie im aktuellen Verzeichnis das
Arbeitsverzeichnis myAgents
an und kopiert ihnen die
aktuellste Version der Software aus dem Repository dort
hinein. Sie haben für jede Datei volle Schreib-/Leserechte und
können die Dateien wie gewohnt editieren. Die meisten CVS
Kommandos funktionieren übrigens nur, nachdem
checkout
ausgeführt wurde.
Wenn sie sich ihr Arbeitsverzeichnis genauer anschauen, werden
sie feststellen, das in jedem Verzeichnis ein zusätzliches
Unterverzeichnis CVS
angelegt wurde. Es enthält
Verwaltungsdaten und sollte von ihnen nicht weiter beachtet
werden.
Sie können mit dem Kommando checkout
auch
einzelne Verzeichnisse und Dateien aus dem Repository holen. Dies
ist hilfreich, wenn sie z.B. Änderungen verwerfen wollen. Löschen
sie dazu das entsprechende Verzeichnis bzw. die Datei, und
wechseln sie anschliessend in ihr Projektverzeichnis, in dem sich
ihr Arbeitsverzeichnis befindet (z.B. /home/student/sopra02). Die
aktuelle Version eines Verzeichnisses erhalten sie z.B. durch
folgenden Befehl:
>cvs checkout myAgents/fireBrigade
Um Modifikationen nur an einer einzelnen Datei zu verwerfen, können sie auch die entsprechende Datei löschen und anschliessend ein Update ausführen (s.u.)
Haben Sie in ihrem Arbeitsverzeichnis Änderungen an Dateien
vorgenommen, sind diese für die anderen erst zugänglich, nachdem
sie in das Repository übertragen wurden. Dazu wird das Kommando
commit
verwendet.
Gibt man eine Datei als Argument an, so wird nur diese eine Datei in das Repository übertragen. Ohne Argument werden alle modifizierten Dateien im aktuellen Verzeichnis (einschliesslich aller Unterverzeichnisse) in das Repository übertragen. Beispiel:
>cvs commit
Bevor jedoch eine neue Version einer Datei in das Repository
abgelegt wird, führt CVS zunächst einen up-to-date check
durch. Wird dabei festgestellt, dass in der Zwischenzeit bereits
eine neue Version im Repository abgelegt wurde, so wird das
commit
Kommando mit der Fehlermeldung
Up-to-date check failed
abgebrochen, damit nicht die
Modifikationen anderer Entwickler rückgängig gemacht bzw.
überschrieben werden. Um ihre Änderungen dennoch in das Repository
übertragen können, müssen sie zuerst noch ein update
ausführen (s.u.)
Wurde der up-to-date check bestanden, muss noch eine Log
Message geschrieben werden. Mit der Umgebungsvariable
CVSEDITOR
können sie angeben, welchen Editor sie zum
Schreiben der Log Message verwenden wollen. Beispiel:
>setenv CVSEDITOR vi
Nachdem sie die Log Message gespeichert und den entsprechenden Editor beendet haben, werden interne Verwaltungsdaten wie z.B. Revisionsnummer, Modifikationsdatum und das Log Message Protokoll der Datei automatisch aktualisiert. Durch Verwendung sogenannter Keywords wie z.B. "$Revision$", "$Date$" und "$Log$" im Quelltext einer Datei kann diese Information externalisiert und somit angeschaut werden. Die Programmcodes enthalten diese Keywords in nachträglich eingefügten Kopfzeilen:
/**************************************************************** * * $RCSfile: courageousFireMan.java,v $ * * $Revision: 1.2 $ * * $Date: 2002/05/06 14:30:29 $ * * AUTHOR: studentXY * * DESCRIPTION: version of a fire man agent which takes high risks * ****************************************************************/ /* * * $Log: Field.cpp,v $ * Revision 1.2 1999/03/16 14:30:29 studentXY * Added file description, log message display and copyright note. * */
Nach der Aktualisierung der Verwaltungsdaten wird die Datei schliesslich in das Repository übertragen. Die entsprechende CVS Ausgabe sollte in etwa so aussehen:
Checking in testAgent.java; /home/gruppe01/repository/myAgents/fireBrigade/courageousFireMan.java,v <-- courageousfireMan.cpp new revision: 1.2; previous revision: 1.1 done
Befindet sich die Datei, die soeben in das Repository übertragen wurde, zum Zeitpunkt der Übertragung noch im Buffer ihres Editors, so sind dort die Änderungen an den Keywords noch nicht zu sehen - sie müssen dazu den entprechenden Buffer aktualisieren.
In der Regel wird sich das Repository stetig weiterentwickeln,
während sie in ihrem eigenen Arbeitsverzeichnis, das sie zu Beginn
ihrer Arbeit angelegt haben, isoliert weiterarbeiten. Von Zeit zu
Zeit wird es sinnvoll sein, ihr Arbeitsverzeichnis mit dem
Kommando update
auf den neuesten Stand zu bringen.
>cvs update
Geben sie als Argument eine Datei an, so wird nur diese eine Datei aktualisiert. Ohne Argument wird das aktuelle Verzeichnis (einschliesslich aller Unterverzeichnisse) aktualisiert. Der Effekt eines Updates hängt von mehreren Faktoren ab:
update
keinen
Effekt.
U
(updated) vor dem entsprechendem
Dateinamen. Beispiel:
cvs update: Updating fireBrigade U fireBrigade/courageousFireMan.java U fireBrigade/cowardlyFireMan.java U fireBrigade/lazyFireMan.java
U
gekennzeichnet, und CVS gibt
sicherheitshalber eine Warnung aus:
cvs update: Updating . cvs update: warning: lazyFireMan.java was lostBeachten sie, dass man mit
update
keine
gelöschten Verzeichnisse wiederherstellen kann! Dazu muss ein
Checkout durchgeführt werden (s.o.).
update
keinen Effekt - ihre modifizierte Version
bleibt also unverändert in ihrem Arbeitsverzeichnis liegen und
wird nicht durch die ältere Version überschrieben. Man erkennt
dies an dem M
(modified) vor dem entsprechendem
Dateinamen. Beispiel:
cvs update: Updating . M lazyFireMan.java
cvs update: Updating . RCS file: /home/gruppe01/repository/myAgents/fireBrigade/lazyFireMan.java,v retrieving revision 1.1.1.1 retrieving revision 1.2 Merging differences between 1.1.1.1 and 1.2 into lazyFireMan.java M lazyFireMan.java
Befindet sich die Datei, in die durch das
update
Kommando Änderungen eingefügt wurden, noch
im Buffer ihres Editors, so sind die von CVS eingefügten
Änderungen dort noch nicht zu sehen - sie müssen dazu den
entsprechenden Buffer aktualisieren.
Kommt es beim Einfügen der Änderungen zu Überlappungen,
wird eine Konfliktwarnung ausgegeben und die entsprechende
Datei mit einem C
(conflict) gekennzeichnet. Eine
entsprechende CVS-Ausgabe sieht in einem solchen Fall ungefähr
so aus:
cvs update: Updating . RCS file: /home/gruppe02/repository/myAgents/lazyFireMan.java,v retrieving revision 1.1.1.1 retrieving revision 1.2 Merging differences between 1.1.1.1 and 1.2 into lazyFireMan.java,v rcsmerge: warning: conflicts during merge cvs update: conflicts found in lazyFireMan.java,v C lazyFireMan.java,v
Es liegt nun an ihnen, den Konflikt durch Editieren der fusionierten Datei aufzulösen. Sicherheitshalber legt CVS eine Sicherungskopie ihrer ursprünglichen Datei an, bevor die Änderungen in ihre Datei eingefügt werden. Ein überlappender Abschnitt wird in ihrer Datei wie folgt gekennzeichnet:
<<<<<<< [ihre Modifikation] ======= [von CVS eingefügter Teil] >>>>>>>Beachten sie, dass es mehrere solcher Abschnitte geben kann, und dass sie diese Markierungen entfernen müssen, bevor sie ihre Datei erneut mit dem
commit
Kommando in das
Repository übertragen (aus Sicherheitsgründen ist es übrigens
nicht möglich, eine mit C
gekennzeichnete Datei
in das Repository zu übertragen).
Haben Sie in ihrem Arbeitsverzeichnis eine neue Datei erstellt,
die in das Repository aufgenommen werden soll, so muß
zunächst mit dem Kommando add
mitgeteilt werden, daß
eine neue Datei existiert. Mit dem Kommando commit
wird die Datei dann tatsächlich in das Repository übertragen. Beispiel:
>cvs add courageousFireMan.java cvs add: scheduling file `courageousFireMan.java' for addition cvs add: use 'cvs commit' to add this file permanently >cvs commit courageousFireMan.java
Wird eine Datei nicht mehr benötigt und soll daher auch aus
dem Repository entfernt werden, so kann dies mit dem Kommando
remove
geschehen. Wie bei add
muß
zunächst mitgeteilt werden, daß eine Datei entfernt werden soll
und dies dann mit commit
tatsächlich durchgeführt
werden. eine Datei kann nur dann aus dem Repository entfernt
werden, wenn sie im Arbeitsverzeichnis vorher gelöscht wurde. Beispie:
>cvs remove courageousFireMan.java cvs remove: file `courageousFireMan.java' still in working directory cvs remove: 1 file exists; remove it first >rm courageousFireMan.java >cvs remove courageousFireMan.java cvs remove: scheduling `courageousFireMan.java' for removal cvs remove: use 'cvs commit' to remove this file permanently >cvs commitBeachten Sie, daß nur die aktuelle Version der Datei entfernt wird, ältere Revisionen aber nach wie vor ausgecheckt werden können, da die entfernte Datei in einem Verzeichnis
Attic
nach wie vor gespeichert ist.
vc-next-action
(C-x v v)
aus. Es wird ein neuer Buffer geöffnet, in dem sie ihre Log
Message eintippen können.
vc-finish-logentry
(C-c C-c)
ihre Eingabe - der Buffer wird geschlossen. Anschliessend wird
ihre Datei in das Repository übertragen und ihr Buffer auf den
neuesten Stand gebracht (insbesondere werden dabei die Keywords
aktualisiert).
vc-next-action
zunächst in der
Echo-Zeile die folgende Meldung:
[file] is not up-to-date. Merge in changes now (y or n)?Antworten sie mit
y
, so wird ein Update für diese
Datei ausgeführt, d.h. die letzten Änderungen im Repository
werden automatisch in ihre Datei eingefügt. Kommt es dabei zu
keinem Konflikt, erscheint der Log Message Buffer und sie können
mit Schritt 2 fortfahren.
Gibt es jedoch beim Fusionieren überlappende Bereiche, so müssen sie zunächst den Konflikt manuell auflösen und dann die beiden Schritte erneut ausführen.
Meist besteht ein Projekt bzw. ein Projektmodul nicht aus einem einzigen File, sondern aus einer Menge von Files. Möchte man sich nun den Zustand einer Menge von Files merken, so muss man für jedes einzelne File wissen, mit welcher Revision es an diesem Zustand beteiligt war. Dies geschieht in CVS dadurch, dass die aktuellen Revisionen mit einem Tag, also einem symbolischen Namen versehen werden. Beispiel:
file1 file2 file3 file4 file5 1.1 1.1 1.1 1.1 /--1.1* <-*- TAG 1.2*- 1.2 1.2 -1.2*- 1.3 \- 1.3*- 1.3 / 1.3 1.4 \ 1.4 / 1.4 \-1.5*- 1.5 1.6Während man bei einem einzelnen File von Revision spricht, handelt es sich bei einer Menge von Files um ein sogenanntes Release. Im obigen Beispiel besteht das Modul aus 5 Files, und mit welcher Revision die Files am letzten Release beteiligt waren, kann man anhand der durchgezogenen Linie erkennen.
Um eine Menge von Files mit einem Tag zu versehen, muss man in
seinem Arbeitsverzeichnis das CVS Kommando mit der Option
tag
ausführen. Führt man z.B. den Befehl
>cvs tag agents-3-1-1 .im Verzeichnis
myAgents
aus, so werden alle Datein rekursiv,
also einschliesslich aller Unterverzeichnise, mit dem Tag
agents-3-1-1
versehen, und zwar nicht nur im
Arbeitsverzeichnis, sondern auch automatisch im Repository. Mit
>cvs tag fireBrigade-2-4-12 fireBrigade/würden alle Files im fireBrigade-Verzeichnis mit dem Tag
fireBrigade-2-4-12
versehen. Es ist sinnvoll, für jedes
Modul im entsprechenden Verzeichnis ein Protokoll-File
(z.B. .release
) anzulegen, in das man für jedes
Release einen kleinen Kommentar eingibt, was im Vergleich zum
vorherigen Release neu bzw. anders ist. Dieses Protokoll-File
sollte ebenfalls unter Revisionskontrolle gestellt werden.