

	\documentclass[a4paper,11pt]{article}
	\usepackage{ngerman}
	\usepackage[latin1]{inputenc}
	\setlength\parskip{\medskipamount}
	\setlength\parindent{0pt}
	\begin{document}

	
 % CVS_fuer_Fortgeschrittene
 % Copyright 
 % Lizenz: GPL
 % 
 % $Name: $
 % $Revision: 1.1.2.5 $
 % $Source: /cvsroot/selflinux/tutorial/software/development/version_control/cvs/cvs_buch/kapitel_6/kapitel_6,v $
 % SelfLinux-0.7.2
 %
 % Diese Datei ist Teil von SelfLinux http://www.selflinux.de
 %
 %%% $Id: kapitel_6,v 1.1.2.5 2002/12/15 18:47:31 fboerner Exp $

	\title{CVS für Fortgeschrittene}


	
	    \author{}
	    %\url{mailto:}
    

	\maketitle

	
	
	%\ref{../index.tex}
	
		%\ref{programmierung.tex}
		Programmierung
		%\ref{cvs.tex}
		CVS
	\ref{CVS_fuer_Fortgeschrittene}

    \par{Layout}
    Johnny Graber
	    %\url{mailto:linux@jgraber.ch}
    
    	\par{Lizenz}
	GPL
 
	\tableofcontents{}

        
	\section{Über die Grundlagen hinaus} \label{d76e52}
        
    

    
  \par
  
Wir haben nun die grundlegenden Konzepte der Benutzung von CVS und der
Administration des Archivs behandelt und werden jetzt einen Blick
darauf werfen, wie CVS in den gesamten Entwicklungsvorgang integriert
werden kann. Der grundlegende CVS-Arbeitszyklus von Checkout
(auschecken), Update, Commit, Update, Commit und so weiter ist schon
anhand von Beispielen in Kapitel 2 illustriert worden. Im Folgenden
werden wir diesen Zyklus noch ausführlicher beschreiben. Außerdem
werden wir in diesem Kapitel untersuchen, wie CVS den Entwicklern
dabei helfen kann, miteinander zu kommunizieren, wie CVS einen
Überblick über die Projektaktivitäten und den Fortschritt des Projekts
liefern kann, wie es einen dabei unterstützt, Entwicklungszweige
(sog. Branches) abzuspalten und wieder mit der Hauptentwicklungslinie
(dem sog. Trunk, also Stamm oder Rumpf) zu vereinen, und schließlich,
wie man mit CVS häufig wiederkehrende Vorgänge automatisieren kann.
Für die Nutzung mancher dieser Eigenschaften werden wir neue
CVS-Kommandos einführen, bei vielen genügt die erweiterte Nutzung der
Kommandos, die Sie bereits kennen gelernt haben.
    
   \section{CVS als Telefon} \label{d76e62}
        
    

    
  \par
  
Ein Hauptvorteil bei der Benutzung von CVS für ein Projekt liegt
darin, dass es sowohl als Kommunikationsmittel als auch als
Protokollant dienen kann. Dieser Abschnitt konzentriert sich darauf,
wie CVS verwendet werden kann, um die Teilnehmer eines Projekts
darüber, was im Projekt vorgeht, auf dem Laufenden zu halten. Die
Teilnehmer müssen jedoch informiert werden wollen, denn wenn sich
jemand entschließt, diese Kommunikationsfähigkeiten nicht zu
verwenden, dann kann auch CVS nicht darüber hinweg helfen.
    
   \section{Watches: Wer arbeitet wann woran} \label{d76e75}
        
    

    
  \par
  
Normalerweise behandelt CVS jede Arbeitskopie als isolierte
»Sandbox1«. Niemand bekommt mit, was Sie an Ihrer Kopie bearbeiten,
solange bis Sie Ihre Änderungen durch einen Commit freigeben.
Umgekehrt wissen Sie nicht, was »die anderen« an ihrer Kopie tun, von
den herkömmlichen Kommunikationsmitteln, wie »Hey, ich werde jetzt an
parse.c arbeiten, sagt mir, wenn ihr etwas daran machen wollt, damit
wir Konflikte vermeiden können!« den Gang runter zu brüllen, einmal
abgesehen.
    

    
  \par
  
Dieses formlose Vorgehen mag für Projekte ausreichen, bei denen jeder
eine grobe Ahnung davon hat, wer wofür zuständig ist. Hingegen wird
diese Vorgehensweise scheitern, wenn eine große Entwicklergruppe an
allen möglichen Teilen des Projekts aktiv ist und Konflikte vermieden
werden sollen. In solchen Fällen überschneiden sich die
Zuständigkeiten, man kann jedoch nichts »den Gang runter brüllen«, da
man über die ganze Welt verteilt arbeitet.
    

    
  \par
  
Eine »Watch2« genannte CVS-Funktion gibt den Entwicklern ein Mittel in
die Hand, um andere darüber zu informieren, wer gerade an welchen
Dateien arbeitet. Indem er ein Watch auf eine Datei setzt, kann ein
Entwickler CVS veranlassen, ihn zu benachrichtigen, sobald ein anderer
beginnt, ebenfalls an ihr zu arbeiten. Diese Benachrichtigungen
werden normalerweise als E-Mail versendet, man kann aber auch andere
Benachrichtigungsmethoden einrichten.
    

    
  \par
  
Um Watches verwenden zu können, muss man ein oder zwei Dateien im
Administrationsteil des Archivs anpassen. Weiterhin ist es
erforderlich, dass die Entwickler einige Zusatzschritte in den
Checkout/Update/Commit-Zyklus einfügen. Die auf Archivseite nötigen
Änderungen sind recht einfach: Man muss CVSROOT/notify so abändern,
dass CVS weiß, wie Benachrichtigungen durchgeführt werden sollen.
Eventuell muss man auch einige Zeilen in CVSROOT/users einfügen, um
die externen E-Mail-Adressen festzulegen.
    

    
  \par
  
In ihrer Arbeitskopie müssen die Entwickler CVS mitteilen, welche
Dateien beobachtet werden sollen, damit CVS ihnen eine
Benachrichtigung schicken kann, wenn jemand anders an diesen Dateien
zu arbeiten beginnt. Die Entwickler müssen außerdem CVS informieren,
wenn sie damit anfangen oder aufhören, an einer Datei zu arbeiten,
damit CVS es denjenigen mitteilen kann, die ein Watch gesetzt haben.
Folgende Kommandos sind für diese Zusatzschritte zuständig:
    

    
     {\bf cvs watch} \linebreak 
     {\bf cvs edit} \linebreak 
     {\bf cvs unedit} \linebreak 
    



     {\bf Bemerkung} \linebreak 

    
  \par
  
Das Kommando {\bf watch} unterscheidet sich von den normalen CVS-Kommandos
dadurch, dass es zusätzliche Unterkommandos benötigt, wie etwa {\bf cvs
watch add...}, {\bf cvs watch remove...}, und so weiter.
    
    
    
    
  \par
  
Im Folgenden werfen wir einen Blick darauf, wie man Watches im Archiv
aktiviert, und darauf, wie Watches aus Entwicklersicht eingesetzt
werden. Zwei Beispielnutzer, jrandom und qsmith, haben beide ihre
eigene Arbeitskopie desselben Projektes, sie können sogar an
verschiedenen Computern arbeiten. Wie üblich wird in allen Beispielen
davon ausgegangen, dass die Umgebungsvariable {\bf \$CVSROOT} bereits gesetzt
wurde, sodass es bei keinem CVS-Kommando nötig ist, {\bf -d \verb+<+ARCHIV\verb+>+} mit
anzugeben.
    

    \subsection{Watches im Archiv aktivieren} \label{d76e139}
        
     

     
  \par
  
Zunächst muss CVSROOT/notify angepasst werden, um
E-Mail-Benachrichtigungen einzuschalten. Das kann von einem Entwickler
erledigt werden, der Administrator muss es nur dann selbst erledigen,
wenn kein Entwickler die Berechtigung hat, die Verwaltungsdateien zu
verändern. In beiden Fällen ist zuerst der Administrationsbereich
auszuchecken und die Datei {\bf notify} zu editieren:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q co CVSROOT
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U CVSROOT/checkoutlist\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/commitinfo\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/config\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/cvswrappers\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/editinfo\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/loginfo\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/modules\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/notify\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/rcsinfo\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/taginfo\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U CVSROOT/verifymsg\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd CVSROOT
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
emacs notify
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
...
       \end{scriptsize} \end{tt} \linebreak 
     
       
      
      
  \par
  
Falls Sie notify zum ersten Mal öffnen, werden Sie in etwa Folgendes sehen:
     

     \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        Datei notify\end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
# The "notify" file controls where notifications from watches set by
# "cvs watch add" or "cvs edit" are sent. The first entry on a line is
# a regular expression which is tested against the directory that the
# change is being made to, relative to the &#36;CVSROOT. If it matches,
# then the remainder of the line is a filter program that should contain
# one occurrence of %s for the user to notify, and information on its
# standard input.
#
# "ALL" or "DEFAULT" can be used in place of the regular expression.
#
# For example:
# ALL mail %s -s "CVS notification"
      \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

     
  \par
  
Es muss lediglich die letzte Zeile einkommentiert werden, indem das
führende Doppelkreuz (\#) entfernt wird. Obwohl die Datei notify
dieselbe flexible Schnittstelle (reguläre Ausdrücke für die
Verzeichnisnamen) wie die anderen administrativen Dateien bietet,
werden Sie diese Flexibilität hier wohl nie brauchen. Der einzig
vorstellbare Grund, mehrere Zeilen mit jeweils einem regulären
Ausdruck für einen bestimmten Teil des Archivs haben zu wollen, wäre,
wenn Sie unterschiedliche Benachrichtigungsarten für die
verschiedenen Projekte haben möchten. Wie auch immer, eine normale
E-Mail ist ein völlig ausreichender Benachrichtigungsmechanismus, er
wird für die meisten Projekte verwendet.
     

     
  \par
  
Um die E-Mail-Benachrichtigung einzuschalten, wird die Zeile
     

     
  \par
  
ALL mail %s -s ''CVS notification''
     

     
  \par
  
wohl auf jedem normalen UNIX-Rechner funktionieren. Die Anweisung
bewirkt, dass Benachrichtigungen als E-Mail versendet werden, mit »CVS
notification« als Betreff, wobei der Ausdruck {\bf ALL} wie üblich für
alle Verzeichnisse steht. Wenn Sie diese Zeile einkommentiert haben,
sollten Sie die Datei notify durch einen Commit dem Archiv zukommen
lassen, damit es die Änderung bemerkt5:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''turned on watch notification''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in notify;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/CVSROOT/notify,v \verb+<+-- notify\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Rebuilding administrative file database\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
floss\$
       \end{scriptsize} \end{tt} \linebreak 
     


     
  \par
  
Die Anpassung der Datei notify kann auch schon alles sein, was im
Archiv zum Aktivieren der Watches getan werden muss. Wenn jedoch auch
Entwickler an anderen Rechnern am Projekt teilnehmen, dann muss
wahrscheinlich auch noch die Datei CVSROOT/users angepasst werden. Die
Aufgabe der Datei users liegt darin, CVS mitzuteilen, wohin die
E-Mail-Benachrichtigungen für diejenigen Benutzer gesendet werden
müssen, die keine lokale E-Mail-Adresse haben. Das Format jeder Zeile
der Datei users:
     

     
  \par
  
CVS\_BENUTZERNAME:EMAILADRESSE
     

     
	     Zum Beispiel: \linebreak 
qsmith:quentinsmith@ganzweitweg.com
     

     
  \par
  
Der CVS-Benutzername am Anfang der Zeile korrespondiert mit einem
CVS-Benutzernamen in CVSROOT/password (falls vorhanden und falls die
Zugriffsmethode pserver verwendet wird). Ansonsten ist es der
Benutzername auf Serverseite der Person, die CVS verwendet. Dem
Doppelpunkt folgt die externe E-Mail-Adresse, an die CVS die
Watch-Benachrichtigungen für diesen Benutzer schicken soll.
     

     
  \par
  
Unglücklicherweise existiert die Datei users (zum Zeitpunkt des
Schreibens) nicht in der CVS-Standarddistribution. Weil es sich um
eine administrative Datei handelt, genügt es nicht, sie zu erzeugen,
sie mit {\bf cvs add} dem Archiv hinzuzufügen und einen Commit auszuführen,
sondern man muss sie auch noch in die CVSROOT/checkoutlist eintragen,
damit immer eine ausgecheckte Kopie im Archiv vorliegt.
     

     
  \par
  
Hier eine Beispielssitzung, die diesen Vorgang illustriert:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ emacs checkoutlist
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ... (die Zeile für die Datei users hinzufügen) ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
floss\$ emacs users
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ... (die Zeile für qsmith hinzufügen) ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
floss\$ cvs add users
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs ci -m ''added users to checkoutlist, qsmith to users''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in checkoutlist;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/CVSROOT/checkoutlist,v \verb+<+-- checkoutlist\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in users;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/CVSROOT/users,v \verb+<+-- users\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Rebuilding administrative file database\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
 
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Es ist durchaus möglich, das erweiterte Format für die E-Mail-Adresse
in CVSROOT/users zu verwenden, man muss nur darauf achten, dass alle
Leerzeichen durch Anführungszeichen gekapselt werden. Zum Beispiel
ist Folgendes möglich:
     

     
  \par
  
qsmith:''Quentin Q. Smith \verb+<+quentinsmith@ganzweitweg.com\verb+>+''
     

     
  \par
  
oder auch:
     

     
  \par
  
qsmith:'Quentin Q. Smith \verb+<+quentinsmith@ganzweitweg.com\verb+>+'
     

     
  \par
  
So geht es allerdings nicht:
     

     
  \par
  
qsmith:''Quentin Q. Smith''  \verb+<+quentinsmith@ganzweitweg.com\verb+>+
     

     
  \par
  
Probieren Sie im Zweifelsfall das Kommando, wie es in der Datei notify
angegeben ist, manuell aus. Es muss lediglich das %s in
     

     
  \par
  
mail %s -s ''CVS notification''
     

     
  \par
  
durch das, was in der Datei users nach dem Doppelpunkt folgt, ersetzt
werden. Wenn es auf der Kommandozeile funktioniert, dann sollte es
auch in der Datei users funktionieren.
     

     
  \par
  
Die Datei checkout sollte jetzt in etwa so aussehen6:
     


     \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        Datei checkout\end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
# The "checkoutlist" file is used to support additional version controlled
# administrative files in &#36;CVSROOT/CVSROOT, such as template files.
#
# The first entry on a line is a filename which will be checked out from
# the corresponding RCS file in the &#36;CVSROOT/CVSROOT directory.
# The remainder of the line is an error message to use if the file cannot
# be checked out.
#
# File format:
#
# [&lt;whitespace>]&lt;filename&gt;&lt;whitespace&gt;&lt;error message&gt;&lt;end-of-line&gt;
#
# comment lines begin with '#'
users Unable to check out 'users' file in CVSROOT.
      \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

     
  \par
  
Und die Datei users sieht in etwa so aus:
     

     \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        Datei users\end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
qsmith:quentinsmith@ganzweitweg.com
      \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

     
  \par
  
Das Archiv ist nun für Watches vorbereitet. Werfen wir einen Blick
darauf, was die Entwickler an ihren Arbeitskopien machen müssen.
     

     {\bf 
Watches bei der Entwicklung verwenden
     }

     
  \par
  
Zuerst wird ein Entwickler eine Arbeitskopie auschecken und sich
selbst auf die Liste der Beobachter (»Watch List«) einer Datei des
Projekts setzen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$      
whoami
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} jrandom\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q co myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux myproj/ \$ 
cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux myproj/ \$ 
 
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Das letzte Kommando, {\bf cvs watch add hello.c}, teilt CVS mit, dass
jrandom benachrichtigt werden soll, wenn jemand beginnt, an hello.c zu
arbeiten. Der Benutzer jrandom wird also der Liste der Beobachter
hinzugefügt. Damit CVS Benachrichtigungen versenden kann, sobald eine
Datei bearbeitet wird, muss der sie bearbeitende Benutzer das CVS
mitteilen, indem er zunächst {\bf cvs edit} für die Datei aufruft. CVS hat
keine andere Möglichkeit, festzustellen, dass jemand eine Datei zu
bearbeiten beginnt. Sobald der Checkout durchgeführt wurde, ist CVS
normalerweise bis zum nächsten Update oder Commit nicht mehr
beteiligt. Letzteres geschieht aber erst, nachdem die Datei schon
bearbeitet wurde:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
whoami
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} qsmith\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q co myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
paste\$ cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ cvs edit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ emacs hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ...\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Wenn qsmith {\bf cvs edit hello.c} aufruft, schaut sich CVS die Watch-Liste
für hello.c an, sieht, dass jrandom darauf vertreten ist, und schickt
jrandom eine E-Mail, die ihm sagt, dass qsmith damit begonnen hat,
die Datei zu bearbeiten. Die E-Mail hat sogar den Anschein, als käme
sie von qsmith:
     

     \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        Email an jrandom\end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
