Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 17 Werkzeuge für Programmierer
  gp 17.1 Make
    gp 17.1.1 Erzeugen eines Makefiles
    gp 17.1.2 Variablen, Makros und Abkürzungen
    gp 17.1.3 Implizite Regeln
    gp 17.1.4 Musterregeln
    gp 17.1.5 make zur Installation verwenden
    gp 17.1.6 make-Optionen
    gp 17.1.7 Ausblick
  gp 17.2 Bibliotheken erstellen
    gp 17.2.1 Statische Bibliotheken erstellen
    gp 17.2.2 Dynamische Bibliotheken (Shared Libraries) erstellen
    gp 17.2.3 Dynamisches Nachladen von Bibliotheken
  gp 17.3 RPM
    gp 17.3.1 Verzeichnisse, die RPM benötigt
    gp 17.3.2 Ein eigenes RPM-Paket erstellen
    gp 17.3.3 Sources
    gp 17.3.4 Die Spec-Datei
    gp 17.3.5 Paket erstellen
    gp 17.3.6 Das Paket installieren
  gp 17.4 RCS und CVS
    gp 17.4.1 Software-Configuration-Management-Systeme (SCM)
    gp 17.4.2 RCS
    gp 17.4.3 CVS
  gp 17.5 Zeitmessung von Programmen
    gp 17.5.1 Einfache Zeitmessung mit TIME – Laufzeit von Prozessen
    gp 17.5.2 Profiling mit GPROF – Laufzeit von Funktionen
    gp 17.5.3 Analyse mit GCOV
  gp 17.6 Debuggen mit gdb und ddd
  gp 17.7 STRACE – Systemaufrufe verfolgen
  gp 17.8 Memory Leaks und unerlaubte Speicherzugriffe
    gp 17.8.1 efence
    gp 17.8.2 valgrind
  gp 17.9 Ausblick


Rheinwerk Computing

17.4 RCS und CVS  downtop

In diesem Abschnitt erhalten Sie einige wichtige Informationen zu so genannten Software-Configuration-Management-Systemen. Dabei werden zwei dieser Tools, nämlich RCS und CVS, etwas näher erläutert.


Rheinwerk Computing

17.4.1 Software-Configuration-Management-Systeme (SCMdowntop

Sicherlich haben Sie schon des Öfteren etwas von RCS oder CVS gelesen und konnten in etwa erahnen, wozu diese Werkzeuge gut sind. Software-Configuration –Management-Systeme (kurz SCM) sind für die Verwaltung von einzelnen Dateien bis hin zu ganzen Projekten verantwortlich. Dabei ist es möglich, jede noch so kleine Änderung zu protokollieren und als eine neue Version abzuspeichern. So kann jederzeit wieder auf eine ältere Version zurückgegriffen werden.

Wozu soll das gut sein? Vielleicht verändern Sie eines Tages in einem Programm ein oder zwei Zeilen Code und kommen nicht gleich dazu, die Veränderungen zu testen. Am nächsten Tag testen Sie das Programm, und es tut nicht das, wozu es gedacht war. Dummerweise wissen Sie jetzt nicht mehr, in welcher Quelldatei oder in welcher Zeile Sie den Code verändert haben. Das bedeutet für Sie erst einmal verlorene Zeit und viel Frust beim Suchen. Sie machen regelmäßig ein Backup, sagen Sie? Bei jeder Codezeile, die Sie verändern? Ich wage es zu bezweifeln, und außerdem ist das sehr umständlich, denn dazu sind ja eigentlich SCM-Systeme entwickelt worden.

Eine weitere interessante Möglichkeit von SCM-Systemen: Sie können damit ohne weiteres Daten von älteren Anwendungen verwenden. Wollen Sie z. B. einen Webbrowser erstellen, dann werden Sie wohl kaum anfangen, sich mit dem kompletten Code des Browsers xyz der Version 7.1. auseinander zu setzen, sondern mit einer möglichst frühen stabilen Version. Sie haben sicherlich schon häufiger etwas derartiges gelesen wie: »Der Browser xyz basiert auf …« Sie können praktisch aus einem älteren bestehenden Projekt ein eigenes ableiten. So entstehen zwar viele Abkömmlinge und Klone, aber Vielfalt tut dem Markt gut.

Und vielleicht noch etwas zum Nachdenken, um Sie von dem Sinn der SCM-Systeme zu überzeugen. Ein gutes Beispiel sind die großen Projekte wie der KDE- oder GNOME-Desktop und die vielen anderen Open-Source-Anwendungen. Sie werden sich sicherlich gefragt haben, wie es möglich ist, dass so viele Programmierer an einem Projekt beteiligt sind, ohne dass sich hier der eine und der andere ins Gehege kommen. Je nach Projekt arbeiten hier nicht gerade nur ein bis zwei Personen – sondern es sind häufig gut und gerne ein Dutzend Entwickler, die sich gleichzeitig einem Projekt widmen. Solche Projekte wären wohl ohne SCM-Systeme kaum möglich. Durch Sperren von einzelnen Texten für einen Benutzer wird dafür gesorgt, dass es nicht vorkommt, dass zwei Personen gleichzeitig daran arbeiten.

Dank der Protokollierung ist es außerdem möglich herauszufinden, wer als Letztes an einer Datei gearbeitet und wieder solchen Murks gemacht hat. Somit kann der Projektleiter schnell die schwarzen Schafe aufspüren und feuern ;-).

Damit es hierbei nicht zu Streitereien kommt, wer denn nun wieder die Datei gesperrt hat und wann diese wieder freigegeben ist, wird meistens ein Gruppenverzeichnis erstellt. Dabei sind dann die neu erstellten Texte immer nur von einer Person zum Schreiben freizugeben. Ist diese Person nicht präsent, kann der Rest der Gruppe nicht mehr weiterarbeiten.


Rheinwerk Computing

17.4.2 RCdowntop

RCS entstand aus dem GNU-Projekt unter UNIX und steht für Revision Control System. Der Vorteil von RCS ist, dass es auf fast allen Plattformen verfügbar ist. RCS ist sozusagen der Vater (oder auch die Mutter) vieler weiterer SCM-Systeme. Auf RCS baut übrigens auch CVS auf. Mehr dazu später. Um es gleich zu erwähnen, ein Nachteil von RCS ist es, dass Sie immer nur einzelne Dateien damit bearbeiten können. Für ganze Projekte müssen Sie entweder ein Shellskript schreiben oder eben CVS verwenden. Für kleinere Aufgaben und zum Einstieg in die Welt von SCM, also auch CVS, ist RCS aber genau das Richtige.

Das Konzept von RCS – die Theorie

Das Konzept von RCS basiert darauf, von einer Datei mehrere Versionen zu verwalten, ohne dadurch viel Speicherplatz zu verschwenden, indem man von einer Datei mehrere Kopien erstellt. Um dies zu erreichen, muss man ein Archiv anlegen, das lokal in einem Unterverzeichnis rcs oder auf einem Server liegt. Die Operationen in diesem Archiv sind das Einchecken einer Datei in das Archiv und das Auschecken mit und ohne Sperre. Will man eine Datei verändern und wieder einchecken, kann dies nur geschehen, wenn diese mit einer Sperre versehen wurde. Solange die Sperre vergeben ist, kann keine andere Person etwas an dieser Datei verändern. Zwar können andere Anwender die Datei lesen oder im Falle eines Quellcodeprojektes kompilieren oder interpretieren (je nach Programmiersprache), aber um etwas zu verändern, muss gewartet werden, bis die Sperre aufgehoben wurde. Damit behält der Projektleiter die Übersicht, wer wann an der Datei gearbeitet hat, und es entsteht kein Datensalat.

