Dass der Computeralltag auch unter Linux des Öfteren für Überraschungen gut ist, ist eher eine Binsenweisheit: Immer wieder funktionieren Dinge nicht oder nicht so, wie eigentlich angenommen. Das Answer-Girl im LinuxUser zeigt, wie man mit solchen Problemchen elegant fertig wird.
Sei es die Studienarbeit, ein Vortrag, Buch, Artikel oder Programmierprojekt – wenn man über längere Zeit an einem (Quell-)Text arbeitet, kommt früher oder später der Tag, an dem man sich wünscht, den letzten Montag gelöschten Abschnitt doch wieder zurückholen zu können. Doch gelöscht ist gelöscht, und wer nimmt sich dann nicht vor, es nächstes Mal besser zu machen?
Versionskontrolle im Kleinen …
Wer allein arbeitet und einigermaßen Durchhaltevermögen hat, fängt möglicherweise an, sich die letzte Version (z.B. kursskript-2.tex) zu Beginn einer Arbeitsrunde in eine neue Datei mit fortlaufender Nummer (kursskript-3.tex) zu kopieren. Welche Version die von letztem Montag war, bekommt man aus der Datumsangabe heraus, die ls -l kursskript* als letztes Änderungsdatum angibt.
Warum dann nicht gleich den Datumsstempel mit im Dateinamen verpacken? Mit date liefert ein Unixsystem die aktuelle Datums- und Zeitangabe schließlich frei Haus, und mit den rückwärts zeigenden Hochkommata kann man die Shell dazu bewegen, zuerst das darin enthaltene Kommando auszuführen und das Ergebnis im Gesamtkommando zu verwenden:
[trish@lillegroenn answergirl]$ cp kursskript.tex kursskript`date`.tex cp: copying multiple files, but last argument (2001.text) is not a directory Try `cp --help' for more information.
cp beschwert sich darüber, dass wir mehr als eine Datei kopieren wollen und das letzte Argument 2001.tex kein Verzeichnis sei (denn in eine einzelne normale Datei kann man nicht mehrere Quelldateien kopieren). 2001.tex? Das sieht doch aus wie ein Bestandteil der date-Ausgabe:
[trish@lillegroenn answergirl]$ date Mon Mar 12 02:24:09 CET 2001
Damit fällt es uns wie Schuppen von den Augen: Die Leerzeichen zählen für die Shell natürlich als Trennzeichen zwischen Argumenten, bei cp also Dateinamen. Mit Gänsefüßchen lässt sich die bash allerdings davon überzeugen, dass die Leerzeichen zum Argumentstring gehören:
[trish@lillegroenn answergirl]$ cp kursskript.tex "kursskript`date`.tex" [trish@lillegroenn answergirl]$ ls -l kursskript* -rw-r--r-- 1 trish users 8967 Mar 12 02:25 kursskript.tex -rw-r--r-- 1 trish users 8967 Mar 12 02:34 kursskriptMon Mar 12 02:34:04 CET 2001.tex
Der Nachteil dabei: kursskriptMon Mar 12 02:34:04 CET 2001.tex sieht nicht nur hässlich aus, dank der Leerzeichen im Dateinamen wird die Datei uns mehr als einmal dazu zwingen, ihren Namen in der einen oder anderen Kommandozeile in Hochkommata zu setzen. Lieber wäre uns also ein Dateiname à la kursskript_dd_mm_yy.tex – oder damit die Dateien auch immer schön in der dem Kalender folgenden Reihenfolge in der ls-Ausgabe auftauchen – kursskript_yy_mm_dd.tex.
Wie ein Blick in die date-Manpage verrät, geht auch dies: Wir müssen date lediglich hinter einem Plus die gewünschten Formatplatzhalter mit auf den Weg geben:
[trish@lillegroenn answergirl]$ mv "kursskriptMon Mar 12 02:34:04 CET 2001.tex" kursskript_`date +%y_%m_%d`.tex -rw-r--r-- 1 trish users 8967 Mar 12 02:25 kursskript.tex -rw-r--r-- 1 trish users 8967 Mar 12 02:34 kursskript_01_03_12.tex
… und für die weniger Gewissenhaften
Sie fühlen sich schon jetzt nicht mehr wohl bei soviel Dran-Denken-Müssen? Da sind Sie nicht die/der Einzige. Erst recht, wenn’s darum geht, mit Co-Autor(inn)en an einem Manuskript zu arbeiten, wird es nicht nur mit der Disziplin schwierig, sondern auch mit dem Abgleichen. Wer garantiert mir schließlich, dass meine Co-Referentin die gestern eingebauten Tippfehler nicht schweigend in ihrer Version beseitigt, während ich die Sätze eben dieses Kapitels in der meinigen gänzlich umstelle?
Nicht nur, um die Arbeitsroutine zu vereinfachen, sondern auch um Mehrarbeit zu vermeiden, gibt es hier nur eins: Ein professionelles Versionsmanagement muss her. Wenn Sie mit Office-Paketen o.ä. arbeiten, die Ihre Arbeit in proprietären Binärformaten (wie StarWriter) abspeichern, werden Sie vermutlich auf die hoffentlich eingebaute Versionskontrollfunktion zurückgreifen. ASCII-Texte hingegen lassen sich spielend mit Versionsmanagern verwalten, wie sie in großen Programmierprojekten eingesetzt werden. (Der Quellcode in einer Programmiersprache ist schließlich auch nichts anderes als Text.)
Die Recherche ergibt erstaunlich wenig Auswahl. Während Firmen noch eher das kommerzielle Perforce (http://www.perforce.com/) lizensieren werden (Open-Source-Projekte können eine kostenlose Lizenz anfordern; die voll funktionsfähige Evaluationsversion lässt nur Zwei-Mann/Frau-Projekte zu) greifen nicht nur Gelegenheitsversionskontrollierer/innen zumeist auf das altgediente Concurrent Versions System cvs zurück.
Das wird auch von den meisten Distributionen mitgeliefert. Wer auf seinen CDs oder dem FTP-Server des Distributors kein passendes Paket findet, steuert am besten http://rpmfind.net/linux/rpm2html/search.php?query=cvs oder für den Sourcecode http://download.cyclic.com/pub/ an.
Aufgesetzt
Zunächst gilt es also zu installieren – bei rpm-basierten Systemen z.B. mit einem
[root@lillegroenn software]# rpm -i cvs-1.10.7-1.i386.rpm
Da wäre es nun vorhanden, das große Unbekannte. Ein zaghaftes
[trish@lillegroenn answergirl]$ cvs --help
trägt mit schöner Unübersichtlichkeit nicht gerade zum Warmwerden bei. Doch ein genauerer Blick ins Chaos hilft dann doch weiter:
Usage: cvs [cvs-options] command [command-options-and-arguments][…]
Um cvs zu bedienen, müssen wir also mindestens cvs sagen und dahinter ein CVS-Kommando angeben. Zusätzlich lässt sich das Verhalten von cvs durch cvs-options verändern, die vor dem CVS-Kommando angegeben werden müssen. Um die Komplexität noch zu vergrößern, können natürlich auch jedem CVS-Kommando eigene Optionen und Argumente folgen.
Einmal tief durchgeatmet, entziffert sich auch eine der nächsten Zeilen:
[…]
(specify --help-commands for a list of commands[…]
Tatsächlich, cvs --help-commands gibt eine ganze Reihe Kommandos aus – und unter dem einen oder anderen können wir uns sogar etwas vorstellen:
[…]
init Create a CVS repository if it doesn't exist[…]
init, das klingt nach initialisieren, und wenn einer/m der Begriff CVS-Repository schonmal beim einen oder anderen Open-Source-Projekt übern Weg gelaufen ist, sieht das ganz nach dem aus, was wir möchten: ein CVS-Depot anlegen, in dem wir unsere Dateien ablegen – einchecken – können.
[trish@lillegroenn answergirl]$ cvs init cvs init: No CVSROOT specified! Please use the `-d' option cvs [init aborted]: or set the CVSROOT environment variable.
Wenn’s denn so einfach wäre … Zum Glück klärt uns die cvs-Manpage über die omininöse Option -d (“directory”) auf:
CVS OPTIONS[…]
-d CVS_root_directory
Use CVS_root_directory as the root directory path
name of the master source repository. Overrides
the setting of the CVSROOT environment variable.
This value should be specified as an absolute path
name.
Wir haben’s also mit einer Option für den cvs-Befehl (im Gegensatz zu einer Option, die sich auf ein CVS-Kommando bezieht) zu tun, die als Argument das Verzeichnis für unser Depot haben will. Wichtig dabei: Wir müssen es mit absolutem Pfad, z.B. ~/cvs/linuxkurs/kursunterlagen angeben.
Wenn der absolute Pfad zu einem Programm nicht in der Umgebungsvariablen PATH aufgeführt ist, reicht es nicht aus, den Namen des Kommandos aufzurufen. Sollte die Shell die im Kasten Mitstreiter/innen gezeigten Kommandoaufrufe für groupadd, usermod und useradd mit einem command not found quittieren, schafft der Aufruf mit absolutem Pfad (/usr/sbin/groupadd usw.) hoffentlich Linderung. Wenn nicht, stellt sich die Frage, ob diese Kommandos überhaupt installiert sind.
[trish@lillegroenn answergirl]$ cvs -d ~/cvs/linuxkurs/kursunterlagen init cvs [init aborted]: cannot make directory /home/trish/cvs/linuxkurs/kursunterlagen: No such file or directory
Na gut, dann sind wir halt nett und erzeugen erstmal das Verzeichnis ~/cvs/linuxkurs samt Eltern-(“parent”-)Verzeichnis ~/cvs/ und versuchen es anschließend nochmal:
[trish@lillegroenn answergirl]$ mkdir -p ~/cvs/linuxkurs [trish@lillegroenn answergirl]$ cvs -d ~/cvs/linuxkurs/kursunterlagen init
Keine Reaktion diesmal, doch das sollte ja in bester Unixtradition bedeuten, dass alles glatt gegangen ist. Und tatsächlich, ls ~/cvs/linuxkurs/kursunterlagen verrät, dass dieses Verzeichnis angelegt wurde und zudem ein weiteres, mit lauter merkwürdig benannten Dateien ausgestattetes Unterverzeichnis namens CVSROOT enthält.
Jetzt müssen da nur noch die angefangenen Kursunterlagen rein – und da war doch nochwas … Stimmt, die Arbeit mit dem Skript- und Folienschreiben will ich nicht alleine machen, also muss auch meine Co-Referentin Zugang zum Depot bekommen. Um all jene nicht zu langweilen, die ihr Repository allein nutzen wollen, sind die dazu nötigen Arbeitsschritte im Kasten Mitstreiter/innen ausgelagert.
Mitstreiter/innen
Wer ein Depot im eigenen Homeverzeichnis anlegt (root könnte es auch woanders, z.B. nach /home/cvs, packen), will natürlich nicht, dass die Co-Autor/innen in allen Dateien in ~ stöbern dürfen.
Gruppeneinteilung
Am besten nehmen wir daher root in Anspruch und lassen sie eine neue Gruppe kurs mit der in /etc/group noch nicht vergebenen Gruppennummer 101 anlegen:
[trish@lillegroenn answergirl]$ su Password: root-Passwort [root@lillegroenn answergirl]# groupadd -g 101 kurs
Die Maintainerin des angelegten CVS-Depots sollte von root natürlich in die neue Gruppe aufgenommen werden. Das geht durch manuelles Editieren von /etc/group und selbstverständlich auch mit grafischen Userverwaltungswerkzeugen. Aber ehe wir die als root gestartet haben, sind wir schneller mit einem
[root@lillegroenn answergirl]# usermod -G kurs trish
Das große -G bedeutet dabei: “Füge eine weitere Gruppe zu den anderen Gruppen hinzu, in denen die Benutzerin Mitglied ist”. Ein
[root@lillegroenn answergirl]# groups trish trish : users kurs
verrät, dass trish jetzt außer zur Gruppe users auch zu kurs gehört.
Neue Nutzer
[root@lillegroenn answergirl]# useradd bille
legt dann einen Account für die Benutzerin bille an. Soll sie diesen lediglich zu CVS-Zwecken benutzen, war das ein wenig voreilig, denn dann soll sie ausschließlich der Gruppe kurs angehören.
[root@lillegroenn answergirl]# usermod -g kurs bille
rettet die Angelegenheit – das kleine -g tauscht die primäreGruppe für bille aus, statt eine zusätzliche Gruppe hinzuzufügen.
Dann bekommt bille noch ein Anfangspasswort …
[root@lillegroenn answergirl]# passwd bille New user password: passwort_fuer_bille Retype new user password: passwort_fuer_bille passwd: all authentication tokens updated successfully
…, das von useradd vordefinierte Homeverzeichnis wird angelegt …
[root@lillegroenn answergirl]# mkdir ~bille
… und übergeben (mit dem “change-owner”-Kommando wird bille Eigentümerin ihres Heims, und kurs als Primärgruppe erwirbt die Gruppenrechte dafür):
[root@lillegroenn answergirl]# chown bille:kurs ~bille
Damit kann sich root mit exit wieder ausloggen. trish als CVS-Maintainerin hat jedoch noch eine Aufgabe vor sich: Die CVS-Gruppe kurs muss noch Lese-, Schreib- und bei Verzeichnissen Ausführbarkeits- (respektive Verzeichniswechsel-) Rechte am Depot-Verzeichnis bekommen. Wenn eine Prüfung der Gruppenrechte ergibt, dass die Rechte stimmen, nur der Name der Gruppe falsch ist, hilft das Kommando chgrp.
Eigentumsverhältnisse
Da man Dateien nur an Gruppen “verschenken” darf, deren Mitglied man ist, muss sich trish neu einloggen – erst dann gibt
[trish@lillegroenn answergirl]$ chgrp -R kurs ~/cvs/linuxkurs/kursunterlagen/
keine Fehlermeldungen mehr aus, denn mit dem Login-Prozess werden die Mitgliedschaftsdaten aufgefrischt. Das -R bei chgrp (wie auch chown und chmod) sorgt dafür, dass die Eigentumsangaben für ~/cvs/linuxkurs/kursunterlagen/ und alle darunter liegenden Dateien/Verzeichnisse rekursiv in einem Aufwasch geändert werden.
Bei mehreren CVS-Nutzer(inne)n mit unterschiedlichen Primärgruppe gilt es, ein weiteres Problem zu bedenken: Da trish das Depotverzeichnis gehört, kann sie Daten auch mit ihrer Primärgruppe users einchecken. Die aber wären für Nur-kurs-Mitglieder wie bille nicht mehr zugänglich.
Das Problem lässt sich lösen, indem man das Depotverzeichnis mit dem s-Recht (“set group ID on execution”) für die Gruppe versieht:
[trish@lillegroenn answergirl]$ chmod g+s ~/cvs/linuxkurs/kursunterlagen/ [trish@lillegroenn answergirl]$ ls -al ~/cvs/linuxkurs/kursunterlagen/ total 3 drwxrwsr-x 3 trish kurs 1024 Mar 11 00:31 . drwxr-xr-x 3 trish users 1024 Mar 11 00:31 .. drwxrwxr-x 2 trish kurs 1024 Mar 11 00:31 CVSROOT
Damit ist sichergestellt, dass alle Daten, die ins Depotverzeichnis geschrieben werden, der Gruppe kurs gehören, selbst wenn trish mit einer anderen Primärgruppe eincheckt.
Fernbeziehung
Nun kann sich bille zwar auf lillegroenn einloggen, in ihrem dortigen Homeverzeichnis die Daten auschecken und bearbeiten, doch es ist eher unwahrscheinlich, dass sie die ganze Zeit online sein will, wenn sie mit den Dokumenten arbeitet. Sinn und Zweck eines Revisionskontrollsystems ist es ja gerade auch, die Arbeit von Leuten, die auf mehreren Rechnern arbeiten, zu koordinieren.
Da bille nun einen Shellaccount auf dem CVS-Server lillegroenn hat, kann sie ihre CVS-Anfragen ohne Weiteres per SecureShelltunneln. Dazu setzt sie auf ihrem Internetarbeitsrechner die Variable CVS_RSH (“CVSRemote Shell”) auf das Kommando ssh (ggf. auch mit Pfadangabe):
[bille@billabox ~]$ export CVS_RSH=ssh
Natürlich muss auch sie ihre CVSROOT-Variable auf das Depot-Verzeichnis /home/trish/cvs/linuxkurs/kursunterlagen richten. Da dieses auf dem entfernten Rechner lillegroenn.answergirl.de liegt, wird das etwas komplizierter: Mit dem Stichwort ext gibt sie an, dass das Depot auf einer externen Maschine zu finden ist, dann folgt die Adresse des Depotrechners mit vorangestelltem Usernamen, und zum Schluss kommt das Zielverzeichnis.
Damit es keine Unklarheiten gibt, wo welcher Bestandteil zu Ende ist, trennt man sie mit Doppelpunkten voneinander. Um cvs ganz sicher gehen zu lassen, dass das ganze Ungetüm nicht einfach nur ein etwas komischer Verzeichnisname ist, darf zudem ein einleitender Doppelpunkt nicht fehlen:
[bille@billabox ~]$ export CVSROOT=:ext:bille@lists.answergirl.de:/home/trish/cvs/linuxkurs/kursunterlagen
Anschließend kann bille online gehen und das im Haupttext angelegte Depot linuxkurs mit cvs co linuxkurs auschecken.
Anfangsdatenbestand
Die CVS-Kommandoübersicht führt mit
[…]
import Import sources into CVS, using vendor branches[…]
ein Kommando auf, mit dem es möglich zu sein scheint, unseren initialen Datenbestand, der momentan im Arbeitsverzeichnis ~/kurs liegt, – zwei Verzeichnisse skript und folien mit jeweils einer tex-Datei und einer Abbildung – in unser Depot zu importieren:
[trish@lillegroenn answergirl]$ cd ~/kurs [trish@lillegroenn kurs]$ cvs import cvs import: No CVSROOT specified! Please use the `-d' option cvs [import aborted]: or set the CVSROOT environment variable.
Natürlich: ohne die Angabe des Depots mit -d kann cvs gar nicht wissen, wohin die Daten aus dem aktuellen Verzeichnis importiert werden sollen. Doch da wir keine Lust haben, ständig den Rattenschwanz -d ~/cvs/linuxkurs/kursunterlagen einzutippen, nehmen wir uns die letzte Zeile der Fehlermeldung zu Herzen und setzen die Umgebungsvariable CVSROOT:
[trish@lillegroenn kurs]$ export CVSROOT=~/cvs/linuxkurs/kursunterlagen
Nie mehr Variablen setzen
Das Setzen der Variablen CVSROOT und bei Remote-Zugang zum CVS-Server CVS_RSH ist nicht unbedingt etwas, was man in jeder neuen Shell erneut von Hand tun will. Einfach haben es diejenigen, die nur auf ein einziges CVS-Depot zugreifen wollen: Sie tragen die export-Zeilen in die beim Start jeder bash ausgelesene ~/.bashrc, ggf. auch in die Initialisierungsdatei für Loginshells, ~/.bash_profile, ein (zumindest dann, wenn sie tatsächlich mit der Bash arbeiten).
Komplizierter wird es, wenn Sie mit mehreren Depots zugange sind. Dann empfiehlt es sich, die export-Zeilen in eine ansonsten leere Textdatei zu schreiben und diese vor dem erstmaligen Zugriff auf ein Depot von einer bestimmten Shell aus mit dem Befehl
source Datei_mit_CVS-Variablen
einzulesen (zu sourcen).
Also auf ein Neues:
[trish@lillegroenn kurs]$ cvs import
Usage: cvs import [-d] [-k subst] [-I ign] [-m msg] [-b branch]
[-W spec] repository vendor-tag release-tags…
-d Use the file's modification time as the time of import.
-k sub Set default RCS keyword substitution mode.
-I ign More files to ignore (! to reset).
-b bra Vendor branch id.
-m msg Log message.
-W spec Wrappers specification line.
(Specify the --help global option for a list of other help options)
So einfach und vielleicht ein wenig interaktiv geht das also nicht – cvs besteht darauf, dass wir ihm explizit sagen, wie das Depot (“repository”) heißen soll. Die beiden zusätzlich geforderten Argumente vendor-tag und release-tags sind zum Glück für einfache Versionsverwaltung nicht nötig, so dass wir hier gut irgendwas eingeben können.
Das Vendor-Tag, zuzusagen eine Kennzeichnung für die Herausgeber der zu importierenden Daten, kommt dann zur Geltung, wenn man Änderungen zu Quellen Dritter per CVS verwalten will, die selbst nicht wieder an den Herausgeber zurück fließen (z.B. weil dieser die Änderungen nicht für wichtig, richtig oder allgemein genug hält). Kommt eine neue Originalversion, sorgt das Release-Tag dafür, dass man zwischen den Versionen unterscheiden kann.
Wie dem auch immer sei, ein
[trish@lillegroenn kurs]$ cvs import linuxkurs trish v2001
macht uns mit dem Aufruf des vi-Editors (oder dem in den Umgebungsvariablen VISUAL oder EDITOR gespeicherten Schreibprogramm) darauf aufmerksam, dass es gern noch eine kurze Beschreibung der Daten von uns hätte:
CVS: ———————————————————————- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: ———————————————————————-
Ein o bringt uns beim vi in eine neue Zeile und in den Schreibmodus, sodass wir den Text eingeben können. Mit einem Druck auf die Escape-Taste gelangen wir in den Kommandomodus des vi und speichern und beenden mit der Sequenz :wq unseren Eintrag. cvs quittiert den Import nun mit
cvs import: Importing /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/skript N linuxkurs/skript/unixkurs.tex N linuxkurs/skript/baum.eps cvs import: Importing /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/folien N linuxkurs/folien/unixfoil.tex N linuxkurs/folien/baum.eps No conflicts created by this import
Damit wäre das Depot mit Daten gefüllt – und das Datenverzeichnis ~/kurs samt Unterverzeichnissen jeweils um ein Verzeichnis namens CVS angereichert.
Raus und rein
Mit den eingecheckten Daten zu arbeiten, ist nun recht einfach: Am Beginn eines Arbeitsabschnitts bringt ein
[trish@lillegroenn kurs]$ cvs update cvs update: Updating . cvs update: Updating folien cvs update: Updating skript
die Daten im Datenverzeichnis auf den aktuellen Stand; will man nur ein bestimmtes Unterverzeichnis updaten, wechselt man vor dem Kommando einfach dort hinein.
Ist man am Ende einer Arbeitseinheit angekommen, checkt man die Änderungen an Dateien in den jeweiligen Unterverzeichnissen mit
[trish@lillegroenn kurs]$ cvs ci cvs commit: Examining . cvs commit: Examining folien cvs commit: Examining skript
(“check in”) ein – hier sind wieder vi-Kenntnisse für die Beschreibung der Änderung nötig. Natürlich lassen sich auch einzelne Dateien “comitten”:
[trish@lillegroenn kurs]$ cvs ci folien/unixfoil.tex CVS: ———————————————————————- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: Committing in folien CVS: CVS: Modified Files: CVS: unixfoil.tex CVS: ———————————————————————-
Skrupel? Wer jetzt noch schnell einen Rückzieher machen will, bricht einfach den Editor ohne irgendeine Änderung ab. :q! im Kommandomodus des vi lässt cvs dann auch prompt quengeln:
Log message unchanged or not specified a)bort, c)ontinue, e)dit, !)reuse this message unchanged for remaining dirs Action: (continue)
Ein a bekräftigt, dass es uns mit dem Abbruch ernst ist – im Gegensatz zum einfachen Enter, das einfach dennoch eincheckt, und e, das uns zurück in den Editor bringt.
Frische Daten
Neue Ideen in neue Unterverzeichnisse – manchmal erweist es sich im Nachhinein ganz hilfreich, der anfänglichen Arbeitswut doch noch eine Struktur zu geben …
[trish@lillegroenn kurs]$ mkdir konzept [trish@lillegroenn kurs]$ cd konzept
Wenn das Konzept, konzept.tex, in diesem Unterverzeichnis steht, sollte es auch eingecheckt werden. Nur wie, wenn’s noch nicht im Depot ist?
[trish@lillegroenn konzept]$ cvs add konzept.tex cvs add: cannot open CVS/Entries for reading: No such file or directory cvs [add aborted]: no repository
… war wohl noch nicht ganz die richtige Idee: Natürlich, cvs ist sich unsicher, wohin mit der Datei, da das Unterverzeichnis konzept noch kein CVS-Verzeichnis enthält.
Also einmal Kommando bzw. ein Verzeichnis zurück …
[trish@lillegroenn konzept]$ cd ..
… und eines nach dem anderen:
[trish@lillegroenn kurs]$ cvs add konzept ? konzept/konzept.tex Directory /home/trish/informatica/kursskript/konzept added to the repository [trish@lillegroenn kurs]$ cd konzept [trish@lillegroenn konzept]$ cvs add konzept.tex cvs server: scheduling file `konzept.tex' for addition cvs server: use 'cvs commit' to add this file permanently
Das scheint geklappt zu haben, nur “drin” ist das File offensichtlich noch nicht. Also folgen wir der Anweisung, den Neuzugang auch zu committen. Ob Sie dafür das lange cvs-Kommando commit oder das kurze ci benutzen, das Ergebnis ist das Gleiche:
[trish@lillegroenn konzept]$ cvs commit cvs commit: Examining . CVS: ———————————————————————- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: Committing in . CVS: CVS: Added Files: CVS: konzept.tex CVS: ———————————————————————-oNeues KonzeptESC:wq RCS file: /home/trish/informatica/kursskript/konzept/konzept.tex,v done Checking in konzept.tex; /home/trish/informatica/kursskript/konzept/konzept.tex,v <-- konzept.tex initial revision: 1.1 done
Konflikte vorprogrammiert?
Wenn mehrere Leute an einem Dokument arbeiten, kann auch kein CVS-Server der Welt verhindern, dass Änderungen, die der eine eincheckt, mit Änderungen kollidieren, die die andere etwas später comitten will:
[trish@lillegroenn linuxkurs]$ cvs commit cvs commit: Examining . cvs commit: Examining folien cvs commit: Examining skript cvs commit: Up-to-date check failed for `skript/unixkurs.tex' cvs [commit aborted]: correct above errors first!
Offensichtlich hat hier jemand ebenfalls an skript/unixkurs.tex gewerkelt, und das Depot enthält eine Version, die neuer ist als trishs Arbeitsversion. Damit ist Aktualisierung angesagt:
[trish@lillegroenn linuxkurs]$ cvs update cvs update: Updating . cvs update: Updating folien cvs update: Updating skript RCS file: /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/skript/unixkurs.tex,v retrieving revision 1.2 retrieving revision 1.3 Merging differences between 1.2 and 1.3 into unixkurs.tex rcsmerge: warning: conflicts during merge cvs update: conflicts found in skript/unixkurs.tex C skript/unixkurs.tex
cvs versucht, die mittlerweile getätigten Änderungen und trishs neue Änderungen unter einen Hut zu bekommen (zu mergen). Geht das gut, hat man keine weiteren Sorgen, geht es wie hier schief, muss trish selbst Hand anlegen und die konfliktreiche Datei unixkurs.tex erneut in den Editor laden.
Die wurde in der Zwischenzeit von CVS so verändert, dass der Konflikt sichtbar wird:
<<<<<<< unixkurs.tex
Sommerkurs Informatica Feminale Uni Bremen
=======
Sommeruni 2001 Universit"at Bremen
>>>>>>> 1.3
Oben ist die eigene Version, unten die aktuelle Depotversion zu sehen. Alles, was damit zu tun bleibt, ist, die <-, =– und >-Zeilen zu entfernen und die sich widersprechenden Zeilen zu genau dem Text zusammenzuschreiben, der jetzt eingecheckt werden soll, z.B.
Sommerkurs Informatica Feminale Uni Bremen
Dann muss nur noch eingecheckt werden.
Umbauen
konzept.tex hin oder her – nachdem einmal die Struktur stand, mutierte die Datei immer mehr zum eigentlichen Kursskript. Nach einigen Arbeitsphasen jedenfalls stimmte der Name vorn und hinten nicht mehr, während das früher mal eingecheckte unixkurs.tex im skript-Verzeichnis von niemandem bearbeitet wurde. Kurzum: es ist an der Zeit, einen Schnitt zu machen, unixkurs.tex mit dem Inhalt von konzept.tex zu überschreiben und konzept.tex aus dem Repository zu nehmen:
[trish@lillegroenn kurs]$ mv konzept/konzept.tex skript/unixkurs.tex [trish@lillegroenn kurs]$ cvs remove konzept/konzept.tex cvs remove: scheduling `konzept/konzept.tex' for removal cvs remove: use 'cvs commit' to remove this file permanently [trish@lillegroenn kurs]$ cvs ci
Beim Check-In gibt’s jetzt zwei Nachfragen: Der neue Inhalt von unixkurs.tex will kommentiert sein (z.B. mit konzept.tex jetzt unixfoil.tex), und bei der Dokumentation des konzept.tex-Rausschmisses gibt cvs netterweise gleich denselben Kommentar vor:
konzept.tex jetzt unixfoil.tex CVS: ———————————————————————- CVS: Enter Log. Lines beginning with `CVS:' are removed automatically CVS: CVS: Committing in konzept CVS: CVS: Removed Files: CVS: konzept.tex CVS: ———————————————————————-
Fertig abgespeichert, werden wir Zeug(inn)en der Veränderungen im Depot:
cvs commit: Examining . cvs commit: Examining folien cvs commit: Examining konzept cvs commit: Examining skript Checking in folien/unixfoil.tex; /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/folien/unixfoil.tex,v <-- unixfoil.tex new revision: 1.3; previous revision: 1.2 done Removing konzept/konzept.tex; /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/konzept/konzept.tex,v <-- konzept.tex new revision: delete; previous revision: 1.1.1.1 done
Jetzt gilt es nur noch, das mittlerweile leere konzept-Verzeichnis zu löschen, und das übernimmt ein
[trish@lillegroenn kurs]$ cvs update -P
(“purge” – “säubern”) für uns.
Nichts ist endgültig
Das Konzept also existiert nicht mehr. Doch da kommt die unerwartete Frage: “Haben Sie vielleicht ein Konzept für mich?” Wir nicht mehr, aber cvs. Alles, was wir tun müssen, ist, uns daran erinnern, zu welchem Zeitpunkt das Konzept ein Konzept war.
Glücklicherweise führt CVS penibel Buch. Dieses kann man sich mit cvs log auch anschauen. Das geht am besten, indem wir nicht alles an uns vorbeirauschen lassen, sondern durch less schicken:
[trish@lillegroenn kurs]$ cvs log | less
Mit dem less-Suchbefehl /konzept finden wir darin auch die Bemerkung zur Umbenennung wieder:
RCS file: /home/trish/cvs/linuxkurs/kursunterlagen/linuxkurs/folien/unixfoil.tex,v[…] —————————- revision 1.3 date: 2001/03/30 21:57:53; author: trish; state: Exp; lines: +1 -1 konzept.tex jetzt unixfoil.tex —————————-
Leider bekommen wir keinen Hinweis auf die vormalige Existenz der Datei konzept.tex selbst: Unsere Schuld… Hätten wir das leere konzept-Verzeichnis mit seinen Informationen im CVS-Unterverzeichnis nicht mit dem -P-Flag von update gelöscht, wären wir jetzt nicht so hilflos.
Im schlimmsten Fall muss man jetzt mit less und ls im Dateibaum des CVS-Depots wühlen, bis sich das passende Attic-File zu konzept.tex (z.B. ~/cvs/linuxkurs/kursunterlagen/linuxkurs/konzept/Attic/konzept.tex,v) findet. Auf jeden Fall stellen wir fest, dass konzept.tex am 30.03.2001 gegen 21:55 noch am Leben gewesen sein muss.
Passen wir also unseren Datenbestand an den an, der um diese Zeit aktuell war:
[trish@lillegroenn kurs]$ cvs update -D "2001-03-30 21:55:53" cvs update: Updating . cvs update: Updating folien U folien/unixfoil.tex cvs update: Updating skript U skript/unixkurs.tex
Leider ist konzept.tex da nicht mit dabei.
[trish@lillegroenn kurs]$ cvs update --help
update: invalid option – -
Usage: cvs update [-APdflRp] [-k kopt] [-r rev|-D date] [-j rev]
[-I ign] [-W spec] [files…]
-A Reset any sticky tags/date/kopts.
-P Prune empty directories.
-d Build directories, like checkout does.[…]
… war gar nicht mal so falsch: Obwohl es die Option --help nicht gibt, spuckt cvs genau das aus, was wir brauchen: eine Hilfe zum cvs-Befehl update. Die Option -d erscheint vielversprechend und lässt sich so interpretieren: Zwar aktualisiert update normalerweise nur bereits ausgecheckte Dateien, doch wenn man update -d sagt, ist es etwas kooperationswilliger und erzeugt auch Verzeichnisse, die im Arbeitsdatenverzeichnis fehlen.
[trish@lillegroenn kurs]$ cvs update -dD "2001-03-30 21:55:53" cvs update: Updating . cvs update: Updating folien cvs update: Updating konzept U konzept/konzept.tex cvs update: Updating skript
Jetzt haben wir konzept.tex wieder und können das Konzept ausdrucken. Damit Sie nicht erst das im Kasten “Mehr Dokumentation zu cvs” erwähnte CVS-Buch konsultieren müssen, verrate ich Ihnen jetzt noch, dass das ominöse -A-Flag zum update-Kommando die einzige Möglichkeit ist, um wieder an eine aktuelle Arbeitskopie ohne konzept.tex zu kommen:
[trish@lillegroenn kurs]$ cvs update -A cvs update: Updating . cvs update: Updating folien U folien/unixfoil.tex cvs update: Updating konzept cvs update: warning: konzept/konzept.tex is not (any longer) pertinent cvs update: Updating skript U skript/unixkurs.tex
Ob Sie mit -P jetzt noch das konzept-Verzeichnis löschen wollen, sei allerdings Ihnen überlassen.
Keinen Shellzugang?
Ein Account auf einem Rechner heißt noch lange nicht, dass er für sämtliche Kollegen und Freundinnen auch zum Stöbern im System, zum Nutzen von Internetdiensten oder zum Ablegen von Daten zu gebrauchen sein soll. Vielmehr gibt es viele gute Gründe, den Zugang auf den CVS-Zugriff zu beschränken, ohne auf die Sicherheit zu verzichten, die SecureShell bietet.
Alles, was die Administratorin des CVS-Servers dazu braucht, sind öffentliche SSH-Schlüssel der CVS-Nutzer/innen von deren Arbeitsrechnern. Ähnlich wie bei PGP erzeugen diese auf ihren Alltagsaccounts ein Schlüsselpaar, von denen der public key nach Belieben verteilt werden kann, der private key jedoch geheimgehalten werden muss. Dies geschieht mit dem den SSH-Paketen beigelegten Kommando ssh-keygen und wurde im Answer-Girl in Heft 04/2001 ausführlich besprochen.
ssh-keygen legt den geheimen privaten Schlüssel in der Voreinstellung unter ~/.ssh/identity ab. Dieser darf nicht weitergegeben werden! Das, was die CVS-Sysadmine gerne hätte, ist eine Textdatei namens ~/.ssh/identity.pub und sieht etwa so aus:
1024 35 165043668525388007503675301831634125912119991502526700029105958161542269846546772572229108798152992529758074045707003573273020044380873112356724249904219939995856241718046388628225862962791292865950083481899332539835181290112611354730215142417376960062146599043065554089684980963002106747241282736545822186999 bille@billabox.bille.de
Diese eine lange Zeile überträgt die Sysadmine in die Datei .ssh/authorized_keys im Homeverzeichnis des Users. billes öffentlicher Schlüssel kommt also in ~bille/.ssh/authorized_keys auf dem CVS-Server zu liegen.
Die Server-Admine hat in der Zwischenzeit ein wenig in der Manpage zum SecureShell-Server sshd gestöbert und ist unter der Rubrik AUTHORIZED_KEYS FILE FORMAT darauf gestoßen, dass sie am Anfang einer Schlüsselzeile ein Kommando angeben kann, das anstelle einer Loginshell immer dann ausgeführt wird, wenn die entsprechende Benutzerin versucht, sich mit dem passenden privaten Key über ssh einzuloggen.
Wenn bille also mit ihrem cvs-Request per ssh kommt, soll einfach der cvs-Server starten und lediglich auf das Depot in ~trish/cvs/linuxkurs/kursunterlagen Zugriff gewähren. Einen passenden cvs-Server startet man mit cvs server --allow-root=/home/trish/cvs/linuxkurs/kursunterlagen, womit die Sysadmine lediglich ein command="cvs server --allow-root=/home/trish/cvs/linuxkurs/kursunterlagen" an den Schlüsselzeilenanfang von billes Public-Key stellen muss:
command="cvs server --allow-root=/home/trish/cvs/linuxkurs/kursunterlagen" 1024 35 165043668525388007503675301831634125912119991502526700029105958161542269846546772572229108798152992529758074045707003573273020044380873112356724249904219939995856241718046388628225862962791292865950083481899332539835181290112611354730215142417376960062146599043065554089684980963002106747241282736545822186999 bille@billabox.bille.de
Wichtig dabei: Der gesamte Rattenschwanz muss auch weiterhin eine einzige Zeile bleiben.
Mehr Dokumentation zu
cvs
Wer an der mit cvs mitgelieferten Dokumentation schier verzweifelt, sollte nicht aufgeben: Unter http://cvsbook.red-bean.com/ findet sich der GPLte Teil von Karl Fogels CVS-Buch zum Onlinestöbern oder Downloaden. Da dieses Answer-Girl keinesfalls erschöpfende Auskunft zu cvs geben kann, empfiehlt es sich als weitergehende Lektüre für alle, die Blut geleckt haben.
Glossar
-
ASCII-Texte
-
Texte, deren Zeichen im “American Standard Code for Information Interchange” abgespeichert sind. Leider umfasst dieser Code in der 7-Bit-Fassung nicht einmal deutsche Umlaute, sondern nur die Zeichen, die auf einer amerikanischen Tastatur zu finden sind, sowie einige Steuercodes wie CR (“Carriage Return”) für den Wagenrücklauf (ursprünglich einer Schreibmaschine) oder LF (“Line Feed”) für einen Zeilenumbruch. In 8-Bit-ASCII können immerhin die meisten Sonderzeichen aus Sprachen mit lateinischen Alphabeten kodiert werden. Wer allerdings kyrillische oder hebräische Zeichen schreiben will, muss andere Kodierungen wie UTF8 benutzen. Die meisten hierzulande gängigen Texteditoren benutzen ASCII.
-
Pfad
-
Der “Weg” zu einer Datei vom Wurzelverzeichnis / aus. So führt der absolute Pfad zur Programmdatei /usr/sbin/groupadd über die Verzeichnisse /usr und sbin und lautet daher /usr/sbin. Relative Pfade gehen im Gegensatz dazu immer vom aktuellen Arbeitsverzeichnis aus.
-
SecureShell
-
Sicherer Ersatz für Internetdienste wie Telnet und RSH (“Remote Shell”), mit deren Hilfe man auf entfernten Rechnern so arbeiten kann, als säße man direkt davor. Die Datenübertragung wird dabei verschlüsselt. Das SecureShell-Paket bringt meist auch einen sicheren Ersatz für RCP (“Remote Copy”) namens scp mit.
-
tunneln
-
Nutzen eines Dienstes über eine Verbindung, die ein anderer Dienst aufbaut. So kann man z.B. CVS-Pakete in SSH-Paketen verpackt übertragen.
-
tex-Datei
-
Text-Datei, die aus Inhalt sowie TeX- oder LaTeX-Kommandos besteht, die die Struktur des Inhalts kennzeichnen. Mit Hilfe der Textsatz-Befehle tex oder latex wird daraus die tatsächliche Darstellung der druckfertigen Datei.
-
Loginshells
-
Der Kommandozeileninterpreter, den man nach dem Einloggen auf einer virtuellen Konsole zu Gesicht bekommt, in den meisten Linux-Fällen die “Bourne Again Shell” bash. Dass eine Bash Loginshell wird, wird mit der Option -login bestimmt, sodass man auch in X-Terminalprogrammen unter X11 Loginshells untergejubelt bekommen kann. Sollte ein echo $variablenname verraten, dass eine in ~/.bashrc gesetzte Variable in der aktuellen Shell nicht auftaucht, hat man es meist mit einer Loginshell zu tun, denn die schert sich bei der Bash nicht um die ~/.bashrc, sondern um ~/.bash_profile. Bleibt auch mit dort gesetzten Variablen der Erfolg aus (sofern es nicht daran liegt, dass sich beim Setzen Fehler eingeschlichen haben), wurde dieser vermutlich durch den Bash-Parameter -noprofile unterbunden. Hier hilft dann nur noch tieferes Graben im System oder das besprochene Sourcen der Variablen kurz vor Gebrauch.
-
Attic
-
In Verzeichnissen dieses Namens lagert CVS den Inhalt und die Geschichte aus dem Depot gelöschter Dateien.
-
PGP
-
“Pretty Good Privacy”, das wohl am weitesten verbreitete Programm zum Verschlüsseln und Signieren von E-Mails u.a. Daten.