From: qsmith
Subject: CVS notification
To: jrandom
Date: Sat, 17 Jul 1999 22:14:43 -0500
myproj hello.c
--
Triggered edit watch on /usr/local/newrepos/myproj
By qsmith
      \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}
     
     
  \par
  
Außerdem wird jrandom jedes Mal, wenn qsmith (oder sonst jemand) den
Commit einer neuen Revision von hello.c ausführt, eine weitere E-Mail
erhalten:
     

     \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        Email commit\end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
myproj hello.c
--
Triggered commit watch on /usr/local/newrepos/myproj
By qsmith
      \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

     
  \par
  
Nach Erhalt dieser E-Mails wird sich jrandom sofort ein Update von
hello.c holen wollen, damit sie sehen kann, was qsmith geändert hat;
möglicherweise wird sie qsmith eine E-Mail schreiben, um
herauszufinden, warum er an der Datei Änderungen vorgenommen hat. Es
bleibt zu beachten, dass niemand qsmith gezwungen hat, {\bf cvs edit}
auszuführen, vermutlich wollte er also, dass jrandom erfährt, was er
vorhatte. Andererseits: Selbst wenn er {\bf cvs edit} vergessen hätte,
würde sein Commit dennoch das Verschicken von Benachrichtigungen
auslösen. Der Sinn, {\bf cvs edit} zu benutzen, liegt darin, dass
Beobachtende benachrichtigt werden, bevor man die Arbeit an einer
Datei aufnimmt. Die Beobachtenden können einen dann informieren, wenn
sie einen Konflikt kommen sehen.
     

     
  \par
  
CVS geht davon aus, dass jeder, der {\bf cvs edit} auf eine Datei anwendet,
selbst - zumindest temporär - auf die Liste der Beobachter der Datei
gesetzt werden will, für den Fall, dass jemand anders beginnt,
Änderungen vorzunehmen. Als qsmith {\bf cvs edit} aufgerufen hat, ist er ein
Beobachter von hello.c geworden. Sowohl er als auch jrandom wären
benachrichtigt worden, hätte ein Dritter {\bf cvs edit} auf die Datei
angewendet oder den Commit einer neuen Revision vorgenommen.
     

     
  \par
  
Allerdings glaubt CVS, dass Personen, die eine Datei bearbeiten, nur
so lange auf der Beobachtungsliste sein möchten, wie sie daran
arbeiten. Benutzer dieser Art werden automatisch von der
Beobachtungsliste genommen, wenn sie mit ihren Änderungen fertig sind.
Sollten sie es wünschen, dauerhafte Beobachter der Datei zu werden,
müssten sie cvs add watch aufrufen. CVS unterstellt, dass jemand mit
seiner Arbeit an einer Datei fertig ist, sobald er den Commit der
Datei ausgeführt hat - jedenfalls bis zum nächsten Mal.
     

     
  \par
  
Jeder, der auf die Beobachtungsliste einer Datei nur dadurch gelangt,
dass er {\bf cvs edit} aufruft, wird als temporärer Beobachter geführt und
automatisch von der Beobachtungsliste genommen, sobald er die
Änderungen an der Datei per Commit abgeschlossen hat. Wenn er danach
wieder etwas bearbeiten möchte, muss er {\bf cvs edit} noch einmal
aufrufen.
     

     
  \par
  
Die Annahme, dass der erstbeste Commit die Arbeitssitzung an der Datei
beendet, ist natürlich nur eine Vermutung, denn CVS kann ja nicht
wissen, wie viele Commits jemand benötigt, um seine Änderungen
abzuschließen. Die Vermutung trifft am wahrscheinlichsten bei so
genannten »One-off7«-Korrekturen zu, Änderungen, bei denen jemand nur
schnell eine Kleinigkeit korrigieren möchte und den Commit gleich
durchführt. Für längere Arbeitssitzungen an einer Datei, die mehrere
Commits umfassen, sollten die Benutzer sich dauerhaft auf die
Beobachtungsliste der Datei setzen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs edit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ emacs hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
paste\$ cvs commit -m ''print hello in Sanskrit''
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Nun bleibt qsmith auch nach einem Commit auf der Beobachtungsliste von
hello.c, da er {\bf watch add} ausgeführt hat. Übrigens wird qsmith keine
Benachrichtigung seiner eigenen Änderungen erhalten, die bekommen nur
andere Beobachter. CVS ist klug genug, einen nicht über eigene
Änderungen zu informieren.
     

     
  \par
  
Eine Bearbeitungssitzung abschließen
     

     
  \par
  
Wenn Sie kein Commit durchführen wollen, die Bearbeitungssitzung aber
explizit beenden wollen, können Sie das mit {\bf cvs unedit} tun:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs unedit hello.c
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Doch Vorsicht! Dadurch wird nicht nur allen Beobachtenden gemeldet,
dass man mit dem Bearbeiten fertig ist, zusätzlich wird Ihnen noch
angeboten, alle Änderungen an der Datei, die Sie noch nicht durch
einen Commit bestätigt haben, rückgängig zu machen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ cvs unedit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} hello.c has been modified; revert changes? y\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
	       
       \end{scriptsize} \end{tt} \linebreak 
     
	      
	      
     
  \par
  
Wenn Sie hier mit »y« (für »yes«) antworten, wird CVS all Ihre
Änderungen rückgängig machen und allen Beobachtenden mitteilen, dass
Sie die Datei nicht mehr bearbeiten. Wenn Sie mit »n« (»no«)
antworten, behält CVS Ihre Änderungen bei und vermerkt Sie weiterhin
als Bearbeiter der Datei. (Es wird also keine Mitteilung verschickt -
alles verhält sich so, als hätten Sie {\bf cvs unedit} nie aufgerufen.) Es
mag ein wenig besorgniserregend erscheinen, dass CVS anbietet, mit
einem einzigen Tastendruck alle Änderungen zu verwerfen; die Logik
dahinter ist allerdings einleuchtend: Wenn Sie »der Welt« mitteilen,
dass Sie eine Bearbeitungssitzung abschließen, dann sind die
Änderungen, die Sie noch nicht mit einem Commit bestätigt haben,
vermutlich solche, die Sie gar nicht beibehalten wollen. CVS sieht es
jedenfalls so. Seien Sie also vorsichtig.
     

     
  \par
  
      {\bf 
Kontrolle über die zu beobachtenden Aktionen
      }
     

     
  \par
  
Beobachter werden normalerweise über drei Aktionsarten informiert: das
Bearbeiten einer Datei (edits), den Commit und das Ende der Arbeiten
an einer Datei (unedits). Falls Sie jedoch beispielsweise nur über
Commits benachrichtigt werden wollen, können Sie die
Benachrichtigungen auch mit der {\bf -a}-Option einschränken (a für
Aktion):
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch add -a commit hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Wenn Sie jedoch sowohl das Editieren als auch den Commit einer Datei
beobachten wollen, können Sie die {\bf -a}-Option auch zwei Mal angeben:
     
     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch add -a edit -a commit hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Wenn Sie eine Watch zusammen mit der {\bf -a}-Option setzen, werden schon
existierende Watches dadurch nicht entfernt. Wenn Sie schon alle drei
Aktionen auf hello.c beobachten, hat der Aufruf
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch add -a commit hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
keinen Effekt - Sie sind weiterhin Beobachter aller drei Aktionen. Um
Watches zu entfernen, sollten Sie
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch remove hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
aufrufen, was, wie {\bf add}, normalerweise alle drei Aktionen von der
Beobachtung ausschließt. Falls Sie {\bf -a}-Argumente übergeben, werden nur
die Watches entfernt, die Sie angeben:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs watch remove -a commit hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Das bedeutet, dass Sie keine weitere Benachrichtigung über ein Commit
bekommen möchten, aber weiterhin über Beginn und Ende des Editierens
informiert werden möchten - vorausgesetzt, Sie beobachteten diese
schon vorher.
     

     
  \par
  
Es gibt zwei spezielle Aktionen, die Sie zusammen mit der -a-Option
übergeben können: {\bf all} für alle oder {\bf none} für keine. Da es das
vorgegebene Verhalten von CVS ist, wenn {\bf -a} nicht mit angegeben wird,
alle Aktionen zu beobachten, und da {\bf none}, also keine Aktionen zu
beobachten, dasselbe ist, wie sich ganz von der Beobachtungsliste
herunterzunehmen, ist eine Situation, in der Sie eine dieser
Sonderoptionen mit angeben wollen, nur schwer vorzustellen.
Andererseits ist die {\bf -a}-Option auch für {\bf edit} verwendbar, und in diesem
Fall kann es von Nutzen sein, {\bf all} oder {\bf none} anzugeben. Zum Beispiel
könnte jemand, der nur sehr kurz an einer Datei arbeitet, keine
Benachrichtigungen darüber wünschen, was andere an der Datei ändern.
So bewirkt das Kommando
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
whoami
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} qsmith\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs edit -a none README.txt
       \end{scriptsize} \end{tt} \linebreak 
     


     
  \par
  
dass die Beobachter der Datei README.txt darüber benachrichtigt
werden, dass qsmith drauf und dran ist, sie zu bearbeiten, qsmith
selbst würde aber nicht als kurzzeitiger Beobachter geführt werden
(was normalerweise der Fall wäre), da er darum gebeten hat, keine
Aktionen zu beobachten.
     

     
  \par
  
Beachten Sie, dass Sie nur die eigenen Beobachtungen mit dem {\bf cvs
watch}-Kommando beeinflussen können. Sie können aufhören, eine Datei zu
beobachten, aber Sie können fremde Watches nicht ändern.
     

     
  \par
  
      {\bf 
Wie man herausfindet, wer was beobachtet
      }      
     

     
  \par
  
Manchmal kann es nützlich sein, zu überprüfen, wer eine Datei
beobachtet, bevor man {\bf cvs edit} aufruft, oder man möchte einfach sehen,
wer was unter Beobachung hat, ohne sich selbst auf eine
Beobachtungsliste zu setzen. Oder man hat einfach vergessen, wie denn
der eigene Status nun genau ist. Wenn man einige Watches gesetzt und
wieder zurückgesetzt hat und einige Dateien per Commit zurückgegeben
hat, kann man sehr leicht den Überblick darüber verlieren, was man
beobachtet und bearbeitet.
     

     
  \par
  
CVS bietet zwei Kommandos, mit denen man anzeigen kann, wer Dateien
beobachtet und wer Dateien unter Bearbeitung hat: {\bf cvs watchers} und 
{\bf cvs editors}.
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$      
whoami
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} jrandom\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} hello.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch remove -a unedit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} hello.c jrandom edit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch add README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} README.txt jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} hello.c jrandom edit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
 
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Beachten Sie, dass beim letzten {\bf cvs watchers}-Kommando keine Dateien
angegeben sind. Darum werden die Beobachter aller Dateien angezeigt -
und natürlich nur die Dateien, die Beobachter haben.
     

     
  \par
  
Dieses Verhalten haben die {\bf watch}- und {\bf edit}-Kommandos
mit anderen
CVS-Kommandos gemeinsam. Wenn Dateinamen mit angegeben werden, wirken
sie sich auf diese Dateien aus. Geben Sie Verzeichnisnamen an, sind
alle Dateien in den Verzeichnissen und in den darin liegenden
Unterverzeichnissen gemeint. Wird gar nichts spezifiziert, arbeiten
die Kommandos auf dem aktuellen Verzeichnis und auf allem darunter,
auf allen darunter liegenden Ebenen. Zum Beispiel (als Fortsetzung
derselben Sitzung):
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs watch add a-subdir/whatever.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} README.txt jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} hello.c jrandom edit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/whatever.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch add
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} README.txt jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} foo.gif jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} hello.c jrandom edit commit unedit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/whatever.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/subsubdir/fish.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} b-subdir/random.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
	     
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Durch die letzten beiden Kommandos wurde jrandom Beobachterin aller
Dateien des Projekts und hat dann die Beobachtungsliste für alle
Dateien im Projekt abgerufen. Die Ausgabe von {\bf cvs watchers} passt
nicht immer exakt in die Spalten, da CVS Tabulatoren mit Informationen
variabler Länge vermischt, doch das Format ist einheitlich:
     

     
  \par
  
{\bf [DATEINAME] [leerzeichen] BEOBACHTER [leerzeichen] BEOBACHTETE\_AKTION ...}
     

     
  \par
  
Folgendes passiert, wenn qsmith eine dieser Dateien editiert:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$      
cvs edit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} README.txt jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} foo.gif jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} hello.c jrandom edit commit unedit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} qsmith tedit tunedit tcommit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/whatever.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/subsubdir/fish.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} b-subdir/random.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
     
     
     
  \par
  