In das Archiv sollten Sie außerdem nur lauffähige und fertige Dateien bzw. Anwendungen einchecken. Sie können diesen Versionen auch symbolische Namen (z. B. stable für stabil oder beta für instabil oder …) geben, auf die Sie jederzeit wieder zugreifen können. Damit vermeiden Sie, dass Sie beim Kunden nicht aus Versehen eine instabile Testversion vorführen. Sinnvollerweise sollten dann alle Dateien des Projekts mit diesem symbolischen Namen versehen werden.

Außerdem ist der Zwang, das Projekt zu dokumentieren, ein weiterer Vorteil. Bei jedem Einchecken werden Sie nach einem Kommentar gefragt, was sich in der neuen Version geändert hat. Haben Sie dann (sinnvollerweise) ein entsprechendes RCS-Schlüsselwort im Kommentar des Dateikopfes parat, dann wird die Datei oder der Quellcode mit entsprechenden Hinweisen zu allen Versionen ergänzt.

RCS bezieht sich in der Regel nur auf Textdateien und nicht auf übersetzten Code. Es ist daher nicht abhängig davon, welche Programmiersprache Sie verwenden. Ganz im Gegenteil, im Prinzip könnten Sie auch die Dokumentation Ihrer Kochrezepte mit RCS verwalten. Zwar wird RCS vorwiegend verwendet, um Programmierern unter die Arme zu greifen, aber dies sollte hier dennoch erwähnt werden. RCS speichert die Änderungen einzelner Zeilen, wobei es das Kommando diff verwendet, um Änderungen von einzelnen Zeilen zu erkennen.

Zwar ist es ab Version 5.7 möglich, RCS auch auf das Verwenden von Binärcode anzusetzen, da es aber in Binärdateien eigentlich keine Zeilen gibt, würde die kleinste Veränderung das Speichern der kompletten Kopie der neuen und der alten Version bedeuten. Dann bräuchten Sie eigentlich kein RCS zu verwenden und können gleich regelmäßig »von Hand« ein Backup anlegen.

Bevor Sie eine Datei einchecken oder auschecken, sucht RCS im aktuellen Arbeitsverzeichnis erst nach einem Unterverzeichnis namens RCS. Existiert das Verzeichnis, werden alle von RCS erzeugten Dateien in diesem Verzeichnis gehalten.

Die Datei, die RCS anschließend erzeugt, ist eine separate Datei mit der Endung v. Darin befinden sich die Beschreibung der Datei, das Änderungsprotokoll, die aktuelle Version, eine Liste der Benutzer, Datum und Uhrzeit der Änderung. Mit diesen Informationen kann RCS eine beliebige Version jederzeit wieder herstellen. Trotz dieser umfangreichen Informationen ist die RCS-Datei kaum größer als die Quelldatei. Und wächst diese dennoch einmal beträchtlich an, können unnötige Informationen jederzeit entfernt werden.

Versionsbäume

Mit RCS (und auch vielen anderen SCM-Systemen) werden die verschiedenen Versionen in einer Baumstruktur verwaltet. Die erste Version stellt somit immer die Wurzel des Baumes dar und erhält die Revisionsnummer 1.1. Bei den folgenden Versionen wird der Wert hinter 1.x immer um 1 inkrementiert (1.2, 1.3 …).

Auch ein anderer Entwicklungszweig kann z. B. von der Version 1.3 erstellt werden (1.3.1.1). Die nächsten Versionen beziehen sich dann immer auf die letzte Stelle der Revisionsnummer (1.3.1.2, 1.3.1.3 …).


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.4    Ein einfacher Versionsbaum


Eine Datei einchecken und auschecken – ci und co

Zur Demonstration von RCS soll (wie immer) ein einfaches C-Programm erstellt werden. Sie können aber gerne auch eine Quelldatei oder auch eine einfache Textdatei Ihrer Wahl dazu verwenden. Das Listing soll den Namen datei.c haben. Sollten Sie einen anderen Namen für Ihre Datei verwenden, so passen Sie diesen bitte im weiteren Verlauf Ihren Bedürfnissen an und ändern eben entsprechende Eingaben unter diesem Namen ab.

Erstellen Sie zuerst in einem Verzeichnis Ihrer Wahl ein Verzeichnis namens RCS.

$ mkdir RCS

Alle folgenden RCS-Kommandos verwenden dieses Verzeichnis; natürlich nur, wenn dies auch das aktuelle Arbeitsverzeichnis ist, in dem sich jetzt anschließend auch Ihre Datei datei.c befindet. Erstellen Sie folgende Datei als Nächstes im selben Verzeichnis:

/* $Id$
 * datei.c
 * Beispielcode zur Demonstration von RCS
 * von Jürgen Wolf
 */
#include <stdio.h>
int main(void) {
   printf("Mein erstes Projekt mit RCS\n");
   return 0;
}

Somit befinden sich im aktuellen Arbeitsverzeichnis das Verzeichnis RCS und die Datei datei.c.

$ ls –l 
drwxr-xr-x+   2 Juergen   Kein            0 Nov  8 14:06 RCS
-rw-r--r--    1 Juergen   Kein          193 Nov  8 14:10 datei.c

Zum Einchecken einer Datei wird der Befehl ci (für check in) verwendet. Zusätzlich zu diesem Befehl werden noch der Parameter zum Sperren (Lock) mit -l und das Flag -u angegeben, womit eine Read-only-Version erstellt wird. Ohne den Parameter -l wird keine Sperre gesetzt, was in der Praxis nicht zu empfehlen ist. Also checken Sie die Datei ein:

$ ci –l -u datei.c
RCS/datei.c,v  <-- datei.c
enter description, terminate with single '.' or end of file
NOTE: This is not the Log message
>>.
initial revision 1.1
done

Hier wurde die erste Revision 1.1 der Datei datei.c eingecheckt. Sollte RCS bereits eine Version der Datei verwalten, z. B. 1.2, so ordnet das Kommando der Datei die nächsthöhere Revisionsnummer zu (1.3). Beim Prompt >> wartet RCS auf eine Eingabe von Ihnen. Mit einem Punkt oder dem Auslösen von End of File ((STRG)+(D)) kann die Protokollmeldung beendet werden, und es wird done ausgegeben. Statt der Quelldatei datei.c erzeugt bzw. verändert RCS eine Datei namens datei.c,v, die sich jetzt im Verzeichnis RCS befindet. Die Endung v stellt hier eine Erweiterung zur Erkennung dar, dass es sich um eine RCS-Datei handelt. Wird eine Datei ausgecheckt, extrahiert RCS eine neue Version aus der entsprechenden Datei.

Wenn Sie wollen, können Sie sich jetzt die Datei datei.c in Ihrem Texteditor ansehen. Sie werden in der ersten Zeile, wo Sie im Listing $Id$ angegeben haben, folgende Veränderung bemerkt haben:

$Id: datei.c,v 1.1 2003/11/08 13:19:59 Juergen Exp Juergen $

Dabei handelt es sich um den Identifikationsstring. Aber dazu erfahren Sie mehr bei den Schlüsselwörtern von RCS.

Checken Sie nun die Datei datei.c aus. Dies erledigen Sie mit dem Kommando:

$ co –l datei.c
RCS/datei.c,v --> datei.c
revision 1.1 (locked)

Damit geben Sie die Datei aus der RCS-Kontrolle wieder frei, und Sie finden diese in Ihrem aktuellen Arbeitsverzeichnis wieder. Würden Sie das Kommando co zum Auschecken ohne das Flag –l (locked) zum Sperren verwenden, kann zwar auf die Datei zugegriffen werden (lesen, übersetzen …), aber es ist keine Veränderung mehr möglich. Der alleinige co-Befehl stellt die Quelldatei im Read-only-Modus zur Verfügung. Somit können andere Programmierer zwar weiterhin auf die Datei zugreifen, aber nur eben im Read-only-Modus. Natürlich ist es auch nicht möglich, eine bereits gesperrte Datei mit co -l auszuchecken.

