Uni-Logo Instituts-Logo
Institut für Informatik


Einführung in CVS

Dieses Dokument beschreibt die Grundlagen von CVS (Concurrent Version System)



Einführung

Die Verwaltung eines (umfangreichen) Software-Projekts verlangt viel Aufmerksamkeit. Um ältere Versionen wiederherzustellen (z.B. für die Fehlersuche), muss man immer wieder Sicherungskopien der aktuellen Software machen und sie mit entsprechenden Versionsnummer versehen und verwalten. Diese Methode ist umständlich und benötigt unnötig viel Speicherplatz, da erfahrungsgemäss die meisten Veränderungen/Erweiterungen nur in kleinen Schritten vorgenommen werden. Ausserdem muss bei mehreren Entwicklern, die gleichzeitig an einem Projekt arbeiten, darauf geachtet werden, dass Änderungen bzw. Erweiterungen der Software durch einen Entwickler nicht aus Unachtsamkeit durch einen anderen Entwickler überschrieben und somit rückgängig gemacht werden.

Was ist CVS?

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.

Das Repository

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.

WARNING 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!

Das Arbeitsverzeichnis

Jeder Entwickler benötigt seine eigene lokale Version der Software. Diese liegt im sogenannten Arbeitsverzeichnis, das sich irgendwo im Home-Verzeichnis des Entwicklers befinden kann. Dort können Veränderungen vorgenommen werden, ohne mit anderen Entwicklern in Konflikt zu geraten. Um neue Version für die anderen Entwicklern zugänglich zu machen, müssen die entsprechenden Dateien aus dem Arbeitsverzeichnis in das Repository übertragen werden.

Log Messages

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.

Revisionsnummern

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.

Multiple Developers

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.



Die wichtigsten CVS Kommandos

Zunächst einmal müssen die CVS Kommandos wissen, wo sich das Repository befindet. Den Pfad zum Repository kann man explizit mit der Option -d direkt als Kommandozeilen-Parameter angeben, z.B.

>cvs -d /usr/local/rescue/repository checkout server

Eine andere Möglichkeit ist die Verwendung der Umgebungsvariable CVSROOT, die den absoluten Pfad zum Repository enthält, z.B. durch folgende Zeile in der .login Datei:

>setenv CVSROOT /usr/local/rescue/repository

Liegt das repository auf einem anderen Rechner und ist nicht per NFS zugänglich, so kann auch per scp darauf zugegriffen werden. Das repository wird dann mit dem Zusatz "LoginName@Rechnername" spezifiziert, z.B.

>setenv CVSROOT student@mahlzahn:/usr/local/rescue/repository

Init - Das Repository initialisieren

Ein repository wird mit dem Befehl 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    
    

Import - Daten in das Repository importieren

Ein bestehendes Projekt kann unter Revisionskontrolle gestellt werden, indem alle entsprechenden Dateien auf einmal in das Repository importiert werden. Um z.B. Software in das Repository-Verzeichnis 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

ausführen. Mit der Option -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/

aus, wird für alle Dateien im Verzeichnis repository die Gruppe gruppe01 gesetzt.

Checkout - Ein Arbeitsverzeichnis anlegen

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.)

Commit - Änderungen in das Repository übertragen

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.

Update - Das Arbeitsverzeichnis 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:

Add - Dateien zum Repository hinzufügen

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
    

Remove - Dateien aus dem Repository entfernen

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 commit                     
    
Beachten 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.

CVS und Emacs

Es ist möglich, verschiedene CVS Kommandos komfortabel von Emacs aus auszuführen. Dabei werden z.T. mehrere Schritte automatisch ausgeführt, so dass der Arbeitsaufwand auf ein Minimum reduziert wird.

Commit und automatischer Update einer Datei

Haben sie eine Datei bearbeitet, die sie nun als neue Version in das Repository ablegen wolllen und befindet sich diese Datei im aktuellen Buffer, so führen sie die folgenden Schritte aus:
  1. Rufen sie die Funktion vc-next-action (C-x v v) aus. Es wird ein neuer Buffer geöffnet, in dem sie ihre Log Message eintippen können.
  2. Beenden sie mit 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).
Hat sich jedoch der Inhalt der Datei im Repository seit ihrem letzten Checkout bzw. Update verändert, so erscheint nach Aufruf der Funktion 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.



Nützliche CVS Kommandos

Tag - Versionen markieren

Zwar kann man als Anwender in einem bestimmten Umfang die Revisionsnummer einzelner Files selbst bestimmen, in der Regel jedoch vergibt CVS diese Nummer automatisch. Ein File durchläuft somit während seiner Entwicklung eine Reihe von Revisionen (im CVS Kontext vermeidet man die Verwendung des Wortes Version) und man kann mit Hilfe von CVS jede beliebige Revision wiederherstellen bzw. betrachten.

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.6
    
Wä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 myAgentsaus, 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.


Created: Mon Mar 8 17:40:46 MET 1999
Wolfgang Hatzack
Last modified: Tue May 7 13:50:23 MET DST 2002
Thilo Weigel