Bei der Datei hello.c ist ein weiterer Beobachter hinzugekommen:
qsmith selbst. Beachten Sie, dass der Dateiname am Anfang der Zeile
nicht wiederholt wird, sondern durch Leerzeichen ersetzt wird -
falls Sie jemals ein Programm schreiben, das die Ausgabe von {\bf watchers}
einliest, kann das wichtig sein. Da er hello.c bearbeitet, ist qsmith
temporärer Beobachter der Datei - so lange, bis er eine neue Revision
von hello.c per Commit erzeugt. Das {\bf t} vor jeder der Aktionen zeigt an,
dass es sich nur um temporäre Watches handelt. Wenn sich qsmith nun
als normaler Beobachter von hello.c hinzufügt:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
paste\$ cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} README.txt jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} foo.gif jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} hello.c jrandom edit commit unedit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} qsmith tedit tunedit tcommit edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/whatever.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} a-subdir/subsubdir/fish.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} b-subdir/random.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
dann wird er sowohl als temporärer als auch als permanenter Beobachter
aufgelistet. Man könnte erwarten, dass der permanente Status den
temporären einfach überschreibt, sodass die Zeile folgendermaßen
aussähe:
     

     
  \par
  
{\bf qsmith edit unedit commit}
     

     
  \par
  
CVS kann aber nicht einfach den temporären Status ersetzen, da es
nicht weiß, in welcher Reihenfolge die Aktionen ablaufen: Wird qsmith
sich von der permanenten Beobachtungsliste entfernen, bevor er die
Bearbeitungssitzung beendet, oder wird er die Änderungen abschließen
und trotzdem Beobachter bleiben? Im ersten Fall würden die
edit/unedit/commit-Aktionen verschwinden, die tedit/tunedit/tcommit
würden aber bleiben. Im zweiten Fall wäre es umgekehrt.
     

     
  \par
  
Wie auch immer - dieser Aspekt der Beobachtungslisten ist
normalerweise nicht von großem Interesse. Es genügt,
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 	     
floss\$ cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
     
     
  \par
  
oder
     
     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs editors
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
von der obersten Verzeichnisebene des Projekts aus aufzurufen, um zu
sehen, wer was tut. Man braucht nicht zu wissen, wen welche Aktionen
kümmern, wichtig sind die Namen der Personen und der Dateien.
     

     
  \par
  
{\bf Benutzer an Watches erinnern}
     

     
  \par
  
Sie haben es wahrscheinlich schon bemerkt: Die Beobachtungs-Features
sind völlig von der Kooperation aller Entwickler abhängig. Wenn jemand
einfach eine Datei ändert, ohne zuvor {\bf cvs edit} aufzurufen, wird es
niemand mitbekommen, bis die Änderungen mit Commit beendet werden. Da
{\bf cvs edit} einen zusätzlichen, nicht routinemäßigen Schritt darstellt,
wird er leicht vergessen.
     

     
  \par
  
Obwohl CVS niemanden zwingen kann, {\bf cvs edit} zu verwenden, hat es
dennoch einen Mechanismus, um die Leute wenigstens daran zu erinnern:
das Kommando {\bf watch on}:
     
     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q co myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch on hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
	      
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Durch den Aufruf von {\bf cvs watch on hello.c} bewirkt jrandom, dass
zukünftige Checkouts von myproj die Datei hello.c in der Arbeitskopie
mit dem Nur-lese-Status erzeugen. Versucht nun qsmith, daran zu
arbeiten, wird er feststellen, dass die Datei nur lesbar ist, und wird
so daran erinnert, zuerst {\bf cvs edit} aufzurufen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q co myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls -l
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} total 6\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} drwxr-xr-x 2 qsmith users 1024 Jul 19 01:06 CVS/\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -rw-r--r-- 1 qsmith users 38 Jul 12 11:28 README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} drwxr-xr-x 4 qsmith users 1024 Jul 19 01:06 a-subdir/\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} drwxr-xr-x 3 qsmith users 1024 Jul 19 01:06 b-subdir/\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -rw-r--r-- 1 qsmith users 673 Jun 20 22:47 foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -r--r--r-- 1 qsmith users 188 Jul 18 01:20 hello.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
	      
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Sobald er das getan hat, wird die Datei auch beschreibbar werden; er
kann sie nun editieren. Wenn er die Änderungen mittels Commit
abschickt, wird sie wieder nur lesbar:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs edit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls -l hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} -rw-r--r-- 1 qsmith users 188 Jul 18 01:20 hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} emacs hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs commit -m ''say hello in Aramaic'' hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in hello.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/hello.c,v \verb+<+-- hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.12; previous revision: 1.11\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls -l hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} -r--r--r-- 1 qsmith users 210 Jul 19 01:12 hello.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
	      
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Sein {\bf edit} und sein {\bf commit} wird 
Benachrichtigungen an alle Beobachter
der Datei hello.c versenden. Beachten Sie, dass jrandom nicht
notwendigerweise selbst Beobachter der Datei ist. Durch den Aufruf von
{\bf cvs watch on hello.c} hat sich jrandom nicht selbst auf die
Beobachtungsliste gesetzt, sie hat nur erwirkt, dass hello.c beim
Checkout den Nur-lese-Status erhält. Jeder, der eine Datei beobachten
will, muss selbst daran denken, sich auf die Beobachtungsliste zu
setzen, dabei kann ihm CVS nicht helfen.
     

     
  \par
  
Es mag eher die Ausnahme sein, die Beobachtung einer einzelnen Datei
einzuschalten. Normalerweise werden Watches für das gesamte Projekt
eingeschaltet:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$      
cvs -q co myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch on
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
	      
       \end{scriptsize} \end{tt} \linebreak 
     


     
  \par
  
Diese Aktion kommt der Ankündigung einer Grundsatzentscheidung für das
gesamte Projekt gleich: »Bitte verwenden Sie {\bf cvs edit}, um Beobachtern
mitzuteilen, woran Sie gerade arbeiten, und beobachten Sie ruhig jede
Datei, die Sie interessiert oder für die Sie sich verantwortlich
fühlen.« Jede Datei des Projekts erhält nun beim Checkout den
Nur-lese-Status, auf dass sich die Leute daran erinnern mögen, dass
sie {\bf cvs edit} aufzurufen haben, bevor sie irgendwelche Arbeiten daran
durchführen.
     

     
  \par
  
Obwohl beobachtete Dateien beim Checkout wieder nur lesbar werden,
geschieht dies durch Updates seltsamerweise nicht. Hätte qsmith den
Checkout seiner Arbeitskopie ausgeführt, bevor jrandom {\bf cvs watch on}
aufgerufen hat, blieben seine Dateien schreibbar, selbst nach einem
Update. Jedoch werden alle Dateien, die er mit einem Commit dem
Archiv zukommen lässt, nachdem jrandrom die Watches eingeschaltet hat,
nur lesbar. Schaltet jrandom die Watches ab
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs watch off
       \end{scriptsize} \end{tt} \linebreak 
     
	   
     
  \par
  
werden qsmiths Dateien nicht von Geisterhand wieder beschreibbar.
Andererseits werden sie auch nicht nach seinen Commits wieder nur
lesbar, was der Fall wäre, wären die Watches noch eingeschaltet.
     

     
  \par
  
Es bleibt zu beachten, dass qsmith, wenn er richtig hinterhältig wäre,
die Dateien seiner Arbeitskopie auch mit dem normalen Unix-Kommando
{\bf chmod} beschreibbar machen und so {\bf cvs edit} völlig umgehen könnte:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
chmod u+w hello.c
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
oder, wenn er alles auf einen Schlag erledigen wollte:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ chmod -R u+w .
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
CVS kann nichts dagegen tun. Die Arbeitskopien sind - bedingt durch
ihre Natur - private »Sandkästen'', durch die Beobachtungs-Features
kann man sie ein klein wenig unter öffentliche Beobachtung stellen,
jedoch nur so weit, wie es der Entwickler erlaubt. Nur wenn ein
Entwickler etwas ausführt, das das Archiv berührt, wie zum Beispiel
ein Commit, gibt er seine Privatsphäre ohne Wenn und Aber auf.
     

     
  \par
  
Das Verhältnis zwischen {\bf watch add, watch remove, watch on} und {\bf watch
off} mag leicht verwirrend erscheinen. Vielleicht hilft es, die
Systematik noch einmal zusammenzufassen: {\bf add} und {\bf remove} sind dafür
da, um Benutzer auf die Beobachtungsliste einer Datei zu setzen oder
sie davon zu entfernen; sie haben nichts damit zu tun, ob Dateien
beim Checkout das Nur-lese-Attribut erhalten oder ob sie nach einem
Commit (wieder) nur lesbar werden. Bei {\bf on} und {\bf off} geht es nur um
Dateirechte. Sie haben nichts damit zu tun, wer auf einer
Beobachtungsliste ist, es geht lediglich darum, die Entwickler daran
zu erinnern, sich an die Beobachtungsübereinkunft zu halten, indem
die Dateien der Arbeitskopie zunächst nur die Leseberechtigung
erhalten.
     

     
  \par
  
Vielleicht wirkt das Ganze ja auch ein wenig inkonsistent. Schließlich
laufen Watches dem Grundkonzept von CVS entgegen. Es ist eine
Teilabkehr von dem idealisierten Universum, in dem viele Entwickler
völlig frei an ihren Arbeitskopien arbeiten, unbemerkt von den
anderen, bis sie sich entschließen, etwas per Commit zu
veröffentlichen. Mit Watches gibt CVS den Entwicklern eine bequeme
Methode, die anderen darüber, was in der eigenen Arbeitskopie vor
sich geht, zu informieren; allerdings ohne die Möglichkeit, das für
die Arbeit mit Watches richtige Verhalten zu erzwingen. Auch gibt es
kein festgelegtes Konzept davon, woraus denn eine Arbeitssitzung nun
genau besteht. Nichtsdestotrotz können Watches unter gewissen
Umständen nützlich sein, wenn die Entwickler sie verwenden.
     

     
  \par
  
      {\bf 
Wie Watches im Archiv aussehen
      }
     

     
  \par
  
Damit mal wieder ein unnötiges Mysterium ausgerottet wird, werfen wir
einen kurzen Blick darauf, wie Watches im Archiv implementiert sind.
Nur einen ganz kurzen Blick, denn es ist wirklich kein schöner
Anblick:
     

     
  \par
  
Wenn man ein Watch setzt
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} /home/jrandom/myproj\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watchers
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} hello.c jrandom edit unedit commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 

       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
hält CVS das in einer gesonderten Datei, CVS/fileattr, im zuständigen
Unterverzeichnis des Archivs fest:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd /usr/local/newrepos
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} CVSROOT/ myproj/\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} CVS/ a-subdir/ foo.gif,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} README.txt,v b-subdir/ hello.c,v\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd CVS
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} fileattr\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 

       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Die Tatsache, dass fileattr in einem Unterverzeichnis des Archivs mit
Namen CVS abgelegt wird, heißt jetzt aber nicht, dass das Archiv zu
einer Arbeitskopie geworden ist. Es ist einfach so, dass der Name CVS
schon für die Buchführung in der Arbeitskopie reserviert ist und sich
CVS deshalb sicher sein kann, dass es niemals ein Unterverzeichnis
mit diesem Namen im Archiv speichern muss.
     

     
  \par
  
Ich werde das Dateiformat von fileattr hier nicht formal beschreiben,
man kapiert es recht schnell, wenn man zusieht, wie sich die Datei von
Kommando zu Kommando verändert:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch add hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch add README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} FREADME.txt \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch on hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watchers=jrandom\verb+>+edit+unedit+commit;\_watched=\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} FREADME.txt \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch remove hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watched=\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} FREADME.txt \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs watch off hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} FREADME.txt \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 

       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Die Informationen über Bearbeitungssitzungen werden ebenfalls in
fileattr abgelegt. Folgendes geschieht, wenn qsmith sich als
Bearbeiter einträgt:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs edit hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Fhello.c \_watched=;\_editors=qsmith\verb+>+Tue Jul 20 04:53:23 1999 GMT+floss$\backslash$\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} +/home/qsmith/myproj;\_watchers=qsmith\verb+>+tedit+tunedit+tcommit\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} FREADME.txt \_watchers=jrandom\verb+>+edit+unedit+commit\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Letztlich bleibt zu bemerken, dass CVS die Datei fileattr sowie das
Unterverzeichnis CVS löscht, wenn für keine der Dateien in einem
Verzeichnis noch Beobachter oder Bearbeiter vorhanden sind:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs unedit
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch off
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs watch remove
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat /usr/local/newrepos/myproj/CVS/fileattr
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cat: /usr/local/newrepos/myproj/CVS/fileattr: No such file or directory\end{scriptsize} \end{tt} \linebreak
     


     
  \par
  
Nach dieser kurzen Enthüllung sollte klar sein, dass man die Analyse
des fileattr-Formats besser CVS überlässt. Die Hauptmotivation dafür,
eine grobe Ahnung von diesem Format zu haben (von der tiefen
Befriedigung zu wissen, was hinter den Vorhängen vor sich geht, einmal
abgesehen), liegt darin, dass man möglicherweise eine Erweiterung an
der Funktionalität der Watches plant oder dass man eventuell
auftretende Probleme beheben kann. Ansonsten ist es ausreichend, zu
wissen, dass es keinen Grund zur Beunruhigung gibt, wenn plötzlich
ein Unterverzeichnis namens CVS in Ihrem Archiv auftaucht. Es handelt
sich einfach um den einzig sicheren Ort, an dem CVS Metainformationen
wie eben die Beobachtungslisten speichern kann.
     

    
   \section{Log-Nachrichten und Commit-E-Mails} \label{d76e1442}
        
    

    
  \par
  
Commit-E-Mails sind beim Commit abgeschickte Benachrichtigungen,
welche die Log-Nachrichten und die vom Commit betroffenen Dateien
auflisten. Sie gehen normalerweise an alle Teilnehmer des Projekts,
manchmal auch an sonstige Interessierte. Da die Details, wie
Commit-E-Mails eingerichtet werden, schon von Kapitel 4 abgedeckt
worden sind, werde ich sie hier nicht wiederholen. Mir ist allerdings
aufgefallen, dass Commit-E-Mails manchmal unerwartete Seiteneffekte
auf Projekte haben können, Effekte, die Sie in Betracht ziehen
sollten, wenn Sie Commit-E-Mails für Ihr Projekt einsetzen wollen.
    

    
  \par
  
Erstens: Rechnen Sie damit, dass die Nachrichten meistens ignoriert
werden. Ob sie gelesen werden oder nicht, hängt zumindest zum Teil
davon ab, wie häufig Commits in Ihrem Projekt vorkommen. Tendieren
die Entwickler eher dazu, täglich eine große Änderung per Commit zu
veröffentlichen, oder eher dazu, es über viele kleine Änderungen,
verteilt über den Tag, zu tun? Je näher Ihr Projekt dem zweiten Fall
ist und je stärker die vielen kleinen Commits den ganzen Tag lang auf
die Entwickler herunter prasseln, um so weniger werden sie sich um
jede einzelne Nachricht kümmern.
    

    
  \par
  