Jetzt ist es an der Zeit, die Datei datei.c zu verändern, um eine neue Versionsnummer zu erhalten.

/* $Id: datei.c,v 1.1 2003/11/08 13:19:59 Jürgen Exp Jürgen $
 * datei.c
 * Beispielcode zur Demonstration von RCS
 * von Jürgen Wolf
 */
#include <stdio.h>
int main(void) {
   const char *str = "Mein erstes Projekt mit RCS\n";
   printf(»%s«,str);
   return 0;
}

Die Veränderung wurde hier fett hervorgehoben. Nehmen Sie diese ebenfalls an Ihrem Text vor. Jetzt wird die Datei erneut eingecheckt.

$ ci –l -u datei.c
RCS/datei.c,v  <-- datei.c
enter description, terminate with single '.' or end of file
new revision: 1.2; previous revision: 1.1
enter log message, terminated with single '.' or end of file:
>> Der Ausgabestring wurde mit einer Stringkonstante umgeändert
>> .
done

Nach erneutem Einchecken hat die Datei nun die Revisionsnummer 1.2. Da Sie hierbei etwas verändert haben, können Sie auch bei der Beschreibung (description) etwas eintragen.

Jetzt haben Sie zwei Versionen von dem Programm datei.c. Der Benutzer, der jetzt die aktuelle Version bearbeiten will, muss diese erst extrahieren. Dies wird wieder mit einem co erledigt.

$ co datei.c
RCS/datei.c,v  -->  datei.c
revision 1.2
done

Hiermit kann der Benutzer zwar auf die aktuelle Version des Programms zugreifen, aber nur im Read-only-Modus. Es wurde ja bereits erwähnt, dass co ohne irgendwelche Angaben die Datei im Read-only-Modus auscheckt. Für die Übersetzung des Listings (z. B. zum Testen der Anwendung) und zum Lesen reicht dies aus. Will der Entwickler diese Version verändern, muss er diese mit dem Flag -l auschecken.

$ co -l datei.c
RCS/datei.c,v  -->  datei.c
revision 1.2 (locked)
done

An der Ausgabe locked können Sie erkennen, dass Sie die aktuelle Version der Datei ausgecheckt und somit für andere gesperrt haben. Somit kann auch kein anderer Programmierer etwas an der Version verändern.

Natürlich sollten Sie, wenn Sie etwas am Programm verändert haben, es jetzt mit ci auch wieder einchecken, so dass es dem anderen Programmierer auch wieder zur Verfügung steht. Der Quelltext soll also nochmals geringfügig verändert werden.

/* $Id: datei.c,v 1.2 2003/11/10 06:47:25 tot Exp $
 * datei.c
 * Beispielcode zur Demonstration von RCS
 * von Jürgen Wolf
 */
#include <stdio.h>
int main(void) {
   char *str = "Mein erstes Projekt mit RCS";
   printf("%s\n",str);
   printf(»Eine weitere Ausgabe\n«);
   return 0;
}

Checken Sie diese Datei wieder wie gewohnt ein, so haben Sie soeben die Revision 1.3 erstellt. Was ist aber jetzt, wenn Sie oder ein anderer Entwickler auf die Versionen 1.1 oder 1.2 zurückgreifen wollen? Nichts ist einfacher als das. Checken Sie die Datei mit dem Flag -r für die Revisionsnummer wie folgt aus:

$ co -l -r1.2 datei.c
RCS/datei.c,v  -->  datei.c
revision 1.2 (locked)
done

Schon befindet sich die Version 1.2 in Ihrem Arbeitsverzeichnis zum Verändern. Wenn Sie jetzt die Version 1.2 verändern und wieder einchecken wollen, was passiert dann? Denn schließlich existiert ja bereits (im Beispiel) eine Version 1.3. Ganz einfach, die neue Versionsnummer geht der Version 1.2 voraus und bekommt somit die Nummer 1.2.1.1.

$ ci -l -u  datei.c
RCS/datei.c,v  <--  datei.c
new revision: 1.2.1.1; previous revision: 1.2
enter log message, terminated with single '.' or end of file:
>> .
done

Und wie sieht das jetzt mit der ersten Ziffer der Revisionsnummer aus? Wie kann hier aus der Version 1.x die Version 2.x gemacht werden? Auch hierbei bedient man sich der Option -r. Im folgenden Beispiel checken Sie die Datei datei.c mit Versionsnummer 1.3 ein, so dass hieraus die Version 2.1 wird. Zuerst müssen Sie natürlich die aktuellste Version auschecken:

$ co -l datei.c
RCS/datei.c,v  -->  datei.c
revision 1.3 (locked)
done

Natürlich sollte an der Version 1.3 jetzt auch etwas verändert werden, denn eine unveränderte Version checkt RCS nicht so ohne weiteres ein. Also ändern Sie etwas in der Datei der Version 1.3, und checken Sie diese dann folgendermaßen wieder ein:

$ ci -r2.1 datei.c
RCS/datei.c,v  <--  datei.c
new revision: 2.1; previous revision: 1.3
enter log message, terminated with single '.' or end of file:
>> Gravierende Veränderungen daher aus 1.x 2.x gemacht
>> .
done

Wollen Sie jetzt im Fall der Fälle einer Datei eine neue Versionsnummer verpassen, obwohl diese nicht verändert wurde, können Sie dies mit der Option -f (force) erzwingen.

$ ci -f -r2.1.1 datei.c
RCS/datei.c,v  <--  datei.c
new revision: 2.1.1.1; previous revision: 2.1
enter log message, terminated with single '.' or end of file:
>> .
done

Dieses Erzwingen ist sinnvoll, wenn Sie zwei Dateiversionen anbieten wollen.

Versionsprotokoll – rlog

Nach (fast) jedem erneuten Einchecken geben Sie im Prompt (>>) häufig einen Kommentar oder eine Anmerkung der Veränderung ein. Da sich mit der Zeit eine Menge Versionen ergeben, kann man sich hier gelegentlich einen Überblick zu den verschiedenen Versionen verschaffen. Dieses Protokoll können Sie sich mit dem Befehl rlog ansehen:

$ rlog datei.c
RCS file: RCS/datei.c,v
Working file: datei.c
head: 2.2
branch:
locks: strict
        tot: 2.2
access list:
symbolic names:
        beta: 2.2
keyword substitution: kv
total revisions: 8;     selected revisions: 8
description:
----------------------------
revision 2.2    locked by: tot;
date: 2003/11/11 05:48:46; author: tot; state: Exp;  lines: +2 -1
Eine ungetestete Version
----------------------------
revision 2.1
date: 2003/11/10 12:23:31; author: tot; state: Exp;  lines: +2 -1
branches:  2.1.1;
Gravierende Veränderungen daher aus 1.x 2.x gemacht
----------------------------
revision 1.4
date: 2003/11/10 12:10:09; author: tot; state: Exp;  lines: +2 -1
Noch ein Textstring wurde hinzugefügt
...

Die Ausgabe wurde hierbei erheblich gekürzt. In diesem Protokoll finden Sie alles, was Sie benötigen, wie die Versionsnummer, das Datum der Versionen und die Zeit des Eincheckens, den Autor, der die Datei verändert hat, Änderungen von hinzugefügten und gelöschten Zeilen und wer alles auf die Datei zugreifen darf. Ebenfalls finden Sie hier, ob jemand aus einer Version eine Nebenversion (branch) gemacht hat, und natürlich die Beschreibung der Veränderungen, die Sie (hoffentlich) regelmäßig eingegeben haben.

Zugriffsliste

Wollen Sie, dass nur autorisierte Personen auf die Datei zugreifen dürfen, können Sie eine Zugriffsliste erstellen. Diese Liste bezieht sich dann auf den ganzen Dateibaum. Dazu müssen Sie das Kommando rcs mit dem Flag -a verwenden. Hinter -a schreiben Sie alle Benutzernamen, getrennt durch ein Komma, die Zugriff auf eine entsprechende Datei erhalten sollen.

