|
Sobald die CVS-Programme auf Ihrem System installiert sind, können Sie
sie direkt als Client benutzen, um auf entfernte Archive zuzugreifen,
indem Sie die in Kapitel 2 beschriebene Vorgehensweise anwenden. Wenn
Sie jedoch Revisionen auf Ihrer Maschine zur Verfügung stellen wollen,
müssen Sie dort ein Archiv erzeugen.
Der Befehl dafür ist:
user@linux ~$
cvs -d /usr/local/newrepos init
|
wobei /usr/local/newrepos ein Pfad zu der (beliebigen) Stelle ist, an
der Sie das Archiv haben möchten. (Natürlich müssen Sie dafür
Schreibberechtigung haben, was implizieren kann, dass Sie das
Programm als Benutzer root ausführen müssen.) Es mag ein wenig gegen
die Intuition sein, dass der Ort des neuen Archivs vor dem init
Unterkommando anstatt danach angegeben wird, doch durch die Verwendung
der -d-Option wird die Konsistenz mit den anderen CVS-Kommandos
gewährleistet.
Der Befehl gibt nach seiner Ausführung keine Meldung zurück. Lassen
Sie uns das neue Archiv untersuchen:
user@linux ~$
ls -ld /usr/local/newrepos
drwxrwxr-x 3 root root 1024 Jun 19 17:59 /usr/local/newrepos/
user@linux ~$
cd /usr/local/newrepos
user@linux /usrlocal/newrepos$
ls
CVSROOT
user@linux /usrlocal/newrepos$
cd CVSROOT
user@linux /usrlocal/newrepos/CVSROOT$
ls
checkoutlist config,v history notify taginfo,v checkoutlist,v cvswrappers loginfo notify,v verifymsg commitinfo cvswrappers,v loginfo,v rcsinfo verifymsg,v commitinfo,v editinfo modules rcsinfo,v config editinfo,v modules,v taginfo
user@linux /usrlocal/newrepos/CVSROOT$
|
Das einzige Unterverzeichnis im neuen Archiv - CVSROOT/ - enthält
verschiedene administrative Dateien, die das Verhalten von CVS
kontrollieren. Später werden wir diese Dateien einzeln untersuchen;
momentan haben wir einfach das Ziel, das Archiv ans Laufen zu
bekommen. Hierbei bedeutet "laufen", dass Benutzer Projekte
importieren, auschecken, aktualisieren und mit commit in das Archiv
übertragen können.
Bemerkung
Verwechseln Sie die Umgebungsvariable CVSROOT, die in Kapitel 2
eingeführt wurde, nicht mit diesem Unterverzeichnis CVSROOT im Archiv.
Sie haben nichts miteinander zu tun - es ist ein unglücklicher
Zufall, dass beide den gleichen Namen tragen. Ersteres ist eine
Methode, damit Benutzer nicht immer -d <Archiv-Ort> bei der Benutzung
von CVS tippen müssen; Letzteres ist das administrative
Unterverzeichnis eines Archivs.
|
Sobald das Archiv erzeugt wurde, müssen Sie sich um seine
Zugriffsrechte kümmern. CVS schreibt kein bestimmtes, standardisiertes
Zugriffsrechts- oder Dateibesitzerschema vor; es braucht nur
Schreibzugriff auf das Archiv. Ich empfehle jedoch sehr - zum Teil aus
Sicherheitsgründen, doch hauptsächlich zu Ihrem eigenen Wohl als
Administrator - die folgenden Schritte durchzuführen:
Erzeugen Sie eine Unix-Gruppe "cvs". Alle Benutzer mit Zugriff auf das
Archiv sollten in dieser Gruppe sein. Hier ist z.B. die relevante Zeile
der /etc/group-Datei meiner Maschine:
|
/etc/group
|
cvs:*:105:kfogel,sussmann,jimb,noel,lefty,fitz,craig,anonymous,jrandom
|
Passen Sie die Gruppenzugehörigkeit und Zugriffsrechte auf diese neue
Gruppe an:
user@linux ~$
cd /usr/local/newrepos
user@linux /usr/local/newrepos$
chgrp -R cvs .
user@linux /usr/local/newrepos$
chmod ug+rwx . CVSROOT
|
Jetzt können alle Benutzer, die in der Gruppe gelistet sind, ein
Projekt beginnen, indem sie cvs import wie in Kapitel 2 beschrieben
aufrufen. Checkout, update und commit sollten genauso funktionieren.
Sie können das Archiv auch von entfernten Orten über die :ext:-Methode
erreichen, vorausgesetzt, sie haben rsh- oder ssh-Zugriff auf die
Archivmaschine. (Sie haben vielleicht bemerkt, dass die chgrp- und
chmod-Kommandos in diesem Beispiel einem Benutzer "anonymous"
Schreibzugriff gegeben haben, was Sie wahrscheinlich nicht erwartet
haben. Der Grund dafür ist, dass auch anonyme Benutzer, die nur
lesend auf das Archiv zugreifen, Schreibzugriff auf Systemebene
benötigen, damit ihre CVS-Prozesse temporäre Lock-Dateien innerhalb
des Archivs anlegen können. CVS erzwingt die "Nur-lesen" Restriktion
des anonymen Zugriffs nicht durch Unix-Dateizugriffsrechte, sondern
mit anderen Mitteln, die wir in Kürze behandeln werden.)
Wenn Ihr Archiv Projekte für die Öffentlichkeit zur Verfügung stellen
soll, wobei Mitarbeiter nicht notwendigerweise Benutzerkennungen auf
der Archivmaschine haben, sollten Sie nun den
Passwortauthentisierungs-Server einrichten. Er ist für anonymen
Nur-lese-Zugriff notwendig, und es ist wahrscheinlich der einfachste
Weg, bestimmten Menschen commit-Zugriff zu gewähren, ohne ihnen
vollständige Benutzerkennungen auf der Maschine zu geben.
|
Bevor wir die Schritte durchgehen, die zum Einrichten des
Passwortservers notwendig sind, betrachten wir abstrakt, wie solche
Verbindungen funktionieren. Wenn ein entfernter CVS-Client die
:pserver:-Methode verwendet, um sich mit einem Archiv zu verbinden,
kontaktiert der Client in Wirklichkeit eine bestimmte Port-Nummer auf
der Servermaschine - um genau zu sein, Port-Nummer 2401 (was 49 zum
Quadrat ist, wenn Sie so etwas mögen). Port 2401 ist der ausgewiesene
Standard-Port für den CVS-pserver; man kann auch einen anderen Port
verwenden, solange Client und Server sich über die Port-Nummer einig
sind.
Der CVS-Server wartet nicht wirklich auf Verbindungen zu diesem Port -
der Server wird nicht gestartet, bis tatsächlich eine Verbindung
ankommt. Anstelle dessen horcht der Unix-inetd (InterNET Daemon) an
diesem Port und muss wissen, dass er den CVS-Server starten und mit
den ankommenden Client verbinden soll, sobald er eine
Verbindungsanfrage erhält.
Dies wird durch die Änderung der inetd-Konfigurationsdateien
/etc/services und /etc/inetd.conf erreicht. Die services-Datei setzt
rohe Port-Nummern in Dienstnamen um, und inetd.conf teilt inetd dann
mit, was für einen bestimmten Dienstnamen zu tun ist.
Fügen Sie zunächst die folgende Zeile in /etc/services ein (nachdem
Sie überprüft haben, ob sie nicht bereits existiert):
|
/etc/services
|
cvspserver 2401/tcp
|
Dann fügen Sie Folgendes in /etc/inetd.conf ein:
|
/etc/inetd.conf
|
cvspserver stream tcp nowait root /usr/local/bin/cvs/cvs \
--allow-root=/usr/local/newrepos pserver
|
(In der Datei selbst sollte dies alles eine lange Zeile ohne den
Backslash1 sein). Wenn Ihr System tcp wrapper verwendet, möchten Sie
etwa so etwas verwenden:
|
/etc/inetd.conf
|
cvspserver stream tcp nowait root /usr/sbin/tcpd /usr/local/bin/cvs/cvs \
--allow-root=/usr/local/newrepos pserver
|
Starten Sie jetzt inetd neu, damit er die Änderungen in den
Konfigurationsdateien registriert. (Wenn Sie nicht wissen, wie man den
Daemon neu startet, dann booten Sie den Rechner einfach neu - das
wird auch funktionieren.)
Das reicht aus, um Verbindungen zu erlauben. Sie werden aber auch
spezielle CVS-Passwörter - getrennt von den normalen
Benutzerpasswörtern - einrichten wollen, damit jemand auf das Archiv
zugreifen kann, ohne die allgemeine Systemsicherheit zu
beeinträchtigen.
Die CVS Passwortdatei befindet sich im Archiv in CVSROOT/passwd. Sie
wurde nicht standardmäßig beim Aufruf von cvs init erzeugt, da CVS
nicht wissen konnte, dass Sie pserver benutzen werden. Auch wenn die
Passwortdatei erzeugt wurde, kann CVS nicht wissen, welche
Benutzernamen und Passwörter angelegt werden sollen. Also müssen Sie
sie selbst anlegen. Hier ist eine Beispiel-CVSROOT/passwd-Datei:
|
CVSROOT/passwd
|
kfogel:rKa5jzULzmhOo
anonymous:XR4EZcEs0szik
melissa:tGX1fS8sun6rY:pubcvs
|
Das Format ist so einfach, wie es aussieht. Jede Zeile sieht wie folgt aus:
|
CVSROOT/passwd
|
<BENUTZERNAME>:<VERSCHLÜSSELTES PASSWORT>:<OPTIONALER SYSTEM
BENUTZERNAME>
|
Der zusätzliche Doppelpunkt, gefolgt von einem optionalen
Systembenutzernamen, sagt CVS, dass Verbindungen, die mit BENUTZERNAME
authentisiert sind, unter der Systembenutzerkennung
SYSTEM-BENUTZERNAME laufen sollen - anders gesagt, wäre diese
CVS-Sitzung nur dazu in der Lage, Dinge im Archiv zu tun, die jemand
tun könnte, der als SYSTEM BENUTZERNAME angemeldet wäre.
Wenn kein Systembenutzername angegeben ist, muss BENUTZERNAME einem
tatsächlich existierenden Benutzernamen auf dem System entsprechen,
und die Sitzung wird mit den Zugriffsrechten dieses Benutzers
gestartet. In beiden Fällen sollte das verschlüsselte Passwort nicht
gleich dem tatsächlichen Login-Passwort des Benutzers sein. Es sollte
ein unabhängiges Passwort sein, das nur für CVS pserver-Verbindungen
verwendet wird.
Das Passwort wird mit denselben Algorithmen wie die
Standard-Unix-Systempasswörter in /etc/passwd verschlüsselt. Sie
fragen sich vielleicht an dieser Stelle, wie man an eine
verschlüsselte Version eines Passwortes kommt? Für
Unix-Systempasswörter kümmert sich passwd um die Verschlüsselung in
/etc/passwd. Unglücklicherweise gibt es kein entsprechendes cvs
passwd-Kommando. (Es wurde schon mehrere Male vorgeschlagen, doch es
ist noch niemand dazu gekommen, es zu schreiben - vielleicht werden
Sie es tun !)
Dies ist eine Unannehmlichkeit, aber nur eine kleine. Wenn alles
andere versagt, können Sie immer noch temporär ein reguläres
Systempasswort eines Benutzers mit passwd ändern, den verschlüsselten
Text von /etc/passwd nach CVSROOT/passwd kopieren und das alte
Passwort wiederherstellen.
Bemerkung
Auf einigen Systemen finden sich die verschlüsselten Passwörter in
/etc/shadow und sind nur von root lesbar.
|
Dieses Schema funktioniert, ist aber recht lästig. Es wäre viel
einfacher, ein Kommandozeilenprogramm zu haben, das ein
Klartext-Passwort als Argument erwartet und die verschlüsselte Version
ausgibt. Hier ist solch ein Werkzeug, in Perl geschrieben:
|
/usr/local/bin/cryptout.pl
|
#!/usr/bin/perl
srand (time());
my $randletter = "(int (rand (26)) + (int (rand (1) + .5) % 2 ? 65 : 97))";
my $salt = sprintf ("%c%c", eval $randletter, eval $randletter);
my $plaintext = shift;
my $crypttext = crypt ($plaintext, $salt);
print "${crypttext}\n";
|
Ich habe dieses Skript als /usr/local/bin/cryptout.pl gespeichert:
user@linux ~$
ls -l /usr/local/bin/cryptout.pl
-rwxr-xr-x 1 root root 265 Jun 14 20:41 /usr/local/bin/cryptout.pl
user@linux ~$
cryptout.pl "ein Text"
sB3A79YDX5L4s
user@linux ~$
|
Wenn ich die Ausgabe dieses Beispiels nähme und es zum Erzeugen des
folgenden Eintrags in CVSROOT/passwd verwendete
|
CVSROOT/passwd
|
jrandom:sB3A79YDX5L4s:craig
|
dann könnte sich jemand mit dem folgenden Kommando mit dem Archiv verbinden:
user@linux ~$
remote$ cvs -d :pserver:jrandom@floss.red-bean.com:/usr/local/newrepos login
|
Sie können dann "ein Text" als ihr Passwort tippen und sind danach in
der Lage, CVS-Kommandos mit den selben Rechten wie der Systembenutzer
"craig" auszuführen.
Wenn jemand versucht, sich mit einem Benutzernamen und Passwort zu
authentisieren, die nicht in CVSROOT/passwd enthalten sind, prüft CVS,
ob der Benutzername und das Passwort in /etc/passwd vorhanden sind.
Wenn sie es sind (und das Passwort natürlich übereinstimmt), erlaubt
CVS den Zugriff. Es verhält sich so, um die Arbeit des Administrators
zu erleichtern, damit separate CVSROOT/passwd-Einträge nicht für
normale Systembenutzer eingerichtet werden müssen. Dieses Verhalten
ist aber auch eine Sicherheitslücke, da es bedeutet, dass das Passwort
dieser Benutzer im Klartext über das Netzwerk übertragen wird, sobald
sie sich mit dem CVS-Server verbinden, was es für die Augen von
Passwort-Sniffern potenziell verwundbar macht. Ein wenig später
werden Sie sehen, wie man dieses "Rückfallverhalten" abstellt, so dass
CVS nur seine eigene passwd-Datei verwendet. Egal ob Sie es
ausschalten oder aktiv lassen, sollten Sie doch alle CVS-Benutzer,
die auch Login-Benutzerkennungen haben, dazu zwingen, verschiedene
Passwörter für die beiden Funktionen zu verwenden.
Auch wenn die passwd-Datei für den Zugriff auf das gesamte Archiv
authentisiert, können Sie sie immer noch mit nur wenig zusätzlichem
Aufwand für die Vergabe von projekt-spezifischen Zugriffsrechten
verwenden. Im Folgenden beschreibe ich eine mögliche Methode:
Angenommen Sie möchten einigen entfernten Benutzern Zugriff auf das
Projekt "foo" und anderen auf das Projekt "bar" gestatten und möchten
nicht, dass Entwickler eines Projektes commit-Zugriff auf das andere
haben. Sie können dies dadurch erreichen, dass Sie projektspezifische
Benutzerkennungen und Gruppen im System erzeugen und dann diese
Benutzerkennungen auf die CVSROOT/passwd-Datei abbilden:
Hier ist der relevante Auszug auf /etc/passwd:
|
/etc/passwd
|
cvs-foo:*:600:600:Public CVS Account for Project Foo:/usr/local/cvs:/bin/false
cvs-bar:*:601:601:Public CVS Account for Project Bar:/usr/local/cvs:/bin/false
|
und aus /etc/group:
cvs-foo:*:600:cvs-foo
cvs-bar:*:601:cvs-bar
|
und schließlich CVSROOT/passwd:
|
CVSROOT/passwd
|
kcunderh:rKa5jzULzmhOo:cvs-foo
jmankoff:tGX1fS8sun6rY:cvs-foo
brebard:cAXVPNZN6uFH2:cvs-foo
xwang:qp5lsf7nzRzfs:cvs-foo
dstone:JDNNF6HeX/yLw:cvs-bar
twp:glUHEM8KhcbO6:cvs-bar
ffranklin:cG6/6yXbS9BHI:cvs-bar
yyang:YoEqcCeCUq1vQ:cvs-bar
|
Einige der CVS-Benutzernamen werden auf die Systembenutzerkennung
cvs-foo und einige auf cvs-bar abgebildet. Da CVS unter der
Benutzerkennung des Systemverwalters läuft, müssen Sie nur
sicherstellen, dass die relevanten Teile des Archivs nur von den
zugehörigen Benutzern und Gruppen beschrieben werden können. Wenn Sie
nur überprüfen, dass die Benutzerkennungen sicher gemacht wurden
(kein gültiges login-Passwort, /bin/false als Shell), ist dieses
System ausreichend sicher (aber lesen Sie weiter hinten in diesem
Kapitel den Abschnitt über CVS-Zugriffsrechte!). Zudem protokolliert
CVS Änderungen und Log-Mitteilungen unter dem CVS-Benutzernamen und
nicht dem Systembenutzernamen, sodass Sie immer noch feststellen
können, wer für eine bestimmte Änderung verantwortlich ist.
|
|
Bisher haben wir nur gesehen, wie man den
Passwortauthentisierungs-Server dazu verwendet, vollen Zugriff auf das
Archiv zu gewähren (auch wenn man zugegebenermaßen diesen Zugriff
durch sorgfältig gewählte Unix-Dateizugriffsrechte beschränken kann).
Anonymen Nur-Lesezugriff zu gewähren ist ein einfacher Schritt: Sie
müssen nur eine, vielleicht zwei Dateien in CVSROOT/ anlegen. Die
Namen dieser Dateien sind "readers" und "writers"4 - die erste Datei
enthält eine Liste von Benutzernamen, die das Archiv nur lesen
dürfen, die zweite eine Liste derer, die lesen und schreiben dürfen.
Wenn Sie einen Benutzernamen in CVSROOT/readers aufführen, hat dieser
Benutzer nur Lesezugriff auf alle Projekte im Archiv. Wenn Sie einen
Benutzernamen in CVSROOT/writers auflisten, hat dieser Benutzer
Schreibzugriff, und jeder pserver-Benutzer, der nicht in writers
aufgeführt ist, hat nur lesenden Zugriff (d.h. wenn die writers-Datei
überhaupt existiert, impliziert sie nur lesenden Zugriff für alle
nicht in ihr enthaltenen Benutzer). Wenn der gleiche Benutzername in
beiden Dateien aufgeführt ist, löst CVS den Konflikt auf konservative
Weise: Der Benutzer erhält nur lesenden Zugriff.
Das Format dieser Dateien ist sehr einfach: ein Benutzer pro Zeile
(vergessen Sie nicht, einen Zeilenvorschub nach dem letzten Benutzer
zu tippen). Hier ist ein Beispiel für eine readers-Datei:
|
CVSROOT/readers
|
anonymous
splotnik
guest
jbrowse
|
Denken Sie daran, dass sich die Dateien nur auf CVS-Benutzernamen,
nicht auf Systembenutzernamen beziehen. Wenn Sie Benutzer-Aliase in
der Datei CVSROOT/passwd verwenden (indem Sie einen
Systembenutzernamen nach dem zweiten Doppelpunkt schreiben), ist der
am weitesten links stehende Benutzername derjenige, der in der
readers- oder writers-Datei anzugeben ist.
Nur um fürchterlich genau zu sein, hier eine formale Beschreibung des
Verhaltens des Servers bei der Entscheidung, ob Nur-Lese- oder
Schreib-Lese-Zugriff zu gewähren ist: Wenn eine readers-Datei
existiert und dieser Benutzer darin aufgeführt ist, erhält er
Nur-Lesezugriff. Wenn eine writers-Datei existiert und der Benutzer
in ihr nicht aufgeführt ist, erhält er ebenfalls Nur-Lesezugriff.
(Dies trifft auch zu, wenn eine readers-Datei existiert und der
Benutzer dort auch nicht aufgeführt ist.) Wenn die Person in beiden
Dateien aufgeführt ist, erhält sie Nur-Lesezugriff. In allen anderen
Fällen erhält die Person vollen Schreib-Lese-Zugriff.
Daher hat ein typisches Archiv mit anonymem CVS-Zugriff die folgende
(oder ähnliche) CVS/passwd-Datei:
|
CVS/passwd
|
anonymous:XR4EZcEsOszik
|
dies (oder Ähnliches) in etc/passwd:
|
etc/passwd
|
anonymous:!:1729:105:Anonymous CVS User:/usr/local/newrepos:/bin/false
|
und Folgendes in CVSROOT/readers:
|
CVSROOT/readers
|
anonymous
|
und natürlich die bereits erwähnte Konfiguration in /etc/services und
/etc/inetd.conf. Das ist schon alles!
Denken Sie daran, dass einige ältere Unix-Systeme keine Benutzernamen
länger als acht Zeichen erlauben. Eine Art dies zu umgehen wäre, den
Benutzer "anon" statt "anonymous" in CVSROOT/passwd und in den
Systemdateien zu nennen, da oft angenommen wird, dass anon sowieso
eine Abkürzung für anonymous ist. Es ist aber besser, etwa das
Folgende in CVSROOT/passwd zu schreiben:
|
CVSROOT/passwd
|
anonymous:XR4EZcEsOszik:cvsanon
|
(und dann natürlich "cvsanon" in den Systemdateien zu verwenden). Auf
diese Weise sind Sie dazu in der Lage, eine Archivadresse zu
veröffentlichen, die "anonymous" verwendet, was mittlerweile mehr
oder weniger Standard ist. Wenn man auf das Archiv dann wie folgt
zugreift:
user@linux ~$
cvs -d :pserver:anonymous@cvs.foobar.com:/usr/local/newrepos (usw...)
|
würde der Server tatsächlich als cvsanon laufen (oder wie auch immer
Sie es konfiguriert haben). Man braucht dann weder zu wissen noch sich
darum zu sorgen, wie Dinge auf der Serverseite eingerichtet sind -
man sieht nur die veröffentlichte Adresse.
|
|
Das neue Archiv beinhaltet immer noch keine Projekte. Lassen Sie uns
den ursprünglichen Import aus Kapitel 2 noch einmal betrachten und
beobachten, was im Archiv dabei vor sich geht. (Aus Gründen der
Einfachheit nehmen alle Befehle an, dass die CVSROOT-Umgebungsvariable
auf /usr/local/newrepos gesetzt ist, damit bei Importen und
checkout-Befehlen das Archiv nicht mit der Option -d angegeben werden
muss.)
user@linux ~$
ls /usr/local/newrepos
CVSROOT/
user@linux ~$
pwd
/home/jrandom/src/
user@linux ~$
ls
myproj/
user@linux ~$
cd myproj
user@linux ~/myproj$
cvs import -m "initial import into CVS" myproj jrandom start
N myproj/README.txt N myproj/hello.c cvs import: Importing /usr/local/newrepos/myproj/a-subdir N myproj/a-subdir/whatever.c cvs import: Importing /usr/local/newrepos/myproj/a-subdir/subsubdir N myproj/a-subdir/subsubdir/fish.c cvs import: Importing /usr/local/newrepos/myproj/b-subdir N myproj/b-subdir/random.c No conflicts created by this import
user@linux ~/myproj$
ls /usr/local/newrepos
CVSROOT/ myproj/
user@linux ~/myproj$
cd /usr/local/newrepos/myproj
user@linux /usr/local/newrepos/myproj$
ls
README.txt,v a-subdir/ b-subdir/ hello.c,v
user@linux /usr/local/newrepos/myproj$
cd a-subdir
user@linux /usr/local/newrepos/myproj/a-subdir$
ls
subsubdir/ whatever.c,v
user@linux /usr/local/newrepos/myproj/a-subdir$
cd ..
user@linux /usr/local/newrepos/myproj$
|
Vor dem Import enthielt das Archiv nur seinen Verwaltungsbereich
CVSROOT. Nach dem Import erschien ein neues Verzeichnis - myproj. Die
Dateien und Unterverzeichnisse in diesem neuen Verzeichnis sehen
verdächtig dem Projekt ähnlich, das wir importiert haben - mit der
Ausnahme, dass die Dateien das Suffix ",v" besitzen. Dies sind
Versionskontrolldateien im RCS-Format (das ",v" steht für "version"),
und sie sind das Rückgrat des Archivs. Jede RCS-Datei speichert die
Revisionshistorie der korrespondierenden Datei im Projekt inklusive
aller Verzweigungen und Markierungen.
Sie müssen das RCS-Format nicht kennen, um CVS zu benutzen (auch wenn
es eine exzellente Dokumentation dazu in der Quelltextdistribution
gibt, siehe doc/RCSFILES). Ein grundlegendes Verständnis des Formates
kann jedoch von großer Hilfe bei der Beseitigung von CVS-Problemen
sein, also werden wir einen kurzen Blick in eine der Dateien -
hello.c,v - werfen. Der Inhalt sieht so aus:
|
hello.c,v
|
head 1.1;
branch 1.1.1;
access ;
symbols start:1.1.1.1 jrandom:1.1.1;
locks ; strict;
comment @ * @;
1.1
date 99.06.20.17.47.26; author jrandom; state Exp;
branches 1.1.1.1;
next;
1.1.1.1
date 99.06.20.17.47.26; author jrandom; state Exp;
branches ;
next;
desc
@@
1.1
log
@Initial revision
@
text
@#include <stdio.h>
void
main ()
{
printf ("Hello, world!\n");
}
@
1.1.1.1
log
@initial import into CVS
@
text
@@
|
Uff! Das meiste davon können Sie ignorieren; machen Sie sich zum
Beispiel keine Gedanken über den Zusammenhang zwischen 1.1 und 1.1.1.1
oder der implizierten 1.1.1-Verzweigung - sie sind vom Standpunkt des
Benutzers oder gar Administrators nicht wirklich von Bedeutung. Sie
sollten einfach versuchen, das Format an sich zu verstehen. Am Anfang
ist eine Ansammlung von Header-Dateien:
|
hello.c,v
|
head 1.1;
branch 1.1.1;
access ;
symbols start:1.1.1.1 jrandom:1.1.1;
locks ; strict;
comment @ * @;
|
Weiter unten in der Datei sind Gruppen von Metainformationen über jede
Revision (die aber immer noch nicht den Inhalt der Revision zeigen),
so z.B.:
|
hello.c,v
|
1.1
date 99.06.20.17.47.26; author jrandom; state Exp;
branches 1.1.1.1;
next ;
|
Und schließlich folgen die Log-Mitteilung und der Text der
eigentlichen Revision:
|
hello.c,v
|
1.1
log
@Initial revision
@
text
@#include <stdio.h>
void
main ()
{
printf ("Hello, world!\n");
}
@
1.1.1.1
log
@initial import into CVS
@
text
@@
|
Wenn Sie genau hinsehen, wird Ihnen auffallen, dass der Inhalt der
ersten Revision unter der Überschrift 1.1 gespeichert ist, die
Log-Mitteilung aber "Initial revision" (ursprüngliche Revision) ist,
wogegen die Log-Mitteilung, die wir eigentlich beim Import verwendet
haben "Initial import into CVS" (ursprünglicher Import in CVS) war,
die weiter unten unter Revision 1.1.1.1 erscheint. Sie brauchen sich
über diese Diskrepanz im Moment keine Gedanken zu machen. Dies
passiert, weil Importe ein besonderer Umstand sind: Um wiederholte
Importe in dasselbe Projekt mit einem nützlichen Effekt zu versehen,
platziert import die ursprüngliche Revision sowohl auf den Hauptzweig
als auch auf einen speziellen Zweig (der Grund dafür wird klarer
werden, wenn wir herstellerabhängige Verzweigungen in Kapitel 6
betrachten). Bis dahin können Sie 1.1 und 1.1.1.1 als identisch
betrachten.
Die Datei enthüllt sogar noch mehr, sobald wir die erste Änderung an
hello.c per commit übertragen:
user@linux ~$
cvs -Q co myproj
user@linux ~$
cd myproj
user@linux ~/myproj$
emacs hello.c
|
(make some changes to the file)
user@linux ~/myproj$
cvs ci -m "print goodbye too"
cvs commit: Examining . cvs commit: Examining a-subdir cvs commit: Examining a-subdir/subsubdir cvs commit: Examining b-subdir Checking in hello.c; /usr/local/newrepos/myproj/hello.c,v <-- hello.c new revision: 1.2; previous revision: 1.1 done
|
Wenn Sie sich nun hello.c,v im Archiv anschauen, können Sie den Effekt
des commit-Befehls sehen:
|
hello.c,v
|
head 1.2;
access;
symbols
start:1.1.1.1 jrandom:1.1.1;
locks; strict;
comment @ * @;
1.2
date 99.06.21.01.49.40; author jrandom; state Exp;
branches;
next 1.1;
1.1
date 99.06.20.17.47.26; author jrandom; state Exp;
branches
1.1.1.1;
next ;
1.1.1.1
date 99.06.20.17.47.26; author jrandom; state Exp;
branches;
next ;
desc
@@
1.2
log
@print goodbye too
@
text
@#include <stdio.h>
void
main ()
{
printf ("Hello, world!\n");
printf ("Goodbye, world!\n");
}
@
1.1
log
@Initial revision
@
text
@d7 1
@
1.1.1.1
log
@initial import into CVS
@
text
@@
|
Nun ist der komplette Inhalt der Revision 1.2 in der Datei
gespeichert, und der Text für Revision 1.1 wurde durch die kryptische
Formel:
ersetzt.
Der Ausdruck "d7 1" ist eine Differenzcodierung, die bedeutet: "Lösche
eine Zeile beginnend bei Zeile 7." Anders ausgedrückt, um Revision 1.1
zu erhalten, lösche Zeile 7 in Revision 1.2! Versuchen Sie es für
sich selbst durchzuarbeiten. Sie werden erkennen, dass es in der Tat
Revision 1.1 erzeugt - es entfernt einfach die Zeile, die wir zur
Datei hinzugefügt hatten.
Dies demonstriert das Grundprinzip des RCS-Formats: Es speichert nur
die Unterschiede zwischen Revisionen und spart dadurch eine Menge
Platz im Vergleich dazu, die Revisionen jeweils komplett zu
speichern. Um von der aktuellsten Revision zur vorhergehenden zu
gelangen, wendet es einen Patch auf die spätere Revision an mit Hilfe
der gespeicherten Differenz. Natürlich bedeutet das, dass mehr
Patch-Operationen durchgeführt werden müssen, je weiter Sie in den
Revisionen mit Hilfe der gespeicherten Differenzen zurückgehen. (Wenn
die Datei z.B. die Revision 1.7 besitzt und von CVS die Revision 1.4
angefordert wird, muss CVS Revision 1.6 durch rückwärts patchen von
1.7, dann 1.5 durch patchen von 1.6 und dann 1.4 durch patchen von 1.5
erzeugen.) Glücklicherweise sind alte Revisionen auch diejenigen, die
am seltensten verlangt werden, daher funktioniert das RCS-System in
der Praxis ganz gut. Je aktueller die Revision ist, desto einfacher
ist es, sie zu erhalten.
Sie müssen nicht wissen, was all die Einträge der Header-Informationen
am Anfang der Datei bedeuten. Die Effekte bestimmter Operationen
zeigen sich jedoch sehr klar in den Headern, und eine flüchtige
Bekanntschaft mit ihnen mag sich als nützlich erweisen.
Wenn Sie eine neue Revision per commit in den Stamm übertragen, wird
die Marke "head" aktualisiert. (Im vorhergehenden Beispiel wurde sie
zu 1.2, nachdem die zweite Revision zu hello.c per commit übertragen
wurde.) Wenn Sie eine Datei als Binärdatei hinzufügen oder an einen
symbolischen Namen binden, werden diese Operationen ebenfalls in den
Headern verzeichnet. Wir fügen z.B. foo.jpg als Binärdatei hinzu und
markieren es einige Male:
user@linux ~$
cvs add -kb foo.jpg
cvs add: scheduling file 'foo.jpg' for addition cvs add: use 'cvs commit' to add this file permanently
user@linux ~$
cvs -q commit -m "added a random image; ask jrandom@red-bean.com why"
RCS file: /usr/local/newrepos/myproj/foo.jpg,v done Checking in foo.jpg; /usr/local/newrepos/myproj/foo.jpg,v <-- foo.jpg initial revision: 1.1 done
user@linux ~$
cvs tag some_random_tag foo.jpg
T foo.jpg
user@linux ~$
cvs tag ANOTHER-TAG foo.jpg
T foo.jpg
|
Betrachten Sie nun den Header-Abschnitt von foo.jpg,v im Archiv:
|
foo.jpg,v
|
head 1.1;
access;
symbols
ANOTHER-TAG:1.1
some_random_tag:1.1;
locks; strict;
comment @# @;
expand @b@;
|
Beachten Sie das b in der expand-Zeile am Ende - es kommt daher, dass
wir beim Hinzufügen der Datei die Option -kb verwendet haben, und
bedeutet, dass die Datei keiner Schlüsselwort- oder
Zeilenendeersetzung unterzogen wird, die normalerweise während
Checkouts und Aktualisierungen geschehen würden, wenn es eine
Textdatei wäre. Die Markierungen erscheinen im Abschnitt symbols, eine
Markierung pro Zeile - beide sind zur ersten Revision hinzugefügt, da
wir diese in beiden Fällen markiert hatten. (Dies erklärt auch, warum
Markierungsnamen nur Buchstaben, Zahlen, Bindestriche und Unterstriche
enthalten dürfen. Wenn die Markierung selbst Punkte oder Doppelpunkte
enthielte, könnten die Aufzeichnungen darüber in der RCS-Datei
zweideutig sein, da es keine Möglichkeit gibt, die Grenze zwischen der
Markierung und der ihr angefügten Revisionsnummer zu erkennen.)
|
|
Das @-Symbol wird in RCS-Dateien als Feldbegrenzer verwendet. Dies
bedeutet, dass ein im Text oder der Log-Mitteilung vorkommendes
"@"-Zeichen quotiert5 werden muss (ansonsten würde CVS es
fälschlicherweise als Endemarkierung für dieses Feld interpretieren).
Es wird durch Verdoppelung quotiert - d.h. CVS interpretiert @@ immer
als "Textzeichen @", nie als "Ende des aktuellen Feldes". Als wir
foo.jpg per commit übertragen haben, war die Log-Mitteilung
|
"added a random image; ask jrandom@red-bean.com why"
|
Das wird in foo.jpg,v wie folgt gespeichert:
|
foo.jpg,v
|
1.1
log
@added a random image; ask jrandom@@red-bean.com why
@
|
Das @-Symbol in jrandom@@red-bean.com wird automatisch
zurückverwandelt, sobald CVS die Log-Mitteilung erstellt:
user@linux ~$
cvs log foo.jpg
RCS file: /usr/local/newrepos/myproj/foo.jpg,v Working file: foo.jpg head: 1.1 branch: locks: strict access list: symbolic names: ANOTHER-TAG: 1.1 some_random_tag: 1.1 keyword substitution: b total revisions: 1; selected revisions: 1 description: ---------------------------- revision 1.1 date: 1999/06/21 02:56:18; author: jrandom; state: Exp; added a random image; ask jrandom@red-bean.com why ===========================================
user@linux ~$
|
Sie müssen dies nur bedenken, wenn Sie einmal RCS-Dateien von Hand
editieren müssen (was selten ist, aber durchaus einmal vorkommen kann)
- denken Sie dann daran, die @-Zeichen in Revisionsinhalten und
Log-Mitteilungen zu verdoppeln. Wenn Sie das nicht tun, wird die
RCS-Datei durcheinander geraten und wahrscheinlich ein seltsames und
unerwünschtes Verhalten zeigen.
Wo wir über von Hand zu editierende RCS-Dateien reden - lassen Sie
sich nicht von den Zugriffsrechten im Archiv in die Irre führen:
user@linux ~$
ls -l
total 6 -r--r--r-- 1 jrandom users 410 Jun 20 12:47 README.txt,v drwxrwxr-x 3 jrandom users 1024 Jun 20 21:56 a-subdir/ drwxrwxr-x 2 jrandom users 1024 Jun 20 21:56 b-subdir/ -r--r--r-- 1 jrandom users 937 Jun 20 21:56 foo.jpg,v -r--r--r-- 1 jrandom users 564 Jun 20 21:11 hello.c,v
user@linux ~$
|
(Für diejenigen Leser, die nicht so vertraut mit der Ausgabe des
ls-Kommandos unter Unix sind - die Zeilen "-r--r--r--" auf der linken
Seite bedeuten im Wesentlichen, dass die Dateien gelesen, aber nicht
geändert werden können.) Auch wenn die Dateien für jeden als nur
lesbar erscheinen, muss man die Zugriffsrechte auf das Verzeichnis in
Betracht ziehen:
user@linux ~$
ls -ld .
drwxrwxr-x 4 jrandom users 1024 Jun 20 22:16 ./
user@linux ~$
|
Das Verzeichnis myproj/ selbst und alle seine Unterverzeichnisse sind
für den Benutzer (jrandom) und die Gruppe (users) beschreibbar. Das
bedeutet, dass CVS (als Benutzer jrandom oder von jemandem in der
Gruppe users ausgeführt) Dateien in diesen Verzeichnissen erzeugen und
löschen kann, auch wenn schon vorhandene Dateien nicht direkt
editiert werden können. CVS editiert eine RCS-Datei, indem es eine
separate Kopie davon anlegt deshalb sollten Sie auch alle Ihre
Änderungen an einer temporären Kopie vornehmen und danach die
existierende RCS-Datei durch die neue ersetzen. (Fragen Sie aber bitte
nicht, warum die Dateien selbst nur lesbar sind - das hat historische
Gründe, die mit der Arbeitsweise von RCS als eigenständigem Programm
zu tun haben.)
Übrigens ist es wahrscheinlich nicht in Ihrem Sinn, wenn die Gruppe
der Dateien "users" ist, wenn man bedenkt, dass das oberste
Verzeichnis des Archivs explizit der Gruppe "cvs" zugewiesen wurde.
Sie können das Problem beheben, indem Sie das folgende Kommando
innerhalb des Archivs ausführen:
user@linux ~$
cd /usr/local/newrepos
user@linux ~$
chgrp -R cvs myproj
|
Unglücklicherweise regeln die üblichen Unix-Dateierzeugungsregeln nur,
welcher Gruppe neue Dateien im Archiv zugeordnet werden, also werden
Sie ab und zu die Kommandos chgrp oder chmod auf bestimmte Dateien
oder Verzeichnisse im Archiv ausführen müssen. Es gibt keine
festgelegten und einfachen Regeln dafür, wie Archivzugriffsrechte
strukturiert werden sollten; es hängt davon ab, wer an welchem
Projekt arbeitet.
|
|
Wenn Sie eine Datei aus einem Projekt löschen, verschwindet diese
nicht einfach. CVS muss dazu in der Lage sein, solche Dateien
wiederherzustellen, wenn Sie einen älteren Schnappschuss des
Projektes anfordern. Stattdessen wird die Datei sozusagen auf den
"Dachboden" (Attic) geräumt:
user@linux ~/src/myproj$
pwd
/home/jrandom/src/myproj
user@linux ~/src/myproj$
ls /usr/local/newrepos/myproj/
README.txt,v a-subdir/ b-subdir/ foo.jpg,v hello.c,v
user@linux ~/src/myproj$
rm foo.jpg
user@linux ~/src/myproj$
cvs rm foo.jpg
cvs remove: scheduling 'foo.jpg' for removal cvs remove: use 'cvs commit' to remove this file permanently
user@linux ~/src/myproj$
cvs ci -m "Removed foo.jpg" foo.jpg
Removing foo.jpg; /usr/local/newrepos/myproj/foo.jpg,v <-- foo.jpg new revision: delete; previous revision: 1.1 done
user@linux ~/src/myproj$
cd /usr/local/newrepos/myproj/
user@linux /usr/local/newrepos/myproj$
ls
Attic/ README.txt,v a-subdir/ b-subdir/ hello.c,v
user@linux /usr/local/newrepos/myproj$
cd Attic
user@linux /usr/local/newrepos/myproj/Attic$
ls
foo.jpg,v
user@linux $
|
In jedem Archivverzeichnis eines Projekts bedeutet die Existenz eines
Attic/ -Unterverzeichnisses, dass mindestens eine Datei aus diesem
Verzeichnis gelöscht wurde. (Dies bedeutet, dass Sie keine
Verzeichnisse namens Attic/ in Ihrem Projekt verwenden sollten.) CVS
bewegt aber nicht einfach die RCS-Datei nach Attic/; es wird auch
eine neue Version dieser Datei mit einem speziellen Revisionsstand
namens "dead" (engl = tot) per commit erzeugt. Dies ist der
entsprechende Ausschnitt aus Attic/foo.jpg,v:
|
Attic/foo.jpg,v
|
1.2
date 99.06.21.03.38.07; author jrandom; state dead;
branches;
next 1.1;
|
Wenn die Datei später wieder zum Leben erweckt wird, kann CVS
aufzeichnen, dass die Datei irgendwann "tot" gewesen ist und nun
wieder zum Leben erweckt wurde.
Wenn Sie also eine gelöschte Datei wiederherstellen wollen, können Sie
sie nicht einfach aus Attic/ herausnehmen und wieder in das Projekt
zurückbewegen. Stattdessen müssen Sie ungefähr
Folgendes in einer Arbeitskopie durchführen:
user@linux ~/src/myproj$
pwd
/home/jrandom/src/myproj
user@linux ~/src/myproj$
cvs -Q update -p -r 1.1 foo.jpg > foo.jpg
user@linux ~/src/myproj$
ls
CVS/ README.txt a-subdir/ b-subdir/ foo.jpg hello.c
user@linux ~/src/myproj$
cvs add -kb foo.jpg
cvs add: re-adding file foo.jpg (in place of dead revision 1.2) cvs add: use 'cvs commit' to add this file permanently cvs ci -m "revived jpg image" foo.jpg Checking in foo.jpg; /usr/local/newrepos/myproj/foo.jpg,v <-- foo.jpg new revision: 1.3; previous revision: 1.2 done
user@linux ~/src/myproj$
cd /usr/local/newrepos/myproj/
user@linux /usr/local/newrepos/myproj$
ls
Attic/ a-subdir/ README.txt,v b-subdir/ hello.c,v
user@linux /usr/local/newrepos/myproj$
ls Attic/
user@linux /usr/local/newrepos/myproj$
|
Es gibt noch einiges weitere Wissenswerte über das RCS-Format, doch
das reicht aus, damit ein CVS-Administrator ein Archiv betreuen kann.
Es kommt sehr selten vor, dass tatsächlich eine RCS-Datei editiert
werden muss; Sie werden normalerweise nur Zugriffsrechte im Archiv
anpassen müssen, zumindest meiner Erfahrung nach. Wenn CVS aber doch
einmal beginnt, sich wirklich seltsam zu verhalten (das ist selten,
aber nicht absolut unmöglich), sollten Sie tatsächlich einmal in die
RCS-Dateien sehen, um herauszufinden, was vor sich geht.
|
|
Die Dateien in newrepos/CVSROOT/ sind nicht Teil eines Projektes,
sondern werden dazu verwendet, das Verhalten von CVS im Archiv zu
steuern. Der beste Weg, diese Dateien zu editieren, ist es, eine
Arbeitskopie von CVSROOT/ genau wie ein reguläres Projekt
auszuchecken:
user@linux ~$
cvs co CVSROOT
cvs checkout: Updating CVSROOT U CVSROOT/checkoutlist U CVSROOT/commitinfo U CVSROOT/config U CVSROOT/cvswrappers U CVSROOT/editinfo U CVSROOT/loginfo U CVSROOT/modules U CVSROOT/notify U CVSROOT/rcsinfo U CVSROOT/taginfo U CVSROOT/verifymsg
|
Wir betrachten die Dateien in ungefährer Reihenfolge ihrer
Wichtigkeit. Jede der Dateien kommt mit einem erklärenden Kommentar am
Anfang. (Die Kommentierungskonvention ist in allen Dateien gleich:
Ein "#"-Zeichen am Anfang der Zeile kennzeichnet einen Kommentar, und
CVS ignoriert solche Zeilen beim Lesen der Datei.) Denken Sie daran,
dass alle Änderungen an der Arbeitskopie der administrativen Dateien
erst dann für CVS wirksam werden, wenn Sie die Änderungen per commit
übertragen.
Tipp
Wenn Sie extreme Sicherheitsbedenken haben, sollten Sie die
Unix-Zugriffsrechte auf CVSROOT unterschiedlich zu den Zugriffsrechten
anderswo im Archiv halten, um genaue Kontrolle darüber zu haben,
welcher Benutzer Änderungen an CVSROOT vornehmen kann. Wie Sie ein
wenig später sehen werden, gibt die Fähigkeit, Dateien in CVSROOT zu
verändern, im Prinzip jedem CVS-Benutzer - auch entfernten - die
Möglichkeit, beliebige Kommandos auf der Archivmaschine auszuführen.
|
Die Datei config
Die Konfigurationsdatei legt gewisse globale Verhaltensparameter fest.
Sie hat ein sehr genau einzuhaltendes Format:
(usw.)
ohne zusätzlich erlaubte Leerzeichen. Hier ist ein Beispiel für eine
Konfigurationsdatei:
|
config
|
SystemAuth=yes
TopLevelAdmin=no
PreservePermissions=no
|
(Ein fehlender Eintrag ist gleichbedeutend mit "no".)
Der Parameter SystemAuth legt fest, ob CVS in der System-passwd-Datei
nachsehen soll, wenn es einen gegebenen Benutzernamen nicht in
CVSROOT/passwd finden kann. CVS-Distributionen werden mit diesem
Parameter auf "no" gesetzt ausgeliefert - zum Wohle Ihrer
Systemsicherheit.
TopLevelAdmin steuert, ob CVS beim Checkout einer Arbeitskopie auch
ein CVS/-Verzeichnis anlegt. Dieses CVS/-Verzeichnis wäre nicht
innerhalb der Arbeitskopie, sondern auf gleicher Verzeichnisebene. Es
mag bequem sein, dies zu aktivieren, wenn Sie (und die Benutzer Ihres
Archivs) dazu neigen, viele verschiedene Projekte aus dem gleichen
Archiv auszuchecken. Ansonsten sollten Sie es ausgeschaltet lassen, da
es verwirren kann, ein unerwartetes zusätzliches CVS/-Verzeichnis zu
sehen.
PreservePermissions kontrolliert, ob Dateizugriffsrechte und ähnliche
Metadaten in der Revisionshistorie erhalten bleiben. Dies ist eine
etwas obskure Funktionalität, und es lohnt sich wahrscheinlich nicht,
sie im Detail zu beschreiben. Schauen Sie unter dem Knoten "Special
Files" im Cederqvist nach, wenn Sie daran interessiert sind.
Tipp
"Knoten" (Node) ist Texinfo-Jargon für einen bestimmten Ort innerhalb
eines Info-Dokuments. Um zu einem bestimmten Knoten beim Lesen eines
Info-Dokuments zu gelangen, tippen Sie einfach an einer beliebigen
Stelle des Dokuments "g" gefolgt vom Namen des Knotens.
|
LockDir ist auch eine selten benutzte Funktion. Unter bestimmten
Umständen möchten Sie CVS dazu bewegen, dass es seine Lock-Dateien an
einer anderen Stelle als direkt in den Projektunterverzeichnissen
anlegt, um Probleme mit Zugriffsrechten zu vermeiden. Diese
Lock-Dateien halten CVS davon ab sich selbst auf die Füße zu treten,
wenn mehrere Operationen gleichzeitig auf demselben Archiv
durchgeführt werden. Normalerweise müssen Sie sich nicht darum
kümmern, doch manchmal können Benutzer Probleme haben, ein Archiv zu
aktualisieren oder auszuchecken, weil sie keine Lock-Datei erzeugen
können. (CVS muss auch für nur lesende Operationen eine Lock-Datei
anlegen, um Situationen zu vermeiden, in denen eine Instanz von CVS
liest, während eine andere Instanz schreibt.) Normalerweise ist die
Lösung dafür, die Archivzugriffsrechte zu ändern; wenn das aber nicht
machbar ist, kann die LockDir-Funktionalität nützlich sein.
Die modules-Datei
In der Datei modules können Sie Aliase und andere Gruppierungen für im
Archiv befindliche Projekte festlegen. Die grundlegendste Zeile in
modules sieht wie folgt aus:
|
modules
|
MODUL_NAME MODUL_IM_ARCHIV
|
zum Beispiel
|
modules
|
mp myproj
asub myproj/a-subdir
|
(Die Pfadangaben auf der rechten Seite sind relativ zum Anfang des
Archivs.). Dies gibt Entwicklern einen anderen Namen, über den sie ein
Projekt oder einen Teil eines Projektes per checkout erhalten können:
user@linux ~$
cvs co mp
cvs checkout: Updating mp U mp/README.txt U mp/foo.jpg U mp/hello.c cvs checkout: Updating mp/a-subdir U mp/a-subdir/whatever.c cvs checkout: Updating mp/a-subdir/subsubdir U mp/a-subdir/subsubdir/fish.c cvs checkout: Updating mp/b-subdir U mp/b-subdir/random.c
|
oder
| |