Daraus folgt nicht, dass die Benachrichtigungen keinen Zweck erfüllen,
man sollte nur nicht davon ausgehen, dass jeder jede Nachricht liest.
Es ist immer noch ein bequemer Weg, die Sachen im Auge zu behalten -
wer was macht -, ohne das Aufdringliche, das Watches an sich haben.
Gehen die E-Mails an eine öffentlich zugängliche Mailingliste, so hat
man einen wundervollen Mechanismus, um interessierten Benutzern
(Entwickler in spe!) die Möglichkeit zu bieten, täglich
mitzubekommen, was am Quelltext geschieht.
    

    
  \par
  
Vielleicht sollten Sie in Betracht ziehen, einen Entwickler
abzustellen, die Log-Nachrichten zu verfolgen und den Überblick über
das gesamte Projekt zu behalten (ein guter Projektleiter tut das
natürlich sowieso). Wenn die Zuständigkeiten klar verteilt sind,
beispielsweise wenn bestimmte Entwickler bestimmten
Unterverzeichnissen des Projekts zugeordnet sind, könnten Sie ganz
besonders schicke Vorkehrungen in CVSROOT/loginfo treffen, sodass
jede verantwortliche Partei gesondert markierte Nachrichten darüber
erhält, was in ihrem Zuständigkeitsbereich passiert. Das hilft dabei
sicherzustellen, dass die Entwickler wenigstens die E-Mails lesen, die
zu ihren Unterverzeichnissen gehören.
    

    
  \par
  
Ein interessanterer Effekt tritt ein, wenn Commit-E-Mails nicht
ignoriert werden. Die Leute fangen an, sie als
Echtzeit-Kommunikationsmittel zu verwenden. Dadurch kann sich so etwas
ergeben:
    

    
Finished feedback form; fixed the fonts and background colors
on the home page. Whew! Anyone want to go to Mon Lung for lunch?
    

    
  \par
  
Es ist nichts Falsches daran, die Logs auf diese Art zu »mißbrauchen«.
Dadurch wird es interessant, sie später noch einmal zu lesen. Dennoch
sollte sich jeder darüber im Klaren sein, dass sich Log-Nachrichten,
wie etwa die folgende, nicht nur per E-Mail verbreiten, sondern sich
auch in der Projekthistorie verewigen. Über die Spezifikationen eines
Kunden zu hadern, mag ein verbreiteter Zeitvertreib unter
Programmierern sein; man kann sich leicht vorstellen, dass jemand
beim Commit eine Log-Nachricht wie folgende schreibt, wissend, dass
die anderen Programmierer sie als E-Mail erhalten:
    

    
Truncate four-digit years to two-digits in input. What the customer
wants, the customer gets, no matter how silly \& wrong. Sigh.
    

    
  \par
  
Kein Zweifel - eine amüsante E-Mail, aber was passiert, wenn der Kunde
sich eines Tages die Log-Nachrichten ansieht? (Ich wette, dass
ähnliche Befürchtungen schon bei mehr als einer Site dazu geführt
haben, CVS/loginfo so einzurichten, dass Mechanismen vorgeschaltet
werden, die Anstößiges aus den Log-Nachrichten heraus halten!)
    

    
  \par
  
Im Großen und Ganzen scheinen Commit-E-Mails die Leute davon
abzuhalten, zu kurze oder unverständliche Log-Nachrichten zu
schreiben, was möglicherweise »eine gute Sache« ist. Jedoch müssen sie
hin und wieder daran erinnert werden, dass jeder, der irgendwann
einmal die Logs liest, ein potenzieller Adressat ist, nicht nur die
Empfänger der E-Mails.
    

    
  \par
  
     {\bf 
Wie man Log-Nachrichten nach dem Commit ändert
     }
    

    
  \par
  
Für den Fall, dass jemand eine Log-Nachricht nach dem Commit bereut,
ermöglicht es CVS, die Logs nachträglich zu ändern. Man erledigt dies
mit der {\bf -m}-Option, die man zusammen mit dem {\bf admin}-Kommando verwendet
(auf das Kommando wird später in diesem Kapitel noch detaillierter
eingegangen). Das Kommando erlaubt es, genau eine Log-Nachricht (pro
Revision, pro Datei) auf einmal zu ändern. Das funktioniert so:
    

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$     
floss\$ cvs admin -m 1.7:''Truncate four-digit years to two in input.'' date.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/someproj/date.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

    
  \par
  
Die ursprüngliche Log-Nachricht, die mit dem Commit von Revision 1.7
abgelegt wurde, ist durch eine völlig unschuldige - wenn auch weniger
scharfzüngige - ersetzt worden. (Nicht den Doppelpunkt vergessen, der
die Revisionsnummer von der Log-Nachricht trennt.)
    

    
  \par
  
Wenn die »falsche« Log-Nachricht beim Commit von mehreren Dateien
verwendet wurde, muss man {\bf cvs admin} für jede Datei getrennt aufrufen.
Es handelt sich also um eines der wenigen Kommandos, bei denen CVS
erwartet, dass nur ein einziger Dateiname als Argument übergeben wird:
    

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs admin -m 1.2:''very boring log message'' hello.c README.txt foo.gif
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs admin: while processing more than one file:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs [admin aborted]: attempt to specify a numeric revision\end{scriptsize} \end{tt} \linebreak
     

    
  \par
  
Lassen Sie sich nicht davon verwirren, dass Sie dieselbe Fehlermeldung
erhalten, als wenn Sie gar keine Dateinamen mit angegeben hätten. Das
liegt daran, dass CVS in dem Fall alle Dateien, die im aktuellen
Verzeichnis und darunter liegen, als implizite Argumente betrachtet:
    

    
     \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs admin -m 1.2:''very boring log message''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs admin: while processing more than one file:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs [admin aborted]: attempt to specify a numeric revision\end{scriptsize} \end{tt} \linebreak
     


    
  \par
  
(Es ist bei CVS-Fehlermeldungen leider häufig der Fall, dass man die
Dinge aus der Sicht von CVS betrachten muss, damit sie einen Sinn
ergeben!)
    

    
  \par
  
Der {\bf admin -m}-Aufruf ändert die Projekthistorie, seien Sie also
vorsichtig. Es wird keine Aufzeichnung geben, die besagt, dass die
Log-Nachricht jemals verändert wurde - es wird einfach so aussehen,
als ob die Revision schon beim ursprünglichen Commit die neue
Log-Nachricht erhalten hätte. Die alte Meldung wird nirgends ihre
Spuren hinterlassen (außer Sie heben die Original-E-Mail auf).
    

    
  \par
  
Obwohl sein Name scheinbar besagt, dass nur der designierte
CVS-Administrator es benutzen kann, kann normalerweise jeder {\bf cvs admin}
aufrufen, solange er Schreibzugriff auf das fragliche Projekt hat.
Existiert jedoch eine Unix-Benutzergruppe namens cvsadmin, dann ist
die Nutzung dieses Kommandos auf die Mitglieder der Gruppe
beschränkt. (Mit der Ausnahme, dass immer noch jeder {\bf cvs admin -k}
benutzen kann.) Dennoch benutzt man es besser mit großer Vorsicht,
denn die Möglichkeit, die Geschichte des Projekts umzuschreiben, ist
verglichen mit anderen, potenziell zerstörerischen Fähigkeiten noch
harmlos. In Kapitel 9 gibt es noch mehr zu {\bf admin}, zusammen mit Wegen,
dessen Benutzung einzuschränken.
   

  \section{Wie man eine Arbeitskopie los wird} \label{d76e1563}
        
   

   
  \par
  
Bei normaler CVS-Nutzung wird man den Verzeichnisbaum der Arbeitskopie
so wie jeden anderen Verzeichnisbaum los:
   

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rm -rf myproj
       \end{scriptsize} \end{tt} \linebreak 
     

     
  \par
  
Wenn Sie sich Ihrer Arbeitskopie auf diese Art entledigen, werden die
übrigen Entwickler allerdings nicht mitbekommen, dass Sie deren
Nutzung eingestellt haben. CVS stellt ein Kommando zur Verfügung, mit
dem man die Arbeitskopie explizit terminieren kann. Sehen Sie {\bf release}8
als das Gegenstück zu {\bf checkout} an - Sie teilen dem Archiv mit, dass
Sie mit Ihrer Arbeitskopie abgeschlossen haben. Wie {\bf checkout} wird
{\bf release} vom übergeordneten Verzeichnis im Verzeichnisbaum aufgerufen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} /home/qsmith/myproj\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd ..
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs release myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} You have [0] altered files in this repository.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Are you sure you want to release directory 'myproj': y\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Falls Sie gegenüber dem Archiv noch nicht per Commit gespeicherte
Änderungen in Ihrer Arbeitskopie haben, wird {\bf release} fehlschlagen,
soll heißen, dass lediglich die modifizierten Dateien aufgelistet
werden und sonst nichts geschieht. Vorausgesetzt der Verzeichnisbaum
ist »sauber« (komplett auf dem aktuellen Stand), vermerkt {\bf release} im
Archiv, dass die Arbeitskopie freigegeben wurde.
     

     
  \par
  
Sie können {\bf release} auch anweisen, den Verzeichnisbaum für Sie zu
löschen, indem Sie {\bf -d} mit angeben:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} myproj\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs release -d myproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} You have [0] altered files in this repository.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Are you sure you want to release (and delete) directory 'myproj: y\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
     


     
  \par
  
Zum Zeitpunkt von CVS Version 1.10.6 ist das {\bf release}-Kommando nicht in
der Lage, die Position des Archivs anhand der Arbeitskopie zu erkennen
(da {\bf release} außerhalb der Arbeitskopie aufgerufen wird). Man muss
daher entweder die globale Option {\bf -d \verb+<+ARCHIV\verb+>+} mit angeben oder
sicherstellen, dass die Umgebungsvariable CVSROOT richtig gesetzt
worden ist. (Dieser Fehler mag in zukünftigen CVS-Versionen behoben
worden sein.)
     

     
  \par
  
Im Cederqvist wird behauptet, dass, wenn Sie {\bf release} verwenden, statt
den Arbeitsverzeichnisbaum einfach zu löschen, diejenigen
benachrichtigt werden, die auf die freigegebenen Dateien ein Watch
gesetzt haben, genau so, als hätten Sie {\bf unedit} aufgerufen. Als ich das
experimentell überprüfen wollte, habe ich festgestellt, dass das
nicht stimmt.
   

  \section{Überblick über die Projekthistorie} \label{d76e1687}
        
   

   
  \par
  
In Kapitel 4 habe ich kurz das Kommando {\bf cvs history} erwähnt. Dieses
Kommando zeigt eine Zusammenfassung aller {\bf checkouts, commits, updates,
rtags} und {\bf releases} an, die im Archiv getätigt wurden (vorausgesetzt,
Logging war bei Erstellung der Datei CVSROOT/history im Archiv aktiv).
Mit den folgenden Kommandos können Sie Inhalt und Erscheinungsbild
der Zusammenfassung mit verschiedenen Optionen kontrollieren.
     

     
  \par
  
Der erste Schritt liegt darin, sicherzustellen, dass Logging im Archiv
eingeschaltet ist. Der Archivadministrator sollte erst einmal
sicherstellen, dass es eine Datei history gibt:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd /usr/local/newrepos/CVSROOT
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls -l history
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ls: history: No such file or directory\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
und falls es keine gibt, sollte er eine wie folgt erstellen:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
touch history
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls -l history
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} -rw-r--r-- 1 jrandom cvs 0 Jul 22 14:57 history\end{scriptsize} \end{tt} \linebreak
     


     
  \par
  
Die Datei history soll außerdem von jedem, der das Archiv benutzt,
beschreibbar sein, ansonsten wird dieser jedes Mal, wenn er ein
CVS-Kommando, das diese Datei verändert, ausführen will, eine
Fehlermeldung erhalten. Am einfachsten macht man die Datei von allen
beschreibbar:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
chmod a+rw history
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls -l history
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} -rw-rw-rw- 1 jrandom cvs 0 Jul 22 14:57 history\end{scriptsize} \end{tt} \linebreak
     


     
      {\bf Bemerkung}
      
  \par
  
Im Fall, dass das Archiv mit dem Kommando cvs init angelegt wurde,
existiert die Datei bereits. Um die Zugriffsrechte muss man sich
allerdings noch kümmern.
      
     

     
  \par
  
In den folgenden Beispielen wird davon ausgegangen, dass History
Logging, also die Aufzeichnung der Projekthistorie, schon eine Weile
eingeschaltet war, sodass sich schon einiges an Daten in der Datei
history angesammelt hat.
     

     
  \par
  
Die Ausgabe von {\bf cvs history} ist etwas knapp geraten (sie ist
vermutlich nicht dafür gedacht, von Menschen analysiert zu werden,
obwohl sie mit ein wenig Übung gut lesbar ist). Rufen wir das Kommando
kurz auf und sehen, was wir bekommen:
     


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ pwd
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} /home/qsmith/myproj\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
paste\$ cvs history -e -a
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} O 07/25 15:14 +0000 qsmith myproj =mp= \~{}/*\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:16 +0000 qsmith 1.14 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:21 +0000 qsmith 1.14 README.txt myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} G 07/25 15:21 +0000 qsmith 1.15 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} A 07/25 15:22 +0000 qsmith 1.1 goodbye.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:23 +0000 qsmith 1.16 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:26 +0000 qsmith 1.17 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:29 +0000 qsmith 1.2 goodbye.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} G 07/25 15:29 +0000 qsmith 1.18 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:30 +0000 qsmith 1.19 hello.c myproj == \~{}/mp\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} O 07/23 03:45 +0000 jrandom myproj =myproj= \~{}/src/*\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} F 07/23 03:48 +0000 jrandom =myproj= \~{}/src/*\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} F 07/23 04:06 +0000 jrandom =myproj= \~{}/src/*\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:12 +0000 jrandom 1.13 README.txt myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:17 +0000 jrandom 1.14 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:18 +0000 jrandom 1.14 README.txt myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:18 +0000 jrandom 1.15 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:23 +0000 jrandom 1.1 goodbye.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:23 +0000 jrandom 1.16 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U 07/25 15:26 +0000 jrandom 1.1 goodbye.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} G 07/25 15:26 +0000 jrandom 1.17 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:27 +0000 jrandom 1.18 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} C 07/25 15:30 +0000 jrandom 1.19 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 15:31 +0000 jrandom 1.20 hello.c myproj == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M 07/25 16:29 +0000 jrandom 1.3 whatever.c myproj/a-subdir == \~{}/src/myproj\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Ist doch alles klar verständlich, oder?
     

     
  \par
  
Bevor wir die Ausgabe näher untersuchen, sei angemerkt, dass der
Aufruf zusammen mit zwei Optionen geschah: {\bf -e} und 
{\bf -a}. Wenn Sie {\bf history}
aufrufen, werden Sie fast immer Optionen mit angeben wollen, die
festlegen, welche Daten wie angezeigt werden sollen. Darin
unterscheidet es sich von den meisten anderen CVS-Kommandos, die
normalerweise bereits dann etwas Sinnvolles tun, wenn sie ganz ohne
Optionen aufgerufen werden. In unserem Beispiel bedeuten die Optionen
»alles« (jede Art von Ereignis) beziehungsweise »alle« (für alle
Benutzer).
     

     
  \par
  
Das {\bf history}-Kommando unterscheidet sich von anderen Kommandos auch
noch darin, dass, obwohl es normalerweise aus einer Arbeitskopie
heraus aufgerufen wird, es seine Ausgabe nicht auf das in der
Arbeitskopie enthaltene Projekt beschränkt. Stattdessen zeigt es die
gesamte Historie aller Projekte im Archiv an - die Arbeitskopie dient
nur dazu, CVS mitzuteilen, welchem Archiv die history-Daten entnommen
werden sollen. (Im vorangegangenen Beispiel waren die einzigen
history-Daten die vom Projekt myproj, daher sieht man sonst keine.)
     

     
  \par
  
Das generelle Format der Ausgabe ist
     

     
  \par
  
{\bf KÜRZEL DATUM BENUTZER [REVISION] [DATEI] PFAD\_IM\_ARCHIV NAME\_DER\_ARBEITSKOPIE}
     

     
  \par
  
Die Buchstabenkürzel beziehen sich auf die verschiedenen
CVS-Operationen, wie in Tabelle 6.1 dargestellt.
     

     
  \par
  
Für Operationen (wie z.B. {\bf checkout}), die sich auf das Projekt als
Ganzes anstelle von einzelnen Dateien davon beziehen, werden die
REVISION und DATEI weggelassen, stattdessen wird der Pfad des Archivs
zwischen die Gleichheitszeichen gesetzt.
     

     
  \par
  
Obwohl die Ausgabe des Kommandos {\bf history} vom Design her als kompakte,
interpretierbare Eingabe für andere Programme gedacht ist, gibt CVS
einem viel Kontrolle über Umfang und Inhalt. Die in Tabelle 6.2
aufgelisteten Optionen kontrollieren, welche Typen von Ereignissen
gemeldet werden. Wenn Sie ausgewählt haben, welche Ereignisse Sie
angezeigt bekommen möchten, können Sie noch weitere Filterkriterien
der Tabelle 6.3 entnehmen.
     




     
    
    %table
    \begin{tabular}{|l|l|}
    \hline 
            
               
		\begin{minipage}{60mm}
              
Buchstabenkürzel
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Bedeutung
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
O
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Auschecken (Checkout) einer Datei
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
T
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Marke (Tag)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
F
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Freigabe (siehe Release)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
W
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Update (Benutzerdatei wurde gelöscht, Datei aus entries
entfernt. Die Datei war im Archiv gelöscht worden.)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
U
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Update (Datei hat unveränderte Datei des Benutzers überschrieben)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
G						
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Update (Datei wurde erfolgreich mit einer vom Benutzer
veränderten Datei verschmolzen)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
C
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Update (Datei wurde verschmolzen, aber Konflikt mit einer vom
Benutzer geänderten Datei, conflicts)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
M
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Commit (einer modifizierten Datei)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
A
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Commit (einer neuen Datei, add)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
R
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Commit (Löschen einer Datei, remove)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
E
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Export (siehe Kapitel 9)
       
		\end{minipage}
	      \\ \hline
    \end{tabular}
  

     
  \par
  
Tabelle 6.1 Die Bedeutung der Buchstabenkürzel
     



     
    
    %table
    \begin{tabular}{|l|l|}
    \hline 
            
               
		\begin{minipage}{60mm}
              
Option
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Bedeutung
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-m MODUL
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige Vorgänge, die MODUL betreffen
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-c
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige Commit-Vorgänge
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-o
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige Checkout-Vorgänge
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-T
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige alle Vorgänge, die Marken (Tags) betreffen
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-x KÜRZEL
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige alle Vorgänge, die vom Typ KÜRZEL sind (mindestens
eines aus OTFWUGCMARE, siehe Tabelle 6.1)
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-e
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige einfach alle Vorgänge
       
		\end{minipage}
	      \\ \hline
    \end{tabular}
  

     
  \par
  
Tabelle 6.2 Optionen, die nach Ereignistyp filtern
     



     
    
    %table
    \begin{tabular}{|l|l|}
    \hline 
            
               
		\begin{minipage}{60mm}
              
Option
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Bedeutung
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-a
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige die Aktionen aller Benutzer
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-w
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige nur die Aktionen, die aus dieser Arbeitskopie heraus
vorgenommen wurden
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-l
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige nur die letzte solche Aktion dieses Benutzers
       
		\end{minipage}
	      \\ \hline 
            
               
		\begin{minipage}{60mm}
              
-u BENUTZER
       
		\end{minipage}
	      & 
            
               
		\begin{minipage}{60mm}
              
Zeige die Einträge für BENUTZER
       
		\end{minipage}
	      \\ \hline
    \end{tabular}
  

     
  \par
  
Tabelle 6.3 Optionen, die Benutzer heraus filtern
     

  \section{Detaillierter Überblick über Projektaktivitäten: Anmerkungen} \label{d76e2135}
        
   

    
  \par
  
Wenn das {\bf history}-Kommando einem einen groben Überblick über die
Projektaktivitäten gibt, dann ist das {\bf annotate}9-Kommando sozusagen das
Mikroskop, das es einem ermöglicht, die Details zu erkennen. Mit
{\bf annotate} kann man sehen, wer die letzte Person war, die ihre Finger an
den einzelnen Zeilen einer Datei hatte, und bei welcher Revision dies
geschah:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
floss\$ cvs annotate
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Annotations for README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.14 (jrandom 25-Jul-99): blah\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.13 (jrandom 25-Jul-99): test 3 for history\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.12 (qsmith 19-Jul-99): test 2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.11 (qsmith 19-Jul-99): test\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): blah\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): Just a test project.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): yeah.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.5 (jrandom 21-Jun-99): nope.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.15 (jrandom 25-Jul-99): /* another test for history */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.13 (qsmith 19-Jul-99): /* random change number two */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.21 (jrandom 25-Jul-99): printf (''Hellooo, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 21-Jun-99): printf (''hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.11 (qsmith 18-Jul-99): /* added this comment */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.16 (qsmith 25-Jul-99): /* will merge these changes */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.18 (jrandom 25-Jul-99): /* will merge these changes too */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 25-Jul-99): /* A completely non-empty C file. */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 25-Jul-99): /* An almost completely empty C file. */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): /* A completely empty C file. */\end{scriptsize} \end{tt} \linebreak
     
	     
    
  \par
  
Die Ausgabe von {\bf annotate} lässt sich intuitiv erfassen. Links sind
Revisionsnummer, Entwickler und da Datum, zu dem die fragliche Zeile
hinzugefügt oder verändert wurde. Rechts sieht man die eigentliche
Zeile zur jeweils aktuellen Revision. Da jede Zeile mit Anmerkungen
(also Revisionsnummer, Entwickler und Datum) versehen ist, bekommt
man den ganzen Inhalt der Datei aufgelistet, aber um die Anmerkungen
nach rechts verschoben.
    

    
  \par
  
Wenn man eine Revisionsnummer oder eine Marke (Tag) spezifiziert,
bekommt man die Anmerkungen, die zu dieser Revision aktuell waren;
soll heißen: Es werden die letzten Modifikationen jeder Zeile zu oder
bis zu dieser Revision angezeigt. Das ist wahrscheinlich der üblichste
Weg, {\bf annotate} zu benutzen: Eine einzige Datei zu einer bestimmten
Revision zu untersuchen, um zu erkennen, welche Entwickler an welchen
Teilen der Datei aktiv waren.
    

    
  \par
  
Zum Beispiel kann man in der Ausgabe aus dem vorangegangenen Beispiel
sehen, dass die aktuellste Revision von hello.c 1.21 ist, als jrandom
etwas an folgender Zeile änderte:
    

    
  \par
  
{\bf printf (''Hellooo, world!$\backslash$n'');}
    

    
  \par
  
Ein Weg herauszufinden, was sie getan hat, ist, sich den {\bf diff}10
zwischen dieser Revision und der vorangegangenen anzusehen:
    


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
cvs diff -r 1.20 -r 1.21 hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} index: hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ===============================================\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.20\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} diff -r1.20 -r1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 9c9\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+<+ printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} --\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+>+ printf (''Hellooo, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
     


    
  \par
  
Eine weitere Möglichkeit, unter Beibehaltung des dateiweiten
Überblicks über die allgemeinen Aktivitäten herauszufinden, was
geschehen ist, liegt darin, die aktuellen Anmerkungen mit denen der
vorigen Version zu vergleichen:
    

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
floss\$ cvs annotate -r 1.20 hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.15 (jrandom 25-Jul-99): /* another test for history */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.13 (qsmith 19-Jul-99): /* random change number two */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 21-Jun-99): printf (''hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.11 (qsmith 18-Jul-99): /* added this comment */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.16 (qsmith 25-Jul-99): /* will merge these changes */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.18 (jrandom 25-Jul-99): /* will merge these changes too */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
     
     

    
  \par
  
Obwohl {\bf diff} die Fakten über die Veränderung des Quelltextes in
knapperer Form darstellt, können die Anmerkungen vorzuziehen sein,
denn durch sie wird der geschichtliche Kontext hergestellt, indem
gezeigt wird, wie lange die vorige Ausführung vorhanden war (in
unserem Fall die ganze Zeit, seit Revision 1.1). Dieses Wissen kann
Ihnen bei der Entscheidung helfen, ob Sie in die Logs schauen wollen,
um die Motivation für die Änderungen herauszufinden:
    

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
cvs log -r 1.21 hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Working file: hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} head: 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} branch:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} locks: strict\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} access list:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} symbolic names:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  random-tag: 1.20\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  start: 1.1.1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  jrandom: 1.1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} keyword substitution: kv\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} total revisions: 22; selected revisions: 1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} description:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ---------------------------\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} evision 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} date: 1999/07/25 20:17:42; author: jrandom; state: Exp; lines: +1 -1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} say hello with renewed enthusiasm\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ==============================================\end{scriptsize} \end{tt} \linebreak
     
     

    
  \par
  
Zusätzlich zu -r können Sie die Anmerkungen auch mit der Option{\bf  -D
DATUM} filtern:
    


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs annotate -D ''5 weeks ago'' hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} floss\$ cvs annotate -D ''3 weeks ago'' hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} **************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 21-Jun-99): printf (''hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
     
     
     
  \par
  
      {\bf 
Anmerkungen und Verzweigungen
      }
     

     
  \par
  
Wenn Sie keine weiteren Optionen angeben, zeigt {\bf annotate} immer die
Aktivitäten der Stammversion (engl. trunk). (Die Tendenz, die
Stammversion so zu bevorzugen, ist entweder ein Bug oder ein Feature,
je nach Standpunkt.) Sie können CVS zwingen, die Anmerkungen einer
abgezweigten Version auszugeben, indem Sie die Marke dieses Zweiges
als Argument für {\bf -r} übergeben. Hier ein Beispiel einer Arbeitskopie,
in der sich hello.c in einer abgezweigten Version namens
Brancho\_Gratuito befindet und in der mindestens eine Änderung in dem
Zweig per Commit vorgenommen wurde:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs status hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ==================================================\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} File: hello.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  Working revision: 1.10.2.2 Sun Jul 25 21:29:05 1999\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  Repository revision: 1.10.2.2 /usr/local/newrepos/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  Sticky Tag: Brancho\_Gratuito (branch: 1.10.2)\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  Sticky Date: (none)\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  Sticky Options: (none\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} floss\$ cvs annotate hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 21-Jun-99): printf (''hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} floss\$ cvs annotate -r Brancho\_Gratuito hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10.2.2 (jrandom 25-Jul-99): printf (''hmmmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10.2.1 (jrandom 25-Jul-99): printf (''added this line'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Sie können auch die Nummer der Zweigversion übergeben:
     


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs annotate -r 1.10.2 hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10.2.2 (jrandom 25-Jul-99): printf (''hmmmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10.2.1 (jrandom 25-Jul-99): printf (''added this line'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
     
     

     
  \par
  
oder auch eine vollständige Revisionsnummer innerhalb der Zweigversion:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs annotate -r 1.10.2.1 hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Annotations for hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99):\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): main ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10 (jrandom 12-Jul-99): /* test */\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.3 (jrandom 21-Jun-99): printf (''hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.4 (jrandom 21-Jun-99): printf (''double hmmm$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.10.2.1 (jrandom 25-Jul-99): printf (''added this line'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.2 (jrandom 21-Jun-99): printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 1.1 (jrandom 20-Jun-99): }\end{scriptsize} \end{tt} \linebreak
     
     
 
     
  \par
  
Wenn Sie so vorgehen, vergessen Sie nicht, dass die Nummern nur für
diese eine Datei gültig sind. Generell ist es wohl besser, - sofern
möglich - den Namen der Zweigversion zu benutzen.
    

  \section{Verwendung der Schlüsselwortexpansion} \label{d76e2778}
        
   

    
  \par
  
Sie erinnern sich vielleicht daran, dass Schlüsselwortexpansion kurz
in Kapitel 2 erwähnt wurde. RCS-Schlüsselwörter sind spezielle Wörter,
die in Dollarzeichen eingeschlossen sind und die CVS aus Textdateien
heraussucht und zu Revisions-Kontrollinformationen expandiert. Wenn
beispielsweise eine Datei
    

    
  \par
  
	    {\bf \$Author\$}
    

    
  \par
  
enthält, dann wird CVS das beim Update dieser Datei auf eine bestimmte
Revision durch den Benutzernamen derjenigen Person expandieren, die
für den Commit der Revision verantwortlich ist:
    

    
  \par
  
	    {\bf \$Author: jrandom \$}
    

    
  \par
  
CVS kümmert sich um diese Schlüsselwörter auch in ihrer expandierten
Form, sodass sie, selbst wenn sie schon einmal expandiert wurden, auch
weiterhin aktualisiert werden.
    

    
  \par
  
Obwohl Schlüsselwörter keine Informationen liefern, die nicht auch auf
anderen Wegen erreichbar sind, bieten sie doch eine bequeme
Möglichkeit, die Fakten über die Revisionskontrolle in die Textdatei
einzubetten, sodass man keine obskuren CVS-Operationen durchführen
muss.
    

    
  \par
  
Hier ein paar weitere gebräuchliche Schlüsselwörter:
    

{\bf \$Date\$} ==\verb+>+ Datum des letzten Commit, wird zu ==\verb+>+ \linebreak 
{\bf \$Date: 1999/07/26 06:39:46 \$}

\$Id\$ ==\verb+>+ Dateiname, Revision, Datum und Autor, wird zu ==\verb+>+
\$Id: hello.c,v 1.11 1999/07/26 06:39:46 jrandom Exp \$

\$Revision\$ ==\verb+>+ genau was Sie denken, wird zu ==\verb+>+
\$Revision: 1.11 \$

\$Source\$ ==\verb+>+ Pfad zur korrespondierenden Datei im Archiv, wird zu ==\verb+>+
\$Source: /usr/local/newrepos/tossproj/hello.c,v \$

\$Log\$ ==\verb+>+ sammelt Log-Nachrichten für diese Datei an, wird
zu ==\verb+>+
\$Log: hello.c,v \$

Revision 1.2 1999/07/26 06:47:52 jrandom
...and this is the second log message.

Revision 1.1 1999/07/26 06:39:46 jrandom
This is the first log message...

    
  \par
  
Das Schlüsselwort {\bf \$Log\$} ist hierbei das einzige, das zu
mehreren Zeilen expandiert wird. Es ersetzt nicht - wie die anderen
- die alte Expansion durch eine neue, sondern fügt direkt nach dem
Schlüsselwort die neuste Expansion und zusätzlich noch eine Leerzeile
ein. So wird die vorige Expansion weiter nach unten geschoben.
Außerdem wird noch jeder Text, der zwischen dem Anfang der Zeile und
{\bf \$Log\$} steht, den expandierten Zeilen vorangestellt, damit
die Log-Nachrichten im Quelltext einkommentiert werden. Wenn Sie
beispielsweise das 
    

    
  \par
  
// {\bf \$Log\$}
    

    
  \par
  
in die Datei schreiben, wird es beim ersten Commit zu so etwas:
    

    
  \par
  
// \$Log: hello.c,v \$ \linebreak 
// Revision 1.14  1999/07/26 07:03:20  jrandom \linebreak 
// this is the first log message... \linebreak 
//
    

    
  \par
  
Beim zweiten Commit:
    

    
  \par
  
// \$Log: hello.c,v \$ \linebreak 
// Revision 1.15  1999/07/26 07:05:34  jrandom \linebreak 
// ...and this is the second log message... \linebreak 
// \linebreak 
// Revision 1.14 1999/07/26 07:03:20 jrandom \linebreak 
// this is the first log message... \linebreak 
    

    
  \par
  
Und so weiter:
    

    
  \par
  
// \$Log: hello.c,v \$ \linebreak 
// Revision 1.16 1999/07/26 07:05:34 jrandom \linebreak 
// ...and this is the third! \linebreak 
// \linebreak 
// Revision 1.15 1999/07/26 07:04:40 jrandom \linebreak 
// ...and this is the second log message... \linebreak 
// \linebreak 
// Revision 1.14 1999/07/26 07:03:20 jrandom \linebreak 
// this is the first log message... \linebreak 
//
    

    
  \par
  
Wenn Sie nicht die gesamte Entwicklung der Log-Datei in Ihrer Datei
haben wollen, können Sie die älteren Abschnitte entfernen, wenn es
Ihnen zu lang wird. Die von {\bf \$Log\$} zur Verfügung gestellte
Funktionalität ist mit Sicherheit komfortabler, als cvs log zu
bemühen, und mag sich bei Projekten lohnen, bei denen die Log-Dateien
ständig gelesen werden müssen.
    

    
  \par
  
Eine üblichere Technik ist es, {\bf \$Revision\$} in die Datei mit
aufzunehmen und es als Versionsnummer des Programms zu verwenden.
Das ist möglich, wenn das Projekt im Wesentlichen aus einer Datei
besteht oder häufig neue Versionen veröffentlicht werden und sich
eine Datei bei jeder neuen Version garantiert ändert. Sie können
sogar die RCS-Schlüsselwörter direkt im Quelltext des Programms
benutzen.
    

    
  \par
  
VERSION = ''{\bf \$Revision: 1.114 \$}'';
    

    
  \par
  
CVS wird das Schlüsselwort wie jedes andere expandieren, es hat keine
Vorstellung von der Semantik der Programmiersprache und geht nicht
davon aus, dass die Anführungszeichen die Zeichenkette in irgendeiner
Form schützen sollen.
    

    
  \par
  
Eine komplette Liste der Schlüsselwörter (es gibt noch ein paar
weitere, ziemlich obskure) gibt es in Kapitel 9.
    
   \section{Eine prekäre Lage: Wie überlebt man die Arbeit mit Verzweigungen?} \label{d76e2914}
        
    

    
  \par
  
Verzweigungen sind gleichzeitig eine der wichtigsten und am
leichtesten mißbrauchten Fähigkeiten von CVS. Es kann sehr hilfreich
sein, wenn man gefährliche oder störende Änderungen in einer
getrennten Entwicklungslinie isoliert, bis sie sich stabilisiert
haben. Wenn sie jedoch nicht richtig gemanagt werden, können
Verzweigungen ein Projekt ganz schnell in Verwirrung und Chaos
stürzen, nämlich wenn die Entwickler den Überblick verlieren, welche
Änderungen wann wieder zusammengeführt wurden.
    

    
  \par
  
Um erfolgreich mit Verzweigungen arbeiten zu können, sollte sich die
Entwicklergruppe an folgende Regeln halten:
    

    
  \par
  
Halten Sie die gleichzeitig aktiven Verzweigungen möglichst klein. Je
mehr Verzweigungen zur gleichen Zeit entwickelt werden, umso
wahrscheinlicher ist es, dass sie Konflikte erzeugen, wenn sie zurück
in die Hauptversion einfließen sollen. In der Praxis erreicht man das,
indem man die Verzweigungen so häufig wie möglich (sobald eine
Zweigversion an einem stabilen Punkt angelangt ist) mit der Hauptlinie
verschmilzt und die Entwicklungsarbeit an der Hauptversion fortsetzt.
Indem man die parallel laufenden Entwicklungen klein hält, kann jeder
besser nachvollziehen, was in welcher Verzweigung vorgeht, und die
Wahrscheinlichkeit, dass Konflikte auftreten, wird kleiner.
    

    
{\bf Bemerkung} \linebreak 
Das heißt jetzt aber nicht, dass die absolute Anzahl an Verzweigungen
im Projekt klein zu halten ist, lediglich die Zahl der Verzweigungen,
an denen gleichzeitig gearbeitet wird, soll klein sein.
    

    
  \par
  
Minimieren Sie die Komplexität - also die Tiefe - in Ihrem
Verzweigungsplan. Es mag Umstände geben, wo es angemessen ist,
Verzweigungen einer Verzweigung zu haben, aber die sind spärlich
gesät. (Man kann ein ganzes Leben lang programmieren, ohne jemals auf
eine zu stoßen.) Nur weil CVS es ermöglicht, beliebig viele Ebenen
von verschachtelten Verzweigungen zu haben und beliebige Verzweigungen
zu vereinen, heißt das noch lange nicht, dass Sie das auch wollen. In
den meisten Situationen ist es am besten, dass alle Verzweigungen ihre
Wurzel in der Hauptversion haben und auch dahin zurückkehren.
    

    
  \par
  
Benutzen Sie einheitlich benannte Marken, um all ihre Verzweigungs-
und Zusammenführungsereignisse zu kennzeichnen. Im Idealfall sollte
die Bedeutung jeder Marke und ihr Verhältnis zu den übrigen
Verzweigungen allein durch den Namen offensichtlich sein. (Dieser
Punkt wird noch anhand der Beispiele klarer werden.)
    

    
  \par
  
Mit diesen Regeln im Kopf wenden wir uns nun einem typischen Szenario
mit verzweigter Entwicklungsarbeit zu. Wir werden jrandom an der
Hauptversion und qsmith an einer abgezweigten Version haben. Bedenken
Sie aber, dass genauso gut mehrere Entwickler an beiden tätig sein
könnten. Die normale Entwicklungsarbeit an jedweder Linie kann
beliebig viele Personen umfassen, die Benennung und Zusammenführung
überlässt man aber am besten genau einer Person auf jeder Seite, wie
Sie gleich sehen werden.
    
   \section{Häufig mit der Hauptversion verschmelzen} \label{d76e2949}
        
    

    
  \par
  
Wir gehen davon aus, dass qsmith für einige Zeit an einer abgezweigten
Version arbeiten möchte, damit er nicht die Hauptversion
destabilisiert, die er mit jrandom teilt. Der erste Schritt liegt
darin, die Verzweigung zu erzeugen. Beachten Sie, wie qsmith zunächst
eine normale Marke (nicht verzweigend) am Punkt der Verzweigung
erzeugt und erst dann die abgezweigte Version erstellt:
     

     

      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
pwd
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} /home/qsmith/myproj\end{scriptsize} \end{tt} \linebreak

      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs tag Root-of-Exotic\_Greetings
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs tag: Tagging .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak

      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs tag -b Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs tag: Tagging .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak

     

     
  \par
  
Der Grund, zuerst die Hauptversion zu markieren, liegt darin, dass es
eines Tages notwendig sein kann, die Hauptversion so abzurufen, wie
sie zum Zeitpunkt der Erstellung der Verzweigung war. Wenn das jemals
nötig ist, so hat man die Möglichkeit, eine Momentaufnahme der
Hauptversion zu referenzieren, die vor der Verzweigung entstanden
ist. Offensichtlich kann nicht die Marke der Verzweigung benutzt
werden, da dann die abgezweigte Version abgerufen werden würde und
nicht die Revisionen der Hauptversion, welche die Wurzel der
Verzweigung bildet. Die einzige Möglichkeit ist die, eine normale
Marke an der Revision anzubringen, an der die Verzweigung wurzelt.
(Mancher hält sich so strikt an diese Regel, dass ich überlegt habe,
sie als Verzweigungsregel Nummer 4 aufzuführen: Erstelle immer eine
nichtverzweigende Marke zum Zeitpunkt der Verzweigung. Wie auch
immer, auf manchen Servern wird dies nicht getan, und sie scheinen
auch ohne auszukommen, sodass es letztlich einfach eine
Geschmacksfrage bleibt.) Von nun an werde ich diese nichtverzweigende
Marke als Verzweigungspunktmarkierung (Branch Point Tag) bezeichnen.
     

     
  \par
  
Beachten Sie auch, dass ein Namensschema eingehalten wird: Die
Verzweigungspunktmarkierung fängt mit Root-of- an, gefolgt vom
eigentlichen Namen der Verzweigung, wobei Unterstriche statt
Bindestriche zur Worttrennung verwendet werden. Wenn die eigentliche
Verzweigung angelegt wird, endet ihre Marke mit -branch, sodass Sie
sie schon anhand ihres Namens als Marke eines Zweiges erkennen können.
(Die Verzweigungspunktmarkierung Root-of-Exotic\_Greetings erhält kein
-branch, da sie ja keine Marke eines Zweiges ist.) Sie müssen sich
natürlich nicht an dieses spezielle Namensschema halten, solange Sie
nur irgendeines verwenden.
     

     
  \par
  
Ich bin hier natürlich besonders pedantisch. In kleineren Projekten,
bei denen jeder weiß, was von wem getan wird, und bei denen man sich
leicht von einer kurzen Phase der Verwirrung erholt, muss man sich
nicht unbedingt an diese Regeln halten. Ob Sie nun
Verzweigungspunktmarkierungen verwenden oder ein striktes Namensschema
für die Marken haben, hängt von der Komplexität des Projektes und vom
Verzweigungsschema ab. (Vergessen Sie außerdem nicht, dass Sie sich
jederzeit umentscheiden können und alte Marken für eine neues
Namensschema aktualisieren können, indem Sie die nach altem Schema
markierte Version abrufen, eine neue Marke anbringen und dann die
alte Marke löschen.)
     

     
  \par
  
Jetzt ist qsmith bereit, an der abgezweigten Version zu arbeiten:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -r Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs update: Updating .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating b-subdir\end{scriptsize} \end{tt} \linebreak

     

     
  \par
  
Er nimmt einige Änderungen an ein paar Dateien vor und führt einen
Commit aus, der die Änderungen in den Zweig hineinbringt:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
emacs README.txt a-subdir/whatever.c b-subdir/random.c
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} ...\end{scriptsize} \end{tt} \linebreak

      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''print greeting backwards, etc''
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Checking in README.txt;\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/README.txt,v \verb+<+-- README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.14.2.1; previous revision: 1.14\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Checking in a-subdir/whatever.c;\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/a-subdir/whatever.c,v \verb+<+-- whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.3.2.1; previous revision: 1.3\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Checking in b-subdir/random.c;\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/b-subdir/random.c,v \verb+<+-- random.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.1.1.1.2.1; previous revision: 1.1.1.1\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
    
In der Zwischenzeit setzt jrandom ihre Arbeit an der Hauptversion
fort. Sie ändert auch zwei der drei Dateien, die qsmith geändert hat.
Aus reiner Bosheit lassen wir sie einige Änderungen machen, die im
Widerspruch zur Arbeit von qsmith stehen:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
emacs README.txt whatever.c
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak

      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''some very stable changes indeed''
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Checking in README.txt;\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/README.txt,v \verb+<+-- README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.15; previous revision: 1.14\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Checking in a-subdir/whatever.c;\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/a-subdir/whatever.c,v \verb+<+-- whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.4; previous revision: 1.3\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Der Konflikt zeigt sich natürlich noch nicht, denn keiner der
Entwickler hat bisher versucht, den Zweig und die Hauptversion wieder
zusammenzuführen. Jetzt nimmt jrandom die Zusammenführung vor:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -j Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs update: Updating .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/README.txt,v\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14.2.1\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.14 and 1.14.2.1 into README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3.2.1\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.3 and 1.3.2.1 into whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.1.2.1\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.1.1.1 and 1.1.1.1.2.1 into random.c\end{scriptsize} \end{tt} \linebreak

      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs update
       \end{scriptsize} \end{tt} \linebreak 

      \begin{tt} \begin{scriptsize} cvs update: Updating .\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} C README.txt\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} C a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating b-subdir\end{scriptsize} \end{tt} \linebreak

      \linebreak\begin{tt} \begin{scriptsize} M b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Zwei der Dateien haben Konflikte. Keine große Sache, denn mit ihrem
üblichen Savoir-faire löst jrandom die Konflikte auf, führt den Commit
aus und markiert die Hauptversion als erfolgreich wieder vereinigt:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
emacs README.txt a-subdir/whatever.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''merged from Exotic\_Greetings-branch (conflicts resolved)''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining b-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in README.txt;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/README.txt,v \verb+<+-- README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.16; previous revision: 1.15\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in a-subdir/whatever.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/a-subdir/whatever.c,v \verb+<+-- whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.5; previous revision: 1.4\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in b-subdir/random.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/b-subdir/random.c,v \verb+<+-- random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs tag merged-Exotic\_Greetings
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs tag: Tagging .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging b-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
     

     
    
  \par
  
Währenddessen braucht qsmith aber nicht abzuwarten, bis die
Zusammenführung abgeschlossen ist, bevor er mit seiner
Programmierarbeit fortfährt, vorausgesetzt, er setzt eine Marke für
die Änderungen, von denen aus jrandom die Zusammenführung durchführt.
(Später wird jrandom den Namen dieser Marke benötigen, generell kommt
es bei der Verwendung von Verzweigungen darauf an, dass die
Entwickler häufig und ausführlich miteinander kommunizieren):
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs tag Exotic\_Greetings-1
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs tag: Tagging .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs tag: Tagging b-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
paste\$ emacs a-subdir/whatever.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
paste\$ cvs ci -m ''print a randomly capitalized greeting''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs commit: Examining .\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs commit: Examining b-subdir\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in a-subdir/whatever.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/a-subdir/whatever.c,v \verb+<+-- whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.3.2.2; previous revision: 1.3.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Natürlich sollte qsmith diese Änderungen durch eine Marke
kennzeichnen, sobald er fertig ist:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
paste\$ cvs -q tag Exotic\_Greetings-2
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Während all das geschieht, nimmt jrandom an einer anderen Datei, die
qsmith bei seinen jüngsten Arbeiten nicht angefasst hat, Veränderungen
vor:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ emacs README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
floss\$ cvs ci -m ''Mention new Exotic Greeting features'' README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in README.txt;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/README.txt,v \verb+<+-- README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.17; previous revision: 1.16\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     


     
  \par
  
Jetzt hat qsmith eine weitere Änderung an der Zweigversion
vorgenommen, und jrandom hat eine konfliktfreie Änderung an der
Hauptversion vorgenommen. Folgendes geschieht, wenn jrandom erneut
versucht, beide zusammenzuführen:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs -q update -j Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/README.txt,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.14 and 1.14.2.1 into README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3.2.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.3 and 1.3.2.2 into whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.1.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.1 and 1.1.1.1.2.1 into random.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
floss\$ cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} C README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} C a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      


     
  \par
  
Es gibt Konflikte! Haben Sie es erwartet?
     

     
  \par
  
Das Problem liegt in der Semantik der Zusammenführung. In Kapitel 2
habe ich gezeigt, dass, wenn Sie
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
floss\$ cvs update -j Zweig
       \end{scriptsize} \end{tt} \linebreak 
     

     
     
  \par
  
in einer Arbeitskopie ausführen, CVS die Unterschiede zwischen der
Wurzel von ZWEIG und seinem derzeitigen Endpunkt in die Arbeitskopie
einbringt. Das Problem mit diesem Verhalten, in dieser Situation,
ist, dass die meisten dieser Änderungen schon beim ersten
Zusammenführen, das jrandom durchgeführt hat, in die Hauptversion
eingeflossen sind. Als CVS versucht hat, diese erneut einzubringen
(über sie selbst), hat es natürlich den Konflikt bemerkt.
     

     
  \par
  
Was jrandom eigentlich tun wollte, war, die Änderungen zwischen dem
letzten Zusammenführen mit dem Zweig und seinem aktuellen Endpunkt in
ihrer Arbeitsgruppe zu vereinen. Sie können das, wie Sie sich
vielleicht aus Kapitel 2 erinnern, mit zwei -j-Optionen für update
bewerkstelligen, vorausgesetzt Sie wissen, welche Revision bei jeder
Option anzugeben ist. Glücklicherweise hat jrandom exakt am Punkt der
letzten Zusammenführung eine Marke gesetzt (Extralob für
Vorausplanung!), sodass das kein Problem ist. Lassen Sie uns zuerst
jrandom ihre Arbeitskopie in einem sauberen Zustand wiederherstellen,
von wo aus sie dann die Zusammenführung erneut versuchen kann:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rm README.txt a-subdir/whatever.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs update: warning: README.txt was lost\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs update: warning: a-subdir/whatever.c was lost\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Nun ist sie bereit, die Zusammenführung durchzuführen, diesmal mit der
von qsmith praktischerweise gesetzten Marke:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -j Exotic\_Greetings-1 -j Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/a-subdir/whatever.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3.2.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.3.2.1 and 1.3.2.2 into whatever.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} M a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
     

     
     
  \par
  
Schon viel besser. Die Änderung von qsmith wurde in whatever.c
eingearbeitet; jrandom kann nun den Commit ausführen und eine Marke
setzen:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q ci -m ''merged again from Exotic\_Greetings (1)''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in a-subdir/whatever.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/a-subdir/whatever.c,v \verb+<+-- whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.6; previous revision: 1.5\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q tag merged-Exotic\_Greetings-1
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Selbst wenn qsmith vergessen hätte, eine Marke beim Zusammenführen
anzubringen, wäre noch nicht alles verloren gewesen. Wenn jrandom
ungefähr wüsste, wann qsmiths erste Änderungen stattgefunden haben,
könnte sie mittels Datum filtern:
     

     
     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -j Exotic\_Greetings-branch:3pm -j Exotic\_Greetings\_branch
       \end{scriptsize} \end{tt} \linebreak 
     


     
  \par
  
Obgleich nützlich als letzter Ausweg, ist das Filtern nach Datum ein
weniger geeignetes Mittel, da die Änderungen auf Grund von
persönlichen Erinnerungen anstelle von verlässlichen Vorgaben der
Entwickler ausgewählt werden. Wenn der erste Satz Änderungen von
qsmith über mehrere Commits anstelle von einem einzigen verteilt
gewesen wäre, hätte jrandom leicht ein Datum oder eine Uhrzeit wählen
können, das zwar einige der Änderungen auffängt, aber
fälschlicherweise nicht alle.
     


     
{\bf Bemerkung} \linebreak 
Es gibt keinen Grund, warum markierbare Punkte in jrandoms Änderungen
als ein einziger Commit an das Archiv gesendet werden müssen - es hat
sich in diesen Beispielen einfach so ergeben. Im wirklichen Leben
könnte qsmith mehrere Commits zwischen den Markierungen vornehmen. Er
kann auch völlig isoliert an dem Zweig arbeiten, wenn es ihm so
gefällt. Der Sinn der Marken ist, aufeinander folgende Punkte
anzuzeigen, zu denen er die Änderungen im Zweig für mit der
Hauptversion verschmelzbar hält. Solange jrandom die Verschmelzung
immer mit zwei -j-Optionen vornimmt und darauf achtet, die
Zusammenführungsmarkierungen von qsmith in der richtigen Reihenfolge
und jeweils nur einmal zu verwenden, sollte die Hauptversion niemals
das Problem mit der mehrfach versuchten Verschmelzung erfahren.
Konflikte können immer noch auftreten, es werden aber unvermeidbare
sein, solche, bei denen sowohl in der Hauptversion als auch im Zweig
Änderungen an denselben Stellen im Quelltext vorgenommen wurden. Diese
müssen immer manuell aufgelöst werden.
    
   \section{Der koordinierte Ansatz: Verschmelzungen von und zur Hauptversion} \label{d76e3674}
        
    

    
  \par
  
Für die an der Hauptversion tätigen Entwickler ist es von Vorteil,
wenn häufig die Änderungen aus der abgezweigten Version in die
Hauptversion eingearbeitet werden, denn so bekommen sie außer ihren
eigenen Änderungen auch noch die Änderungen im Zweig mit. Die
Entwickler, die den Zweig bearbeiten, sehen aber nichts von den
Veränderungen der Hauptversion und können sie so nicht für ihre eigene
Arbeit nutzen beziehungsweise sie darauf abstimmen.
     

     
  \par
  
Damit das möglich wird, muss der am Zweig tätige Entwickler ab und zu
(soll heißen: wann immer er Lust hat, die letzten Änderungen der
Hauptversion zu übernehmen und die unvermeidlichen Konflikte zu
behandeln) einen zusätzlichen Schritt ausführen:
     

     
  \par
  
{\bf paste\$ cvs update -j HEAD}
     

     
  \par
  
Die reservierte Marke HEAD bezeichnet den Kopf, also die Spitze der
Hauptentwicklungslinie. Obiges Kommando übernimmt alle Änderungen aus
der Hauptversion, die zwischen der Wurzel des aktuellen Zweigs
(Exotic\_Greetings-branch) und der jeweils aktuellsten Revision aller
Dateien der Hauptversion liegen. Natürlich sollte qsmith danach eine
Marke anbringen, damit die Entwickler an der Hauptversion vermeiden
können, aus Versehen ihre eigenen Änderungen zu übernehmen, wenn sie
die Änderungen von qsmith bekommen wollen.
     

     
  \par
  
Der Entwickler des Zweigs kann ebenso die Marken der Hauptversion als
Begrenzungen verwenden, was dem Zweig erlaubt, genau die Änderungen zu
übernehmen, die zwischen dem letzten Zusammenführen und dem aktuellen
Zustand der Hauptversion liegen (genau wie die Hauptversion
Zusammenführungen durchführt). Zum Beispiel (angenommen jrandom hat
nach dem Verschmelzen mit dem Zweig einige Änderungen an hello.c
vorgenommen):
     


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
emacs hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''clarify algorithm'' hello.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in hello.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/hello.c,v \verb+<+-- hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.22; previous revision: 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Dann kann qsmith diese Änderungen in den Zweig einarbeiten, den Commit
ausführen und, natürlich, eine Markierung anbringen:
     


     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs -q update -j merged-Exotic\_Greetings-1 -j HEAD
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.22\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.21 and 1.22 into hello.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} M hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs -q ci -m ''merged trunk, from merged-Exotic\_Greetings-1 to HEAD''\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in hello.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/myproj/hello.c,v \verb+<+-- hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.21.2.1; previous revision: 1.21\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q tag merged-merged-Exotic\_Greetings-1
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} T README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T foo.gif\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T hello.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} T b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
     
     
    
  \par
  
Beachten Sie, dass jrandom nach dem Commit der Änderungen an hello.c
keine Markierung gesetzt hat, qsmith hingegen schon. Das Prinzip
dahinter ist, dass man nicht nach jeder winzigen Änderung eine Marke
anzubringen braucht. Nach dem Zusammenführen oder nach einem Commit
einer Entwicklungslinie zu einem verschmelzbaren Zustand hin jedoch
schon! So haben andere Entwickler - möglicherweise an anderen Zweigen
- einen Bezugspunkt, um ihre eigenen Zusammenführungen abzustützen.
    
   \section{Der Ansatz »fliegender Fisch«: Wie's einfacher geht} \label{d76e3792}
        
    

    
  \par
  
Es gibt auch eine einfachere, wenn auch leicht beschränkende Variante
des Vorangegangenen. Bei ihr frieren die Entwickler am Zweig ihre
Arbeit ein, wenn die Hauptversion eine Verschmelzung durchführt,
worauf die Entwickler der Hauptversion einen völlig neuen Zweig
abspalten, der dann den alten ersetzt. Die Entwickler des alten
Zweiges wechseln zu dem neuen über und fahren mit der Arbeit fort.
Dieser Zyklus wird wiederholt, bis es keinen Bedarf mehr für den Zweig
gibt. Das Ganze geht in etwa so (in Steno - wir setzen wie immer
voraus, dass jrandom@floss die Hauptversion bearbeitet und
qsmith@paste den Zweig):
     

     
      \begin{tt} \begin{scriptsize} user@linux floss/ \$  
cvs tag -b BRANCH-1
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux paste/ \$ 
cvs checkout -r BRANCH-1 myproj
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Die Arbeit sowohl an der Hauptversion als auch am Zweig wird
aufgenommen; irgendwann beratschlagen sich die Entwickler und
beschließen, dass es Zeit ist, die Änderungen aus dem Zweig in die
Hauptversion einfließen zu lassen:
     

     
      \begin{tt} \begin{scriptsize} user@linux paste/ \$  
cvs ci -m ''committing all uncommitted changes''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux floss/ \$ 
cvs update -j BRANCH-1
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Alle Änderungen werden aus dem Zweig übernommen; die Entwickler am
Zweig unterbrechen ihre Arbeit, während die Entwickler der
Hauptversion alle Konflikte auflösen, den Commit vornehmen, eine
Markierung setzen und einen neuen Zweig erzeugen:
     

     
      \begin{tt} \begin{scriptsize} user@linux floss/ \$ 
cvs ci -m ''merged from BRANCH-1''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux floss/ \$ 
cvs tag merged-from-BRANCH-1
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux floss/ \$ 
cvs tag -b BRANCH-2
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
jetzt schalten die Entwickler des (alten) Zweiges ihre Arbeitskopien
auf den neuen Zweig um; sie wissen, dass sie keine noch nicht per
Commit bestätigten Änderungen verlieren, denn als die Verschmelzung
begonnen wurde, waren sie up-to-date, und der neue Zweig stammt aus
einer Hauptversion, welche die Änderungen des alten Zweigs übernommen
hat:
     

     
      \begin{tt} \begin{scriptsize} user@linux paste/ \$  
cvs update -r BRANCH-2
       \end{scriptsize} \end{tt} \linebreak 
     
     
     
  \par
  
Das Ganze wird so endlos fortgesetzt, man muss nur BRANCH-1 durch
BRANCH-2 und (vorher) BRANCH-2 durch BRANCH-3 ersetzen.
     

     
  \par
  
Ich nenne das die Technik »fliegender Fisch«, denn der Zweig
»entspringt« mehrfach der Hauptversion, reist ein kurzes Stück und
»taucht« dann wieder in sie ein. Der Vorteil dieses Ansatzes ist,
dass er einfach ist (die Hauptversion übernimmt immer alle Änderungen
des jeweiligen Zweigs) und dass die Entwickler der Zweigversion nie
Konflikte auflösen müssen - sie bekommen einfach jedes Mal einen neuen
sauberen Zweig ausgehändigt, an dem sie dann arbeiten. Der Nachteil
ist, dass die »abgespaltenen« Entwickler natürlich jedes Mal untätig
herumsitzen müssen, während ihre Änderungen in die Hauptversion
propagiert werden - und das kann beliebig viel Zeit in Anspruch
nehmen, abhängig davon, wie viele Konflikte aufgelöst werden müssen.
Ein weiterer Nachteil liegt darin, dass dann viele kleine unbenutzte
Zweige herumliegen anstelle von vielen unbenutzten nichtverzweigenden
Marken. Wie auch immer - wenn es Sie nicht stört, Millionen winziger,
überflüssiger Zweige zu haben und Sie weitgehend reibungslose
Zusammenführungen zu schätzen wissen, dann ist »fliegender Fisch« der
- was die mentale Buchhaltung angeht - leichteste Weg.
     

     
  \par
  
Egal wie Sie nun vorgehen, Sie sollten immer versuchen, die Trennung
so kurz wie möglich zu halten. Wenn Zweig und Hauptversion zu lange
ohne Verschmelzung laufen, können sie schnell nicht nur unter
textuellem, sondern auch unter semantischem Auseinanderdriften leiden.
Änderungen, die nur textuell in Konflikt stehen, sind sehr leicht
aufzulösen. Änderungen, die auf Grund unterschiedlicher Konzepte in
Konflikt stehen, erweisen sich häufig als die am schwersten zu
findenden und zu behebenden. Die Abspaltung eines Zweiges, die für die
Entwickler so befreiend wirkt, ist auch genau deswegen so gefährlich,
da sie beide Seiten von den Auswirkungen durch die Änderungen auf der
jeweils anderen Seite abschirmt ... eine gewisse Zeit lang. Wenn Sie
Verzweigungen verwenden, wird Kommunikation lebensnotwendig: Jeder
muss doppelt sicherstellen, dass er die Pläne der anderen kennt und so
programmiert, dass alle auf dasselbe Ziel zusteuern.
     

     
Verzweigungen und Schlüsselwortexpansion sind natürliche Feinde
     

     
  \par
  
Wenn Ihre Dateien RCS-Schlüsselwörter verwenden, die im Zweig und in
der Hauptversion unterschiedlich expandiert werden, werden Sie beinahe
unter Garantie bei jedem Versuch, die beiden zusammenzuführen,
»unberechtigte« Konflikte erhalten. Selbst wenn überhaupt nichts
geändert wurde, überlappen die Schlüsselwörter, und ihre Expansionen
passen nicht zueinander. Wenn beispielsweise README.txt in der
Hauptversion
     

     
  \par
  
\$Revision: 1.5 \$
     

     
  \par
  
enthält, im Zweig hingegen
     

     
  \par
  
\$Revision: 1.5 \$
     

     
  \par
  
dann werden Sie beim Verschmelzen folgenden Konflikt bekommen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
cvs update -j Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/README.txt,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.14 and 1.14.2.1 into README.tx\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cat README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} ....\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+ README.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} key \$Revision: 1.5 \$\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} =======\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} key \$Revision: 1.5 \$\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+ 1.14.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Um dies zu verhindern, können Sie die Expansion zeitweise
unterdrücken, indem Sie beim Zusammenführen die Option -kk mit
übergeben (ich weiß nicht, wofür -kk steht, vielleicht »kill
keywords«11?):
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
cvs update -kk -j Exotic\_Greetings-branch
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/myproj/README.txt,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.14.2.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.14 and 1.14.2.1 into README.txt\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
floss\$ cat README.txt
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \$Revision: 1.5 \$\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Eines müssen Sie allerdings beachten: Wenn Sie -kk verwenden, wird
auch jede andere Schlüsselwortexpansion, die Sie vielleicht für die
Datei gesetzt haben, außer Kraft gesetzt. Das ist besonders bei
Binärdateien ein Problem, die normalerweise auf -kb gesetzt sind
(wodurch jede Schlüsselwortexpansion und jede Zeilenendekonvertierung
unterdrückt wird). Wenn Sie also Binärdateien aus dem Zweig in die
Hauptversion überführen möchten, benutzen Sie -kk nicht. Behandeln
Sie stattdessen die auftretenden Konflikte von Hand.
    
   \section{Die Quelltexte Dritter verfolgen: Vendor Branches} \label{d76e3955}
        
    

    
  \par
  
Manchmal müssen an einer Software eines externen Lieferanten lokale
Änderungen vorgenommen werden, die bei jedem erhaltenen Update der
Software aktualisiert werden müssen, nämlich wenn der Lieferant die
lokalen Änderungen nicht übernimmt. (Und es gibt eine Menge in Frage
kommender legitimer Gründe, warum das nicht möglich ist.)
     

     
  \par
  
CVS kann einen bei dieser Aufgabe unterstützen. Der dafür zuständige
Mechanismus nennt sich »Vendor Branches«, was soviel heißt wie
»abgezweigte Version eines externen Lieferanten«. »Vendor Branches«
sind die Erklärung für die (bisher) verwirrenden letzten beiden
Optionen, die man bei cvs import angeben kann: vendor tag und release
tag; beide habe ich in Kapitel 2 unter den Tisch fallen lassen.
     

     
  \par
  
Das Ganze funktioniert so: Der allererste Import gleicht jedem anderen
initialisierenden Import eines CVS-Projekts (abgesehen davon, dass Sie
besondere Sorgfalt bei der Wahl der Lieferantenmarkierung (»vendor
tag«) und der Versionsmarke (»release tag«) walten lassen sollten):
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
pwd
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} /home/jrandom/theirproj-1.0\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs import -m ''Import of TheirProj 1.0'' theirproj Them THEIRPROJ\_1\_0
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} N theirproj/INSTALL\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/README\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/src/main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/src/parse.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/doc/random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/doc/manual.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} No conflicts created by this import\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Dann checken Sie irgendwo eine Arbeitskopie aus, nehmen Ihre lokalen
Anpassungen vor und führen commit auf dem Ergebnis aus:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
cvs -q co theirproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U theirproj/INSTALL\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/README\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/manual.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/digest.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/parse.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cd theirproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
emacs src/main.c src/digest.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} M src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M src/main.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
floss\$ cvs -q ci -m ''changed digestion algorithm; added comment to main''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in src/digest.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/theirproj/src/digest.c,v \verb+<+-- digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in src/main.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/theirproj/src/main.c,v \verb+<+-- main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2; previous revision: 1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Ein Jahr später erreicht uns die nächste Version der Software von
Them, Inc., und Sie müssen Ihre lokalen Änderungen darin einbauen.
Deren und Ihre Änderungen überlappen ein wenig. Them, Inc. hat eine
neue Datei hinzugefügt, einige Dateien geändert, die Sie nicht berührt
haben, aber auch zwei Dateien verändert, an denen auch Sie Änderungen
vorgenommen haben.
     

     
  \par
  
Zunächst müssen Sie einen neuen import durchführen, diesmal von den
neuen Quelltexten. Nur wenig ist gegenüber dem ersten Import anders:
Sie importieren dasselbe Projekt in das Archiv, mit demselben Vendor
Branch. Das Einzige, was sich unterscheidet, ist der Release Tag:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$   
floss\$ pwd
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} /home/jrandom/theirproj-2.0\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs -q import -m ''Import of TheirProj 2.0'' theirproj Them THEIRPROJ\_2\_0
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} U theirproj/INSTALL\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} N theirproj/TODO\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/README\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs import: Importing /usr/local/newrepos/theirproj/src\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} C theirproj/src/main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/parse.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} C theirproj/src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs import: Importing /usr/local/newrepos/theirproj/doc\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/manual.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 2 conflicts created by this import.\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Use the following command to help the merge:\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  cvs checkout -jThem:yesterday -jThem theirproj\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Himmel! Nie haben wir CVS so hilfsbereit gesehen. Es sagt uns in der
Tat, welches Kommando wir eingeben sollen, um die Änderungen
zusammenzuführen. Und was es uns sagt, ist sogar fast richtig! Das
angegebene Kommando funktioniert - vorausgesetzt, Sie passen yesterday
so an, dass es einen beliebigen Zeitraum bezeichnet, der mit
Sicherheit den ersten Import einschließt, aber nicht den zweiten. Ich
bevorzuge aber leicht die Methode, die den Release Tag verwendet:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs checkout -j THEIRPROJ\_1\_0 -j THEIRPROJ\_2\_0 theirproj
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs checkout: Updating theirproj\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/INSTALL\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/README\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/TODO\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs checkout: Updating theirproj/doc\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/manual.txt\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/doc/random.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} cvs checkout: Updating theirproj/src\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/theirproj/src/digest.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.1.1.1 and 1.1.1.2 into digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} rcsmerge: warning: conflicts during merge\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/theirproj/src/main.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.1.1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.1.1.1 and 1.1.1.2 into main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} U theirproj/src/parse.c\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Beachten Sie, dass import uns zwei Konflikte gemeldet hat, merge
hingegen scheint nur einen Konflikt zu bemerken. Es scheint, als wäre
die Vorstellung eines Konflikts, die CVS beim Importieren hat, leicht
abweichend von den übrigen Fällen. Grundsätzlich meldet import einen
Konflikt, wenn sowohl Sie als auch der Lieferant zwischen dem letzten
Import und dem jetzigen eine Datei verändert hat. Wenn es jedoch an
das Zusammenführen geht, dann hält update es mit der normalen
Definition von »Konflikt«: überlappende Änderungen. Änderungen, die
nicht überlappen, werden auf übliche Art zusammengeführt; die Datei
wird dann als geändert markiert.
     

     
  \par
  
Ein schneller diff bestätigt, dass nur eine der Dateien
Konfliktmarkierungen trägt:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} C src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M src/main.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs diff -c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Index: src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ===============================================\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/theirproj/src/digest.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.2 digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} *** src/digest.c 1999/07/26 08:02:18 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -- src/digest.c 1999/07/26 08:16:15\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} *** 3,7 ****\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -- 3,11 ----\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  void\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  digest ()\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} + \verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+ digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  printf (''gurgle, slorp$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} + =======\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} + printf (''mild gurgle$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} + \verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+ 1.1.1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  }\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Index: src/main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ==========================================\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/theirproj/src/main.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.2 main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} *** src/main.c 1999/07/26 08:02:18 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -- src/main.c 1999/07/26 08:16:15\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} *** 7,9 ****\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} -- 7,11 ----\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  {\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize}  }\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} +\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} + /* I, the vendor, added this comment for no good reason. */\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Jetzt gilt es nur noch, die Konflikte - wie bei jedem anderen
Verschmelzen von Zweigen auch - auszuräumen:
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
emacs src/digest.c src/main.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize}  ...\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs -q update
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} M src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} M src/main.c\end{scriptsize} \end{tt} \linebreak
      \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$  
cvs diff src/digest.c
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} cvs diff src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Index: src/digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} =====================================\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/newrepos/theirproj/src/digest.c,v\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} diff -r1.2 digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} 6c6\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+<+ printf (''gurgle, slorp$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} --\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} \verb+>+ printf (''mild gurgle, slorp$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
Und noch ein Commit der Änderungen
     

     
      \begin{tt} \begin{scriptsize} user@linux \~{}/ \$  
floss\$ cvs -q ci -m ''Resolved conflicts with import of 2.0''
       \end{scriptsize} \end{tt} \linebreak 
      \begin{tt} \begin{scriptsize} Checking in src/digest.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/theirproj/src/digest.c,v \verb+<+-- digest.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.3; previous revision: 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} Checking in src/main.c;\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} /usr/local/newrepos/theirproj/src/main.c,v \verb+<+-- main.c\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} new revision: 1.3; previous revision: 1.2\end{scriptsize} \end{tt} \linebreak
      \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
     

     
  \par
  
und dann auf die nächste Version des Lieferanten warten. (Natürlich
sollten Sie überprüfen, ob Ihre lokalen Anpassungen noch
funktionieren!)
    
   \section{Der bescheidene Guru} \label{d76e4421}
        
    

    
  \par
  
Wenn Sie alles in diesem Kapitel gelesen und verstanden haben (besser
noch: damit experimentiert haben), können Sie beruhigt davon ausgehen,
dass CVS keine unangenehmen Überraschungen mehr für Sie bereithält -
zumindest solange niemand größere neue Funktionen einbaut, was mit
einer gewissen Regelmäßigkeit geschieht. Alles, was Sie über CVS
wissen müssen, um ein richtiges Projekt zu überstehen, wurde
angesprochen.
    

    
  \par
  
Bevor Ihnen das zu Kopf steigt, lassen Sie mich den Vorschlag
wiederholen, den ich erstmals in Kapital 4 gemacht habe, nämlich die
Mailingliste info-cvs@gnu.org zu abonnieren. Trotz des kümmerlichen
Signal-zu-Rauschen-Abstands, der den meisten Internet-Mailinglisten
gemeinsam ist, sind die wenigen Signale, die durchkommen, das Warten
fast immer wert. Ich war die ganze Zeit, in der ich dieses Kapitel
geschrieben habe, auf der Mailingliste (während der übrigen Kapitel
auch), und Sie wären erstaunt, wenn Sie wüssten, wie viele wichtige
Details ich über das Verhalten von CVS anhand der Diskussionen gelernt
habe. Wenn Sie CVS ernsthaft einsetzen wollen - besonders, wenn Sie
CVS-Administrator für eine Entwicklergruppe sind -, können Sie sehr
stark von dem gesammelten Wissen der anderen ernsthaften Anwender
profitieren.
    



    \newcounter{d6e4433} 
                  \begin{list}{\arabic{d6e4433}}
                  {\usecounter{d6e4433}}
        
     
	\item 
 Anm. d. Übers.: wörtlich: Sandkasten, also ein persönlicher
Spielplatz, der keinen Einfluss auf die restliche Welt hat.
     

     
	\item 
 Anm. d. Übers.: Watch, Plural watches: wörtlich Beobachtung, im
Sinne von unter Beobachtung stellen
     

     
	\item 
 Anm. d. Übers.: Auf Deutsch und in Kürze der Inhalt: Die Datei
notify bestimmt, wohin Benachrichtigungen geschickt werden. Der erste
Eintrag in einer Zeile ist ein regulärer Ausdruck, gegen den das
Verzeichnis, in dem es zu Änderungen kommt, getestet wird. Wenn es
»passt«, dann wird der Rest der Zeile zum Aufrufen eines
Filterprogramms verwendet, das seine Eingabe über die Standardeingabe
erhält, wobei %s für den zu benachrichtigenden Benutzer steht. Statt
eines regulären Ausdrucks kann auch »ALL« oder »DEFAULT« genommen
werden, dann gilt es für alle Verzeichnisse
     

     
	\item 
 Anm. d. Übers.: In etwa »Nachricht von CVS«
     

      
	\item 
 Anm. d. Übers.: Die verwendete Log-Nachricht »turned on watch
notification« kann man mit »habe die Watch-Benachrichtigung aktiviert«
übersetzen.
     

     
	\item 
 Anm. d. Übers.: Kurze Übersetzung des Dateiinhalts: Die Datei
checkoutlist dient dazu, weitere Dateien zu ermöglichen, die Teil des
administrativen Bereichs von CVS sind. Der erste Eintrag einer Zeile
bezeichnet den Namen der Datei, die aus den korrespondierenden
RCS-Dateien in {\bf \$CVSROOT/CVSROOT} ausgecheckt werden kann. Der Rest der
Zeile beinhaltet die Fehlermeldung, die verwendet wird, wenn die
Datei nicht ausgecheckt werden kann. [...]
     

     
	\item 
 Anm. d. Übers.: Etwa: knapp daneben
     

     
	\item 
 Anm. d. Übers.: Loslassen, abgeben
     

     
	\item 
 Anm. d. Übers.: to annotate: anmerken
     

     
	\item 
 Anm. d. Übers.: Von engl. »differences'' - Unterschiede, hier:
Änderungen
     

     
	\item 
 Anm. d. Übers.: Schlüsselwörter ausschalten
     

    \end{list}
   
	\ref{inhalt.tex}


	\end{document}
	