$ rcs -atot,mars,pluto,erde datei.c

Somit haben nur noch die Benutzer(-Namen) tot, erde, mars und pluto Zugriff auf die Datei datei.c. Wenn Sie rlog auf diese Datei aufrufen, finden Sie entsprechende Namen in der access list eingetragen:

access list:
        tot
        erde
        pluto
        mars

Wollen Sie einen Benutzer aus dieser Liste herausstreichen, so machen Sie das mit dem Kommando rcs und dem Flag -e.

rcs -epluto,erde datei.c

Hiermit würden die Benutzer pluto und erde aus der Zugriffsliste gestrichen. Ein Blick in rlog bestätigt Ihnen das.

Ist die Zugriffsliste leer, dann darf jeder auf die Datei zugreifen. Der Eigentümer und der Superuser dürfen ebenfalls immer auf die Datei zugreifen und können sich somit niemals selbst aussperren. Natürlich gilt auch hier, ist die Datei ausgecheckt und mit dem Flag -l gesperrt, müssen alle anderen Teilnehmer warten, bis die Datei wieder eingecheckt wurde, sofern die Teilnehmer etwas verändern wollen. Denn die Read-only-Version steht ja zur Verfügung.

Versionen miteinander vergleichen – rcsdiff

Benötigen Sie einen Vergleich zwischen der aktuellen Version und irgendeiner vorherigen, können Sie zum Vergleich das Kommando rcsdiff verwenden. rcsdiff ist sehr hilfreich, wenn man die Veränderungen zwischen verschiedenen Versionen vergleichen muss; gerade wenn mehrere Entwickler an der Datei arbeiten und Sie nicht genau wissen, was an dieser alles verändert wurde. Ein Beispiel:

$ rcsdiff -r1.2 -r2.1 datei.c
=================================================================
RCS file: RCS/datei.c,v
retrieving revision 1.2
retrieving revision 2.1
diff -r1.2 -r2.1
1c1
< /* $Id: datei.c,v 1.2 2003/11/10 06:37:27 tot Exp $
---
> /* $Id: datei.c,v 2.1 2003/11/11 12:23:31 tot Exp $
10,11c10,13
<    char *str = "Mein erstes Projekt mit RCS\n";
<    printf("%s",str);
---
>    char *str = "Mein erstes Projekt mit RCS";
>    printf("%s\n",str);
>    printf("Eine weitere Ausgabe\n");
>    printf("\n\n");
13c15

Hier wurde ein Vergleich zwischen der Version 1.2 und der Version 2.1 von der Datei datei.c aufgelistet.

RCS-Schlüsselwörter

RCS-Schlüsselwörter sind makroähnliche Erkennungen, die Informationen in der Datei oder auch in der ausführbaren binären Datei speichern. Alle Schlüsselwörter werden dabei zwischen zwei Dollarzeichen gestellt ($Schlüsselwort$). RCS expandiert dann $Schlüsselwort$ durch $Schlüsselwort: Wert$. Ein Beispiel haben Sie ja auf den vergangenen Seiten mit dem Schlüsselwort $Id$ gesehen. Wo Sie im Quellcode $Id$ angegeben haben, hat RCS nach dem Einchecken Folgendes daraus gemacht:

$Id: datei.c,v 1.2.1.1 2003/11/10 12:14:06 tot Exp tot $

Somit lautet das Format für den $Id$-String folgendermaßen:

$Schlüsselwort: Dateinamen, Revisionsnummer, Datum, Uhrzeit, 
Autor, Status, Locker$

Weitere mögliche Schlüsselwörter, mit denen Sie experimentieren können, sind:

gp  $Header$ – Standardheader: Pfad- und Filename des RCS-Files, Versionsnummer, Datum, Autor, Status und Urheber einer aktuellen Sperre.
gp  $Log$ – Der beim Einchecken angegebene Kommentar, davor ein Header mit Filename des RCS-Files, Versionsnummer, Datum, Autor. Das $Log$-Schlüsselwort wird beim Auschecken nicht ersetzt, sondern um den letzten Kommentar erweitert.
gp  $Author$ – Name des Nutzers, der die Version eincheckte
gp  $Date$ – Datum und Zeit des Eincheckens
gp  $Locker$ – Name des Nutzers, der eine Sperre auf die Version eingetragen hat
gp  $Revision$ – Versionsnummer
gp  $State$ – Statusattribut

Hinweis   Mit dem Programm ident können Sie aus jeder Datei egal welchen Typs diese Schlüsselwörter extrahieren und anzeigen lassen.



Rheinwerk Computing

17.4.3 CVtoptop

Nachdem mit RCS die Pionierarbeiten für SCM-Systeme erledigt wurden, kann ich mir die eine oder andere Erklärung sparen, da Sie jetzt bereits mit den Grundlagen solcher Systeme vertraut sind.

Es wurde bereits erwähnt, dass der Hauptnachteil von RCS die fehlende Projektunterstützung ist. Wenn man also Dateien eines ganzen Verzeichnisses oder Verzeichnisbaums mit RCS bearbeiten will, muss man sich entweder kleine Skripte basteln oder die Erweiterung von RCS, nämlich CVS, verwenden.

CVS (Kurzform für Concurrent Versions System) ist mittlerweile das am häufigsten eingesetzte Open-Source-Tool zur Versionskontrolle und steht unter der GPL. Außerdem gibt es für Kommandozeilen-Muffel inzwischen mehrere GUIs für Windows, Mac und Linux/UNIX.

Das System von CVS besteht aus zwei Teilen: der serverseitigen Software, die das Archiv verwaltet, und einer clientseitigen Software, womit der Entwickler mit den Dateien im Archiv arbeiten kann.

CVS benötigt außerdem keine Sperren (Locking). (Es benutzt aber Locks während des Ein-/Auscheckens, um Race Conditions zu vermeiden.) Anwender von CVS können gleichzeitig Änderungen an denselben Quelldateien vornehmen.

Neben den Hauptanwendungen wie der Softwareentwicklung im Team eignet sich CVS auch für die Verwaltung und Wartung von Websites und ebenso zur Administration des Systems (Wartung mehrerer Systeme über ein gemeinsames Archiv).


Hinweis   CVS ist ein gewaltiges Werkzeug, worüber man gut und gerne ein paar hundert Seiten verfassen könnte. Über Sinn und Unsinn eines solch umfangreichen Textes kann man diskutieren, aber für weitere Informationen sei immer die Manual oder info-Page von CVS empfohlen.


Kernstück von CVS ist die Repository (dt. Behälter oder dt.-frz. Repertoire), worin die Quelldateien liegen. Diese Repository wird allerdings nicht direkt bearbeitet. Man legt stattdessen für jeden an einem Projekt beteiligten Entwickler ein eigenes Arbeitsverzeichnis an. Darin kann dieser nun beliebig die Dateien verändern, neue hinzufügen oder einzelne Dateien löschen. Jede Veränderung betrifft vorerst immer nur die lokale Kopie des einzelnen Entwicklers in seinem Arbeitsverzeichnis.

Will der Entwickler jetzt seine neu überarbeitete Version in die Repository einspielen, muss diese Version eingecheckt werden. Dazu macht der Entwickler zuvor erst ein Update seiner eigenen Datei(en). Dabei werden nur die Quelldateien übernommen, deren Datum jünger als die eigenen sind, um Widersprüche zu vermeiden. Denn es könnten ja in der Zeit, wo Sie die Datei verändert haben, weitere Programmierer ebenso an der gleichen Datei gearbeitet haben. War das Update erfolgreich, wird die Veränderung in die Repository übertragen. Das nächste Auschecken bzw. Update erfolgt jetzt bei allen anderen Entwicklern aus der eben aktualisierten Version.

Einrichten eines Archivs (Repository)

Damit Sie den anschließenden Erklärungen auch folgen bzw. dies in der Praxis nachvollziehen können, sei Folgendes gegeben: In Ihrem Arbeitsverzeichnis erstellen Sie ein Verzeichnis namens my_project und darin die Verzeichnisse quellcodes und repository. Im Verzeichnis quellcodes befinden sich folgende Dateien (deren Inhalt hier nicht von Interesse sein soll):

$ cd my_project/quellcodes
$ ls
extra.h  funktionen.c  main.c

Das Verzeichnis repository ist noch leer. Somit liegt folgende Hierarchie im Arbeitsverzeichnis vor:

my_project
  |
  |-quellcodes
  |   |
  |   |-extra.h
  |   |-funktionen.c
  |   |-main.c 
  |
  |-resository

Wenn Sie diese Gegebenheiten erstellt bzw. erzeugt haben, können Sie die folgenden Beispiele selbst in der Praxis ausführen – was sehr zu empfehlen ist. Das komplette Beispiel wird auf dem lokalen Rechner ausgeführt. In der Praxis wird dies natürlich eher selten der Fall sein, was bedeutet, dass der ganze Vorgang meistens über ein Netzwerk (z. B. Internet) vonstatten geht.

Bevor Sie ein Archiv verwenden können, müssen Sie dieses logischerweise erst erstellen. Damit Sie im Verlaufe dieses Kapitels nicht immer mittels

$ cvs -d /pfad/zum/cvs Kommando 

auf das Archiv zugreifen müssen, soll erst die Umgebungsvariable CVSROOT neu gesetzt werden. Damit wird so manches einfacher. Dies erledigen Sie mit dem Kommando:

$ setenv CVSROOT /pfad/zum/Archiv

oder

$ export CVSROOT=/pfad/zum/Archiv

Hinweis   Für eine dauerhafte Einstellung müssen Sie einen Eintrag in z. B. ~/.bashrc vornehmen.


In diesem Fall befindet sich das Projekt im Arbeitsverzeichnis. Somit setzen Sie die Umgebungsvariable mittels

$ export CVSROOT=$HOME/my_project/repository
$ echo $CVSROOT
/home/tot/my_project/repository

Eine neues Projekt einrichten (Repository initialisieren)

Im nächsten Schritt müssen Sie festlegen, welches Archiv verwaltet werden soll. Dazu muss lediglich die Repository initialisiert werden. Wechseln Sie also in das Verzeichnis, in dem sich das Projekt befindet, und initialisieren Sie die Repository wie folgt:

$ cd my_project/repository
$ cvs init
$ ls
CVSROOT 

Jetzt befindet sich in dem Verzeichnis (repository) ein neues Verzeichnis mit dem Namen CVSROOT. Darin befinden sich u. a. viele verschiedene Hilfsdateien für CVS. Bisher haben Sie allerdings nur eine neue Repository initialisiert – somit müssen Sie im nächsten Schritt die einzelnen Dateien des Projektes unter die Kontrolle von CVS bringen – besser importieren. Dazu müssen Sie wieder im Hauptverzeichnis des Projektes sein, worin sich die Quelldateien befinden. Dank der gesetzten Umgebungsvariablen CVSROOT findet CVS sein Ziel von selbst.

$ cd $HOME/my_project/quellcodes
$ ls
extra.h  funktionen.c  main.c
$ cvs import -m 'Bemerkung' mytest_projekt Wolf alpha
N mytest_projekt/extra.h
N mytest_projekt/main.c
N mytest_projekt/funktionen.c
No conflicts created by this import

Durch import wird in repository ein Verzeichnis mit dem Namen mytest_projekt erzeugt. Darin befinden sich jetzt sämtliche Archivdateien mit der Endung ,v, die mit zusätzlichen Informationen erweitert wurden. Hätten Sie beim import das Flag -m nicht verwendet, hätte sich für den dazugehörenden Kommentar einen Editor (der in der Umgebungsvariablen $EDITOR spezifiziert ist) geöffnet, worin Sie einen entsprechenden Kommentar (hier nur Bemerkung) hätten schreiben können. Die beiden Angaben Wolf und alpha sind so genannte Vendor- und Release-Tags, die zwar vorgeschrieben sind, aber in diesem Fall noch nicht näher betrachtet werden.

Auschecken einer Arbeitskopie

Jetzt können Sie (endlich) mit der Arbeit an Ihrem Projekt beginnen. Dazu müssen Sie die Quelldateien aus dem CVS-Archiv in einen Arbeitsbereich kopieren. Dies wird mit dem Befehl checkout (oder kurz co) erledigt, womit Sie ein komplettes Verzeichnis und alle darin enthaltenen Dateien aus dem Archiv in das aktuelle Verzeichnis kopieren.

$ cd ~
$ cvs checkout mytest_projekt
cvs checkout: Updating mytest_projekt
U mytest_projekt/extra.h
U mytest_projekt/funktionen.c
U mytest_projekt/main.c

Jetzt befindet sich in Ihrem Arbeitsverzeichnis eine Arbeitskopie desselben Inhalts, den Sie zuvor mit import importiert haben – inklusive eines CVS-Verzeichnisses, in dem die CVS-Informationen für die Versionskontrolle gespeichert werden. Dieses CVS-Verzeichnis finden Sie jetzt in JEDEM Unterverzeichnis; also für jedes Verzeichnis im Projekt ein CVS-Verzeichnis. Dies bedeutet natürlich, dass sich in dem Verzeichnis oder den Verzeichnissen Ihres Projekts kein Verzeichnis mit dem Namen CVS befinden darf.

$ ls mytest_projekt
CVS  extra.h  funktionen.c  main.c

Hierzu auch gleich ein Unterschied zu RCS: Bei CVS müssen die Dateien nicht erst gesperrt werden, sondern stehen gleich zum Arbeiten zur Verfügung. Natürlich ist hier von besonderem Interesse, was sich in diesem CVS-Verzeichnis befindet. Dabei erfahren Sie auch gleich, wie CVS arbeitet.

Bestimmt fällt Ihnen eine gewisse Ähnlichkeit zu RCS auf – was ja auch selbstverständlich ist, denn (wie schon erwähnt) CVS verwendet das RCS-System. Der einzige Unterschied (auch das wurde schon erwähnt) liegt darin, dass Sie mit CVS die Fähigkeit haben, ganze Dateibäume zu verwalten. Damit dies realisierbar ist, legt CVS eben ein entsprechendes CVS-Archiv (Verzeichnis) an, das dieselben Dateien, Verzeichnisse und Unterverzeichnisse enthält wie das Projekt selbst; nur dass es sich hier um RCS-Dateien handelt.

Bei dem checkout-Befehl, den Sie eben verwendet haben, legt CVS die Verzeichnisse in derselben Struktur an wie im CVS-Archiv. Anschließend werden mit RCS die Dateien anhand der Revision konstruiert und in das Arbeitsverzeichnis kopiert. Durch das Konstruieren der Revision ist es erst möglich, dass der Programmierer auch eine ältere Version des Projektes auschecken kann.


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.5    Checkout verschiedener Versionen aus einem CVS-Archiv


Jetzt folgt ein Blick in das CVS-Verzeichnis des ausgecheckten Projekts:

$ ls mytest_projekt/CVS
Entries  Repository  Root

Die Datei Root verweist auf das Projekt:

$ cat mytest_projekt/CVS/Root
/home/tot/my_project/repository

In Entries sind die Nummern und Checkout-Zeiten des Projektes gespeichert:

$ cat mytest_projekt/CVS/Entries
/extra.h/1.1.1.1/Wed Dec 10 01:37:08 2003//
/funktionen.c/1.1.1.1/Wed Dec 10 01:37:08 2003//
/main.c/1.1.1.1/Wed Dec 10 01:37:08 2003//
D

Diese sind nötig, damit ein commit (folgt noch) richtig ausgeführt wird. Damit weiß CVS, welche Revision sich im Arbeitsbereich befindet. Die Datei Repository verweist auf ein Projekt innerhalb des Archivs, in dem sich die passenden RCS-Dateien befinden (hier auf: mytest_projekt).

$ cat mytest_projekt/CVS/Repository
mytest_projekt
$ ls repository/mytest_projekt
extra.h,v  funktionen.c,v  main.c,v

Würden Sie in Ihrem CVS-Archiv eine Quelldatei entfernen (löschen), dann wird auch hierfür von CVS ein Eintrag gemacht. Diese Dateien befinden sich dann in einem Unterverzeichnis namens Attic.

Veränderungen von Dateien

Im nächsten Schritt können Sie jetzt mit den ausgecheckten Dateien Ihres oder des Projektes arbeiten. In diesem Beispiel soll die Datei funktionen.c verändert werden.

$ cd ~/mytest_projekt
$ ls
CVS  extra.h  funktionen.c  main.c
$ vi funktionen.c

Was verändert wurde, spielt zum Verständnis keine Rolle. Ich rufe hierbei den Editor vi auf, füge eine Zeile im Quellcode dazu und speichere diese Veränderung ab.


Hinweis   Sie müssen hier nicht den Editor vi verwenden, obwohl der Arbeitsvorgang damit um einiges schneller ist. Sofern Sie vi verwenden und der Editor das Listing geladen hat, drücken Sie (i)für insert, fügen jetzt Ihre neue Zeile bzw. irgendeine Veränderung ein und drücken anschließend (ESC), um aus dem Editormodus herauszukommen. Jetzt können Sie die Datei speichern, indem Sie die (Ş)-Taste gedrückt halten und zweimal (Z) (Z) betätigen. Alternativ bietet sich auch joe oder pico an. Auf jeden Fall aber sollte $EDITOR gesetzt sein, wenn 'cvs ci' ausgeführt wird, sonst endet man hilflos im vi!


Jetzt wollen Sie noch eine Veränderung einbringen. Doch jetzt kommt es: Sie erhalten einen wichtigen Telefonanruf und müssen alles liegen und stehen lassen. Während dieser Zeit hat sich aber jemand anderes am Code zu schaffen gemacht. Jetzt gilt es natürlich, erst einmal mit der Untersuchung der Veränderung(en) fortzufahren.

Untersuchen von Veränderungen

Um herauszufinden, welche Datei(en) zuletzt geändert wurde(n), reicht der Befehl update:

$ cd ~/mytest_projekt
$ cvs update
 cvs update: Updating .
 M funktionen.c 

Die Datei funktionen.c wurde hier verändert. Dies können Sie am M, das für Modified steht, erkennen. Bei den vielen Zeilen Code wollen Sie sicherlich auch wissen, was verändert wurde. Dazu können Sie diff verwenden.

$ cvs -Q diff -c
Index: funktionen.c
=================================================================
RCS file: /home/tot/my_project/repository/mytest_projekt/funktionen.c,v
retrieving revision 1.1.1.1
diff -c -r1.1.1.1 funktionen.c
*** funktionen.c        10 Dec 2003 01:37:08 -0000      1.1.1.1
--- funktionen.c        11 Dec 2003 03:23:51 -0000
***************
*** 1,4 ****
--- 1,5 ----
  static void hallo(void)
  {
        printf("Hallo Welt\n");
+         printf("Heute ist ein schöner Tag\n");
  }

Ich denke, klarer geht es nicht mehr. Hier finden Sie mit Index den Namen der Datei, mit RCS file den Ort, wo die RCS-Datei gespeichert wurde, die Version, wann diese erstellt wurde und wann die Änderung gemacht wurde. Die Veränderung sehen Sie anhand des +-Zeichens in der Zeile (hier printf("Heute ist ein schöner Tag\n");). In welcher Zeile die Datei geändert wurde und welche Zeilen hinzugekommen sind, können Sie im folgenden Eintrag lesen:

*** 1,4 ****
(Start des Kontextes ab Zeile 1, Chunk ist 4 Zeilen lang)
--- 1,5 ---- 
(Start des Kontextes ab Zeile 1, Chunk ist 5 Zeilen lang)

Mehr dazu würde jetzt den Rahmen des Kapitels sprengen. Daher seien hierfür die Manual Pages von diff empfohlen.


Hinweis   Bei umfangreicheren Programmen sollten Sie das Flag –pdu hinter diff verwenden, da mit pdu die Ausgabe weitaus besser lesbar ist als mit c.


Natürlich können Sie diff und update auch auf einzelnen Dateien verwenden wie z. B.

$ cvs update funktionen.c

Veränderungen an das Archiv weitergeben

Nachdem Sie an Ihrem jüngsten Projekt weitergearbeitet und es verändert haben, wird es Zeit, das Projekt mitsamt seinen Änderungen ins Archiv zurückzulegen. Dazu wird der Befehl commit (oder kurz ci) verwendet. Geben Sie commit ohne einen Dateinamen an, werden alle Quelldateien des Projektes, die verändert wurden, an das Archiv gesendet.

$ cvs commit -m 'zusätzliche Textausgabe'
cvs commit: Examining .
Checking in funktionen.c;
/home/tot/my_project/repository/mytest_projekt/funktionen.c,v  <--  funktionen.c
new revision: 1.2; previous revision: 1.1
done

An der Ausführung von commit können Sie erkennen, welche Dateien verändert wurden und somit neu eingecheckt werden (hier funktionen.c). Somit hätte hier auch folgendes commit angegeben werden können:

$ cvs commit -m 'zusätzliche Textausgabe' funktionen.c

Außerdem können Sie an der Ausgabe auch erkennen, dass aus der Revisionsnummer 1.1 die Revisionsnummer 1.2 geworden ist. Jede Datei im Projekt hat übrigens eine eigene Revisionsnummer. Dabei wird die rechte Stelle der Nummer um eins inkrementiert. Unterschiedliche Revisionsnummern in einem Projekt bedeuten nur, dass eine Datei häufiger als die andere verändert (ein commit gesendet) wurde.

Mehrere Programmierer – ein Projekt

Da Sie bei CVS – im Gegensatz zu RCS – nicht mit Sperren zu kämpfen haben, kann gleich mit den Arbeiten am ausgecheckten Projekt begonnen werden. Und das bedeutet auch im Gegensatz zu RCS, dass jetzt ohne weiteres mehrere Entwickler gleichzeitig das Projekt oder Teile davon auschecken und verändern können. Zwangsläufig können hierbei Konflikte auftreten, wenn ein Entwickler nach Beendigung des Projektes keinen Abgleich (ein commit) mehr macht bzw. den Abgleich erst nach längerer Zeit macht.

Wie die Gruppe von Entwicklern dabei vorgehen muss, soll anhand eines Fallbeispiels erklärt werden.

Programmierer Anton und Programmierer Xaver greifen beide auf das CVS-Archiv eines Projekts zu und machen ein checkout.


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.6    Zwei Programmierer checken aus einem CVS-Archiv aus.


Programmierer Xaver verändert eine Datei im Projekt und teilt die Veränderung dem CVS-Archiv mittels einem commit mit.


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.7    Ein Programmierer bringt eine Veränderung ins Archiv ein.


Jetzt kommt Programmierer John und holt sich ebenfalls mit einem checkout das Projekt auf seine Platte.


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.8    John holt sich die neuste Version aus dem Archiv.


Programmierer Anton hat jetzt auch eine Datei verändert und will dies mit commit dem Archiv mitteilen. Das commit schlägt fehl, da ja Xaver bereits eine Veränderung eingebracht hat, und somit stimmt die Revisionsnummer von Anton nicht mehr mit dem Archiv überein. Um diesen Umstand zu beheben, muss Anton ein Update seines Arbeitsbereiches machen. Dies erledigt dieser mit dem update-Befehl.

$ cvs update

Mittels update werden bei Anton alle Daten aktualisiert, die seit seinem letzten checkout verändert wurden.


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.9    Arbeitsbereich mittels update auf den neuesten Stand bringen


Jetzt muss Anton natürlich überprüfen, ob seine Veränderung mit der von Xaver übereinstimmt – sprich, keine Konflikte entstehen. Hat Xaver z. B. eine globale Funktion verworfen, die Anton in seiner Veränderung verwendet, muss Anton dies beachten. Es empfiehlt sich im Grunde, immer vor dem commit ein update zu machen. Wenn es keine Probleme bei den Veränderungen gab, kann Anton seine Veränderung ebenfalls dem Archiv mitteilen (commit).


Abbildung
Hier klicken, um das Bild zu vergrößern

Abbildung 17.10    Wenn es keine Konflikte gibt, erfolgt ein weiteres commit.


Wenn Xaver jetzt ebenfalls wieder das Projekt auf seiner Platte aktualisieren will, reicht auch hier ein einfaches update.

Derselbe Vorgang wie eben beschrieben läuft auch ab, wenn zwei Entwickler dieselbe Datei verändern. Allerdings muss auch hierbei beachtet werden, dass sich die Veränderungen vertragen.

Jetzt haben wir ja noch John, der dem Archiv mitteilen will, dass er eine Veränderung eingebracht hat. Währenddessen macht Anton eine status-Abfrage, die wie folgt aussieht:

$ cvs status funktionen.c
File: funktionen.c Status: Locally Modified
Working revision: 1.2 Mon Mar 2 06:35:27 2004
Repository revision: 1.2 /home/anton/my_projects/repository/funktionen.c,v
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none) 

Mittlerweile hat ein weiterer Programmierer namens Leon die Datei funktionen.c bearbeitet und dies bereits dem Archiv mitgeteilt. Somit wäre die aktuelle Revisionsnummer 1.3. Eine status-Abfrage von John auf seinem lokalen Rechner würde jetzt folgendermaßen aussehen:

$ cvs status funktionen.c
File: funktionen.c Status: Needs Patch
Working revision: 1.2 Mon Mar 2 06:35:27 2004
Repository revision: 1.3 /home/john/my_projects/repository/funktionen.c,v
Sticky Tag: (none)
Sticky Date: (none)
Sticky Options: (none) 

Damit wüsste John, dass jemand bei der Datei funktionen.c ein commit gemacht hat, die Revisionsnummer im Archiv 1.3 und seine Revisionsnummer 1.2 ist und er somit hinterherhinkt. An der Statuszeile könnte John auch erkennen, dass er einen Patch machen soll. Dabei müsste er lediglich ein update machen und die Änderung mittels patch einarbeiten. John hat jetzt aber weder den status-Befehl noch ein update gestartet – schlimmer noch, er hat in derselben Datei funktionen.c dieselbe Zeile wie Leon verändert. Hier liegt ein ganz klarer Konflikt vor.

Diesen Konflikt bemerkt John erst beim nächsten commit, das logischerweise erst einmal fehlschlägt. Also macht er zunächst ein update der Datei funktionen.c:

$ cvs update funktionen.c 
RCS file: /home/john/my_project/repository/funktionen.c,v
retrieving revision 1.2
retrieving revision 1.3
Merging differences between 1.2 and 1.3 into funktionen.c
rcsmerge: warning: conflicts during merge
cvs update: conflicts found in funktionen.c
C funktionen.c 

Es liegt also ein Konflikt vor. Welcher Konflikt das genau ist, kann er feststellen, indem er die Datei funktionen.c in seinem lokalen Arbeitsverzeichnis ansieht.

$ cat funktionen.c
void hallo()
{
        printf("Hallo Welt\n");
<<<<<< funktionen.c
        printf ("Heute ist ein schöner Tag\n");
=======
       printf ("Heute ist kein schöner Tag\n");
>>>>>>> 1.3
}

Der folgende Teil entspricht der Veränderung von John:

<<<<<< funktionen.c
        printf ("Heute ist ein schöner Tag\n");

Getrennt werden diese beiden Veränderungen in derselben Zeile mit:

=======

Der untere Teil entspricht der Veränderung der aktuellen Revision 1.3, wie sie im CVS-Archiv vorliegt:

       printf ("Heute ist kein schöner Tag\n");
>>>>>>> 1.3

Diesen Konflikt müssen Sie nun selbst beseitigen. Meistens handelt es sich allerdings nicht um ein solch stupides Beispiel, so dass eine Rücksprache mit dem Entwickler, der an derselben Zeile wie Sie gearbeitet hat, vonnöten ist, so dass es zu einer Übereinkunft kommen wird.

Wobei das mit der Rücksprache auch nicht so einfach zu lösen ist. Der Franzose spricht kein Chinesisch, der Chinese kein Deutsch und der Amerikaner kein Spanisch (o. k., viele sprechen Englisch – aber tun wir mal so, als könnte dies auch niemand). Doch auch dafür gibt es bei CVS einen Weg. Wollen Sie allen mitteilen, dass Sie gerade an der Datei funktionen.c arbeiten, können Sie dies mit dem edit-Befehl machen.

$ cvs edit funktionen.c

Jetzt werden alle anderen informiert, dass Sie gerade an dieser Datei arbeiten. Voraussetzung dafür ist, dass die anderen Entwickler ebenfalls den edit-Befehl verwenden. Jetzt können sich die Entwickler untereinander absprechen oder warten, bis derjenige, der an dieser Datei arbeitet, diese Veränderung(en) an das Archiv (commit) einsendet. Mit einem commit tragen Sie sich praktisch aus dem edit-Modus aus.

Besser noch ist der Befehl watch. Damit tragen Sie sich als ständiger Beobachter ein. Wenn Sie hierbei eine Datei auschecken (checkout), geschieht dies vorerst im Lesemodus. Will der Entwickler diese Datei verändern, muss dieser erst edit eingeben. Durch diese Zwangseingabe erfährt der Entwickler, ob gerade jemand an dieser Datei arbeitet.

Dass es funktioniert, beweist die Open Source Community, wo CVS fast 100 % Marktanteil hat (Tendenz fallend).

Veränderungen rückgängig machen

Es kommt zwar nicht häufig vor, aber manchmal kann es sinnvoll sein, eine Veränderung rückgängig zu machen; sei es nun aus Gründen der Unstabilität des Programms oder aus Eigeninteresse, um eine eigene Version aus einem älteren Release zu machen. Das Rückgängigmachen geschieht auch hier wieder mit dem update-Befehl und dem Flag j für join.

$ cat funktionen.c
static void hallo(void)
{
        printf("Hallo Welt\n");
        printf("Heute ist ein schöner Tag\n");
}
$ cvs update -j 1.2 -j 1.1 funktionen.c
RCS file: /home/tot/my_project/repository/mytest_projekt/funktionen.c,v
retrieving revision 1.2
retrieving revision 1.1
Merging differences between 1.2 and 1.1 into funktionen.c
$ cat funktionen.c
static void hallo(void)
{
        printf("Hallo Welt\n");
}
$ cvs update
cvs update: Updating .
M funktionen.c
$ cvs commit -m "Rückgängig zur Version 1.1" funktionen.c
Checking in funktionen.c;
/home/tot/my_project/repository/mytest_projekt/funktionen.c,v  <--  funktionen.c
new revision: 1.3; previous revision: 1.2
done

Somit hätten Sie ohne großen Aufwand hier im Beispiel die Version 1.1 von der Datei funktionen.c wiederhergestellt, die allerdings trotzdem eine neue Versionsnummer erhält!

Dateien und Verzeichnisse hinzufügen, entfernen oder umbenennen

Jetzt fehlen Ihnen eigentlich nur noch zwei wichtige Kommandos zu CVS; das wären add und remove (oder kurz rm) zum Hinzufügen bzw. Entfernen von Dateien oder Verzeichnissen. Zuerst soll in unserem aktuellen Projekt ein neues Verzeichnis erstellt werden.

$ mkdir include
$ cvs add include
Directory /home/tot/my_project/repository/mytest_projekt/include added to the repository
$ cvs update
cvs update: Updating .
cvs update: Updating include

Bei einem Blick in das neue Verzeichnis finden Sie auch hier ein extra dafür angelegtes CVS-Verzeichnis für das Archiv. Das wurde aber bereits zu Beginn erwähnt. Ein commit entfällt hierbei.

In de include-Verzeichnis wollen Sie nun eine neue Headerdatei anlegen, vorausgesetzt, es existiert eine Headerdatei in diesem Verzeichnis. Hier machen Sie es sich mit touch leicht.

$ touch include/header.h
$ cvs add include/header.h
cvs add: scheduling file `incl/header.h' for addition
cvs add: use 'cvs commit' to add this file permanently
$ cvs commit -m "neue Headerdatei"
cvs commit: Examining .
cvs commit: Examining incl
RCS file: /home/tot/my_project/repository/mytest_projekt/incl/header.h,v
done
Checking in incl/header.h;
/home/tot/my_project/repository/mytest_projekt/incl/header.h,v  <--  header.h
initial revision: 1.1
done

Jetzt können Sie mit dem neu erstellten Verzeichnis und auch mit der neu erstellten Datei – wie mit jedem anderen Verzeichnis bzw. jeder Datei – mit CVS arbeiten. Verändern Sie jetzt z. B. die Headerdatei header.h, gehen Sie auch hier gewöhnlich wie im Beispiel folgt vor:

$ cvs update
cvs update: Updating .
cvs update: Updating include
M include/header.h
$ cvs commit -m 'Headerdatei verändert'
include/header.hChecking in
include/header.h;/home/tot/my_project/repository/mytest_projekt/include/header.h,v
 <--  header.h new revision: 1.2; previous revision: 1.1
done

Ebenso einfach ist es auch, eine Datei wieder aus dem CVS-Archiv zu entfernen. Hierbei sind folgende Schritte nötig:

$ cd include
$ rm header.h
$ cvs remove header.h
cvs remove: scheduling `header.h' for removal
cvs remove: use 'cvs commit' to remove this file permanently
$ cvs commit -m 'Headerdatei entfernt'
cvs commit: Examining .
Removing header.h;
/home/tot/my_project/repository/mytest_projekt/include/header.h,v  <--  header.h
new revision: delete; previous revision: 1.2
done

Natürlich muss die Datei, bevor diese aus dem CVS-Archiv entfernt wird, erst aus der Arbeitskopie mittels rm gelöscht werden.


Hinweis   Eine Datei, die versehentlich aus der Arbeitskopie gelöscht wurde, aber noch im CVS-Archiv existiert, kann mit 'cvs update' wiedergeholt werden (es wird eine Nachricht »xxx.c got lost« angezeigt).


Ebenso einfach ist das Umbenennen von Dateien bzw. Verzeichnissen im CVS-Archiv. Im Beispiel wird die Datei leer.c in voll.c umbenannt:

$ mv leer.c voll.c
$ cvs remove leer.c
cvs remove: scheduling `leer.c' for removal
cvs remove: use 'cvs commit' to remove this file permanently
$ cvs add voll.c
cvs add: scheduling file `voll.c' for addition
cvs add: use 'cvs commit' to add this file permanently
$ cvs commit -m 'Name leer.c umbenannt in voll.c'
cvs commit: Examining .
cvs commit: Examining include
Removing leer.c;
/home/tot/my_project/repository/mytest_projekt/leer.c,v  <--  leer.c
new revision: delete; previous revision: 1.3
done
RCS file: /home/tot/my_project/repository/mytest_projekt/voll.c,v
done
Checking in voll.c;
/home/tot/my_project/repository/mytest_projekt/voll.c,v  <--  voll.c
initial revision: 1.1
done

Sie müssen lediglich die alte Datei in der Arbeitskopie umbenennen (hier mit mv) und diese aus dem CVS-Archiv entfernen (cvs remove), um anschließend die neue Datei hinzufügen (cvs add) zu können. Am Ende führen Sie noch ein commit aus, und Sie haben einen neuen Dateinamen.

Bei Verzeichnissen können Sie ähnlich vorgehen. Erstellen Sie zuerst ein neues Verzeichnis, fügen Sie dies mit cvs add zum Vormerken für das Archiv dazu. Kopieren Sie jetzt den Inhalt des alten Verzeichnisnamens in das neue Verzeichnis. Jetzt müssen Sie den Inhalt des alten Verzeichnisses löschen, ehe Sie das alte Verzeichnis komplett löschen können. Teilen Sie jetzt noch mit cvs add die neuen Dateien im neuen Verzeichnis dem CVS mit und führen zu guter Letzt ein commit aus.

Zugriff auf ein Remote-Verzeichnis

Natürlich trifft der Anwendungsfall, wie im Beispiel, in dem sich das Arbeitsverzeichnis auf einem lokalen Rechner befindet, recht selten zu. Gerade in der freien Software (KDE, GNOME, Mozilla usw.) befindet sich die so genannte Repository irgendwo auf einem entfernten CVS-Server. Hier hat man mehrere Zugriffsmöglichkeiten. Entweder verfügt der User über einen eigenen Login auf dem Remote-Rechner via rsh oder ssh. Hierfür muss allerdings die CVS_RSH-Umgebungsvariable angepasst werden. Oder es besteht die Möglichkeit, eine von CVS portierte rsh-Implementierung zu verwenden. Hierbei muss die bereits bekannte Umgebungsvariable CVSROOT angepasst werden.

Sind alle Angaben angepasst worden, erfolgt ein Auschecken der Dateien ähnlich wie auf dem lokalen Rechner. Es muss hierbei lediglich ein etwas längerer Pfad zum Archiv angegeben werden. Zuerst erfolgt die Zugriffsmethode, welche von beiden Seiten mit einem Doppelpunkt abgetrennt wird. Anschließend folgen der Benutzername und der Servername, die mit einem @-Zeichen zusammengesetzt werden – gefolgt von einem weiteren Doppelpunkt als Trenner. Am Ende folgt dann der Pfad des Archivverzeichnisses auf dem Server. Als Beispiel folgt hierzu eine durch Passwort authentisierte Server-Zugriffsmethode (kurz pserver):

$ cvs -d :pserver:wolf@cvs.pronix.de:/usr/local/cvsroot login 
(Logging in to wolf@cvs.pronix.de)
CVS password: ********

Für mehr Details (in der Praxis) bezüglich des Zugriffs auf ein Remote-Verzeichnis seien die Dokumentation zu CVS oder auch ein Blick auf die Buch-CD empfohlen.

Grafische Frontends

Wer die Open-Source-Entwicklergemeinde kennt, weiß, dass es immer das eine oder andere grafische Frontend für ein Konsolen-Tool gibt. Ich möchte hier gar nicht auf die einzelnen Frontends eingehen, sondern nur auf die einzelnen Tools hinweisen. Zwei interessante Frontends stehen Ihnen mit jCVS – ein Java-basierter Client – und tkCVS – ein Tk-basiertes grafisches CVS-Interface – zur Verfügung. Als Web-Frontend wäre cvsweb, ein in Perl geschriebener CVS-Client, sehr zu empfehlen, zu dem es auch zusätzlich einige nützliche kleine Skripte gibt. Geht es Ihnen allerdings darum, die ganze Funktionsvielfalt von CVS auszunutzen, dann werden Sie mit dem Java-basierten Client jCVS am besten beraten sein (abgesehen von der Kommandozeile).

 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern