

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

	
 % uebersicht
 % Copyright 
 % Lizenz: GPL
 % 
 % $Name: $
 % $Revision: 1.1.2.10 $
 % $Source: /cvsroot/selflinux/tutorial/software/development/version_control/cvs/cvs_buch/kapitel_2/kapitel_2,v $
 % SelfLinux-0.7.2
 %
 % Diese Datei ist Teil von SelfLinux http://www.selflinux.de
 %
 %%% $Id: kapitel_2,v 1.1.2.10 2003/02/03 23:49:29 jgraber Exp $

	\title{Übersicht CVS}


	
	    \author{}
	    %\url{mailto:}
    

	\maketitle

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

    \par{Layout}
    Matthias Hagedorn
	    %\url{mailto:herbert-kw@t-online.de}
    
    	\par{Lizenz}
	GPL
 
	\tableofcontents{}

        
	\section{CVS-Grundlagen} \label{d74e60}
        
 
  

  
  \par
  
Dieses Kapitel führt in die grundlegenden Konzepte von CVS ein und
gibt dann eine tiefer gehende Erläuterung des täglichen CVS-Einsatzes.
Nach dessen Lektüre sind Sie auf dem besten Wege, ein CVS-Guru zu
werden.
  

  
  \par
  
Wenn Sie noch nie CVS (oder ein anderes Versionsverwaltungssystem)
benutzt haben, kann es leicht sein, dass Sie durch einige der zu
Grunde liegenden Annahmen ins Stolpern geraten. Was anfänglich
anscheinend für die meiste Verwirrung sorgt ist, dass CVS für zwei
scheinbar unabhängige Aufgaben eingesetzt wird: Historienverwaltung
und Zusammenarbeit. Es zeigt sich jedoch, dass diese beiden
Funktionen eng miteinander verbunden sind.
  

  
  \par
  
Die Historienverwaltung wurde notwendig, weil Benutzer den momentanen
Zustand eines Programmes mit dem an einem bestimmten Zeitpunkt der
Vergangenheit vergleichen wollten. Zum Beispiel bringt ein
Programmierer während der Implementation einer neuen Funktion das
Programm in einen ziemlich fehlerhaften Zustand, in dem das Programm
auch wahrscheinlich erst einmal bleiben wird, bis diese neue Funktion
endgültig fertig implementiert ist. Unglücklicherweise ist genau dies
meist der Zeitpunkt, zu dem ein Benutzer die Beschreibung eines
Fehlers in der letzten veröffentlichten Version einschickt. Um diesen
Fehler zu finden (der auch in der aktuellen Entwicklerversion
enthalten sein kann), muss das Programm zuerst wieder in einen
benutzbaren Zustand gebracht werden.
  

  
  \par
  
Diesen Zustand wieder herzustellen stellt dann kein Problem dar, wenn
die Historie zu den Quelltexten mit CVS verwaltet wird. Ein Entwickler
kann einfach sagen: »Gib mir den Quelltext, wie er vor drei Wochen
war«, oder vielleicht: »Gib mir den Quelltext so, wie er war, als wir
die letzte öffentliche Version freigegeben haben.« Wenn Sie bisher
noch nie einen so praktischen Zugriff auf frühere Versionen hatten,
werden Sie überrascht sein, wie schnell man davon abhängig werden
kann. Persönlich verwende ich eine Revisionskontrolle bei allen
meinen Programmierprojekten - dies hat mich schon oft gerettet.
  

  
  \par
  
Um zu verstehen, was dies mit der Unterstützung der Zusammenarbeit zu
tun hat, müssen wir zunächst die Mechanismen etwas näher betrachten,
mit denen CVS es ermöglicht, dass mehrere Personen zusammen an einem
Projekt arbeiten. Doch zuvor sehen wir uns einen Mechanismus an, den
CVS nicht bietet (oder der zumindest nicht zu empfehlen ist):
Dateisperren. Wenn Sie bereits andere Versionsverwaltungssysteme
benutzt haben, werden Sie schon mit dem Entwicklungsmodell
Sperren-Ändern-Freigeben vertraut sein, bei dem ein Entwickler zuerst
den exklusiven Schreibzugriff auf die zu bearbeitende Datei (eine
Sperre) bekommen muss, die Veränderungen anbringt und dann die Sperre
wieder freigibt, damit andere Entwickler auf diese Datei zugreifen
können. Wenn jemand anders bereits eine Sperre für diese Datei
gesetzt hat, so muss er diese zuerst wieder freigeben, bevor man
selbst eine Sperre setzen und Veränderungen anbringen kann. (In
manchen Implementationen kann man diese Sperre auch {\bf stehlen}, 
was
aber für den anderen eine böse Überraschung und außerdem kein guter
Stil ist!)
  

  
  \par
  
Dieses System ist dann brauchbar, wenn sich die Entwickler kennen,
wissen, wer was zu einem bestimmten Zeitpunkt machen möchte, und, im
Falle von Zugriffskonflikten, schnell miteinander kommunizieren
können. Wenn jedoch die Entwicklergruppe zu groß wird oder zu
weiträumig verstreut ist, knabbert die Verwaltung der Sperren an der
eigentlichen Arbeitszeit; dies wird zu einem ständigen Problem und
entmutigt viele, wirkliche Arbeit zu leisten.
  

  
  \par
  
CVS verfolgt einen ausgereifteren Ansatz. Anstatt von den Entwicklern
zu verlangen, sich gegenseitig zu koordinieren, erlaubt CVS den
Entwicklern gleichzeitiges Arbeiten, übernimmt die Integration der
Veränderungen und behält mögliche Konflikte im Auge. Dieser Prozess
benutzt das {\bf Kopieren-Modifizieren-Zusammenfassen-Modell}, das 
wie
folgt funktioniert:
  

  
  \par
  
Entwickler A fordert eine Arbeitskopie von CVS an (ein
Verzeichnisbaum, der alle Dateien eines Projektes enthält). Dies wird
auch {\bf Checking out} einer Arbeitskopie genannt, wie das Ausleihen
eines Buches aus einer Bibliothek.
  

  
  \par
  
Entwickler A arbeitet frei an seiner Arbeitskopie. Zum gleichen
Zeitpunkt können auch andere Entwickler an ihren eigenen Arbeitskopien
fleißig sein. Weil alle Kopien unabhängig voneinander sind, gibt es
auch keine Konflikte - es ist so, als hätten alle Entwickler ihre
eigene Kopie des gleichen Buches aus der Bibliothek, und sie alle
schreiben, unabhängig voneinander, Kommentare an die Ränder oder
bestimmte Seiten vollständig neu.
  

  
  \par
  
Entwickler A beendet seine Veränderungen und sendet diese mit einer
{\bf Log-Nachricht}, also einem Kommentar, der beschreibt, was der 
Zweck
der Veränderungen war, an den CVS-Server ({\bf commit}). Dies ist 
damit
vergleichbar, die Bibliothek darüber zu informieren, welche
Veränderungen gemacht wurden und warum. Die Bibliothek lässt diese
wiederum in eine Hauptkopie einfließen, wo sie damit für alle Zeit
aufgezeichnet werden.
  

  
  \par
  
In der Zwischenzeit können andere Entwickler CVS dazu veranlassen, die
Bibliothek abzufragen, um herauszufinden, ob die Hauptkopie in
jüngster Zeit verändert wurde. Ist dem so, aktualisiert CVS
automatisch deren Arbeitskopie. (Dieser Teil grenzt an Magie und ist
einfach wunderbar, ich hoffe, Sie wissen dies zu schätzen. Stellen
Sie sich vor, wie anders die Welt wäre, wenn echte Bücher so
funktionieren würden!)
  

  
  \par
  
Soweit es CVS betrifft, sind alle Entwickler eines Projektes gleich.
Zu entscheiden, wann ein {\bf Commit} oder eine Aktualisierung 
durchgeführt
wird, ist eine Sache der persönlichen Einschätzung oder
der Projektregeln. Eine übliche Strategie bei Programmierprojekten ist es,
immer eine Aktualisierung zu machen, bevor die Arbeit an größeren
Veränderungen begonnen wird, und einen {\bf Commit} erst dann zu 
machen,
wenn die Veränderungen vollständig und getestet sind, sodass die
Hauptkopie immer in einem funktionsfähigen Zustand ist.
  

  
  \par
  
Vielleicht fragen Sie sich, was passiert, wenn die Entwickler A und B
in ihren Arbeitskopien unterschiedliche Veränderungen an dem gleichen
Stück (Quell-)Text vornehmen und beide ihre Veränderungen mittels
{\bf Commit} abschicken? Dies wird Konflikt genannt und von CVS 
entdeckt,
sobald Entwickler B, versucht seine Veränderungen abzuschicken.
Anstatt Entwickler B zu erlauben fortzufahren, gibt CVS bekannt, dass
es einen Konflikt gefunden hat, und setzt Konfliktmarkierungen
(leicht zu erkennende Marken im Text) an die in Konflikt stehenden
Stellen im Text seiner Kopie. Diese Stellen beinhalten beide
Veränderungen und sind derart angeordnet, dass sie leicht verglichen
werden können. Entwickler B muss sich nun alles noch einmal ansehen
und eine neue Version abschicken, die den Konflikt auflöst.
Vielleicht müssen die beiden Entwickler miteinander reden, um die
Sache zu klären. CVS alarmiert nur die Entwickler über die Konflikte;
es ist an den Menschen, diese tatsächlich zu lösen.
  

  
  \par
  
Was ist nun mit der Hauptkopie? In der offiziellen CVS-Terminologie
wird diese das Archiv (Repository) eines Projektes genannt. Das Archiv
ist schlicht nur ein Datei-/Verzeichnisbaum, der auf einem Server
gespeichert ist. Ohne zu stark in die Tiefe der Struktur zu gehen
(siehe jedoch Kapitel 4), werfen wir einen Blick darauf, was das
Archiv leisten muss, um den Anforderungen des
{\bf Checkout-Commit-Aktualisieren-Zyklus} gerecht zu werden.
  

  
  \par
  
Stellen Sie sich folgendes Szenario vor:
  

  
  \par
  
Zwei Entwickler, A und B, führen gleichzeitig einen {\bf Checkout} 
des
gleichen Projektes aus. Das Projekt befindet sich noch am
Ausgangspunkt - es wurden noch von niemandem Veränderungen
per {\bf Commit} an das Archiv geschickt, sodass sich noch alle 
Dateien in
ihrem ursprünglichen Zustand befinden.
  

  
  \par
  
Entwickler A beginnt sofort mit seiner Arbeit und führt schon bald den
ersten {\bf Commit} seiner Änderungen aus.
  

  
  \par
  
In der Zwischenzeit sieht Entwickler B fern.
  

  
  \par
  
Entwickler A arbeitet, als ob es kein Morgen gäbe, und führt einen
zweiten {\bf Commit} für einen weiteren Satz von Veränderungen aus. 

Das
Archiv enthält nun die Originaldateien, gefolgt von As zweitem Satz
von Veränderungen, gefolgt von diesem Satz an Veränderungen.
  

  
  \par
  
In der Zwischenzeit spielt Entwickler B Videospiele.
  

  
  \par
  
Plötzlich schließt sich Entwickler C dem Projekt an und macht einen
{\bf Checkout} einer Arbeitskopie aus dem Archiv. Die Kopie von 
Entwickler C
enthält As erste zwei Sätze von Veränderungen, weil diese schon im
Archiv enthalten waren, als C für seine Arbeitskopie einen 
{\bf Checkout}
gemacht hat.
  

  
  \par
  
Entwickler A arbeitet weiter wie von Geistern besessen, vollendet
seinen dritten Satz an Veränderungen und führt abermals einen 
{\bf Commit}
aus.
  

  
  \par
  
Zu guter Letzt, nichts ahnend von der jüngsten rasanten Aktivität,
entscheidet Entwickler B, dass es Zeit wird, an die Arbeit zu gehen.
Er kümmert sich nicht darum, eine Aktualisierung seiner Arbeitskopie
durchzuführen; er fängt an, Dateien zu bearbeiten, von denen einige
jene Dateien sein könnten, an denen A gearbeitet hat. Kurz darauf
führt Entwickler B seinen ersten {\bf Commit} dieser Veränderungen 
aus.
  

  
  \par
  
An diesem Punkt können nun zwei Dinge passieren. Wenn keine der von
Entwickler B bearbeiteten Dateien von A bearbeitet wurde, dann ist der
{\bf Commit} erfolgreich. Wenn CVS jedoch merkt, dass einige der 
Dateien
von B verglichen mit den aktuellen Dateien des Archivs veraltet sind
und diese auch von B in seiner Arbeitskopie verändert wurden,
informiert CVS B darüber, dass er eine Aktualisierung durchführen
muss, bevor ein {\bf Commit} durchgeführt werden kann.
  

  
  \par
  
Wenn Entwickler B die Aktualisierung durchführt, fügt CVS alle
Veränderungen von A in Bs lokale Kopien der Dateien ein. Einige von As
Veränderungen können mit Bs noch nicht abgeschickten Veränderungen in
Konflikt geraten, manche nicht. Die Teile, welche nicht in Konflikt
stehen, werden einfach ohne weitere Komplikationen in Bs Kopie
eingefügt; die in Konflikt stehenden müssen zuerst von B bereinigt
werden, bevor der {\bf Commit} durchgeführt werden kann.
  

  
  \par
  
Wenn Entwickler C nun eine Aktualisierung durchführt, bekommt er
mehrere Sätze an Veränderungen aus dem Archiv: den dritten 
{\bf Commit} von
A, den ersten von B und vielleicht den zweiten von B (wenn B die
Konflikte aufgelöst hatte).
  

  
  \par
  
Damit CVS Veränderungen in der richtigen Reihenfolge an die Entwickler
verteilen kann, deren Arbeitskopien unter Umständen unterschiedlich
stark veraltet sind, muss das Archiv alle {\bf Commits} seit 
Projektbeginn
aufzeichnen. In der Praxis speichert das CVS-Archiv diese als
aufeinander folgende {\bf Diffs}. Daher kann CVS auch noch für sehr 

alte
Arbeitskopien den Unterschied zwischen den Dateien der Arbeitskopien
und dem aktuellen Stand des Archivs bestimmen und dadurch die
Arbeitskopie effizient wieder auf den aktuellen Stand bringen. Für
Entwickler ist es dadurch einfach, die Historie des Projektes
einzusehen und zu jedem Zeitpunkt sogar sehr alte Arbeitskopien
wieder zum Leben zu erwecken.
  

  
  \par
  
Obwohl das Archiv genau genommen das gleiche Resultat mit anderen
Methoden erreichen könnte, ist das Abspeichern der {\bf Diffs} eine 

einfache
und intuitive Methode, die notwendige Funktionalität zu
implementieren. Dieser Prozess hat den zusätzlichen Vorteil, dass CVS
durch die korrekte Anwendung von {\bf patch} jeden vorangegangenen 
Zustand
des Verzeichnisbaumes wiederherstellen und damit jede Arbeitskopie von
einem Zustand in einen anderen überführen kann. Es erlaubt jedem,
einen {\bf Checkout}. Daher des Projektes in einem womöglich 
vergangenen Zustand
zu machen. Es kann ebenso die Unterschiede im {\bf diff}-Format 
zwischen
zwei Zuständen des Projektes aufzeigen, ohne dabei irgendeine
Arbeitskopie zu beeinflussen.
  

  
  \par
  
Daher sind genau diese Funktionen, die den vernünftigen Zugriff auf
die Historie eines Projektes zulassen, auch dafür nützlich, es einer
dezentralen, unkoordinierten Entwicklergruppe zu ermöglichen, an
einem Projekt zusammenzuarbeiten.
  

  
  \par
  
Die Details, wie ein Archiv angelegt wird, Benutzerzugriffe
administriert werden und CVS-spezifische Dateiformate gehandhabt
werden (diese werden in Kapitel 4 beschrieben), können Sie erst
einmal außer Acht lassen. Im Augenblick konzentrieren wir uns darauf,
wie Veränderungen an einer Arbeitskopie durchgeführt werden können.
  

  
  \par
  
Doch zuerst noch eine kurze Übersicht der Terminologie:
  

  
Revision - Eine Veränderung an einer Datei oder Menge von Dateien, die
durch einen Commit abgeschlossen wurde. Eine Revision ist eine
Momentaufnahme eines sich ständig verändernden Projektes.
  

  
Archiv - Die Hauptkopie, in der CVS die vollständige Revisionshistorie
eines Projektes speichert. Jedes Projekt hat genau ein Archiv.
  

  
Arbeitskopie - Die Kopie, mit der gearbeitet wird und die tatsächlich
verändert wird. Es kann mehrere Arbeitskopien eines bestimmten
Projektes geben; im Allgemeinen hat jeder Entwickler seine eigene
Kopie.
  

  
Checkout - Eine Arbeitskopie von dem Archiv anfordern. Die
angeforderte Kopie stellt den Zustand des Projektes zu dem Zeitpunkt
dar, zu dem die Kopie angefordert wurde; wenn Sie oder andere
Entwickler Veränderungen vornehmen, müssen commit und update
durchgeführt werden, um die eigenen Veränderungen zu
»veröffentlichen« und die der anderen Mitentwickler sehen zu können.
  

  
Commit - Senden der eigenen Veränderungen zum zentralen Archiv. Auch
Check-in genannt.
  

  
Log-Nachricht - Ein Kommentar der bei einem Commit einer Revision
angehängt wird und die vorgenommenen Veränderungen beschreibt. Andere
Entwickler können durch die Log-Nachrichten blättern und erhalten so
die Antwort auf die Frage, was in dem Projekt passiert ist.
  

  
Aktualisierung (update) - Veränderungen von anderen Entwicklern vom
Archiv in die eigene Arbeitskopie aufnehmen und aufzeigen, ob die
eigene Arbeitskopie noch nicht durch commit abgeschickte
Veränderungen enthält.
  

  
Konflikt - Situation, in der zwei Entwickler Veränderungen im gleichen
Teil der gleichen Datei per commit abzuschicken versuchen. CVS bemerkt
solche Konflikte und benachrichtigt die Entwickler, aber die
Entwickler müssen diese selbst auflösen.
  
 \section{Ein Tag mit CVS} \label{d74e269}
        

  

  
  \par
  
Der folgende Teil des Buches gibt eine Einführung in die grundlegende
Benutzung von CVS, gefolgt von einer beispielhaften Sitzung, welche
die typischsten CVS-Operationen beinhaltet. Im Laufe dessen werden
wir auch beginnen, die interne Arbeitsweise von CVS zu betrachten.
  

  
  \par
  
Obwohl Sie zur Benutzung die Implementation von CVS nicht bis ins
kleinste Detail verstehen müssen, ist ein Grundwissen über seine
Funktionsweise unschätzbar wertvoll, um ein bestimmtes Ergebnis zu
erzielen. CVS verhält sich eher wie ein Fahrrad als ein Auto, denn
seine Mechanismen sind für jeden transparent, der einen aufmerksamen
Blick darauf wirft. Wie mit einem Fahrrad kann man einfach
aufspringen und sofort anfangen zu fahren. Wenn man sich jedoch einen
Augenblick Zeit nimmt, um genauer zu betrachten, wie das Getriebe
funktioniert, kann man wesentlich besser fahren. (Im Falle von CVS
bin ich mir nicht sicher, ob diese Transparenz ein bewusstes
Entwicklungsziel oder ein Unfall gewesen ist, aber es scheint eine
Eigenschaft zu sein, die auf viele freie Programme zutrifft.
Durchschaubare Implementationen haben den Vorteil, Benutzer dazu zu
motivieren, zu dem Projekt beitragende Entwickler zu werden, indem sie
von Anfang an mit den internen Prozessen konfrontiert werden.)
  

  
  \par
  
Unsere Führung findet in einer Unix-Umgebung statt. CVS läuft auch
unter Windows oder dem Macintosh Betriebssystem, und Tim Endres von
Ice Engineering hat sogar einen Java-Client geschrieben, 
der überall dort läuft, wo auch Java
läuft. Dennoch wage ich die grobe Schätzung, dass die Mehrheit der
CVS-Benutzer - derzeitig und potenziell - wahrscheinlich mit einer
Unix Kommandozeilenumgebung arbeiten. Sollten Sie kein solcher sein,
so sollten die Beispiele dieser Führung dennoch leicht auf andere
Oberflächen übertragbar sein. Haben Sie die Konzepte einmal
verstanden, können Sie sich an jede CVS-Oberfläche setzen und damit
arbeiten (vertrauen Sie mir, ich habe dies schon oft gemacht).
  

  
  \par
  
Die Beispiele der Führung orientieren sich an Benutzern, die CVS für
Programmierprojekte einsetzen werden. Trotzdem sind CVS-Operationen
auf alle Textdokumente anwendbar, nicht nur auf Quelltexte.
  

  
  \par
  
In der Führung wird auch davon ausgegangen, dass Sie CVS bereits
installiert haben (es ist bei vielen bekannten freien Unix-Systemen
bereits enthalten, wodurch Sie es bereits haben könnten, ohne es zu
wissen) und dass Sie Zugriff auf ein Archiv haben. Auch wenn Sie diese
Voraussetzungen nicht erfüllen, können Sie dennoch von dieser Führung
profitieren. In Kapitel 4 werden
Sie lernen, wie man CVS installiert
und wie Archive angelegt werden.
  

  
  \par
  
Davon ausgehend, dass CVS installiert ist, sollten Sie sich einen
Augenblick Zeit nehmen, die Online-Dokumentation zu CVS zu finden.
Gewöhnlich als das {\bf Cederqvist} bekannt (nach Per 
Cederqvist, dem
ursprünglichen Autor), liegt es dem Quelltextpaket von CVS bei und ist
die wohl aktuellste verfügbare Referenz. Der Text ist im
Texinfo-Format geschrieben und sollte auf Unix-Systemen in der
{\bf Info}-Hierarchie zu finden sein. Sie können dieses entweder mit
dem Kommandozeilen {\bf info}-Programm lesen
  
    
  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
info cvs
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
oder durch die Tastenkombination 

   CTRL+H

und dann {\bf i} in Emacs. Wenn
keines derer bei Ihnen funktioniert, fragen Sie den nächsten Unix-Guru
(oder lesen Sie Kapitel 4, 
Installation). Wenn Sie regelmäßig mit CVS
arbeiten wollen, sollten Sie auf jeden Fall das {\bf Cederqvist} zur 
Hand haben.
  

 \section{CVS aufrufen} \label{d74e340}
        

  
   
  
  \par
  
CVS ist ein einzelnes Programm, kann aber viele verschiedene Aktionen
ausführen: {\bf Update}, {\bf Commit}, Verzweigung 
(Branch), {\bf Diff},  und so weiter.
Wenn Sie CVS aufrufen, müssen Sie angeben, welche Aktion
Sie ausführen wollen. Daraus folgt das Format für CVS-Aufrufe:
  

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

  
  \par
  
Zum Beispiel
  

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

  
  \par
  
und so weiter. (Aber versuchen Sie nicht, eines dieser Kommandos in
dieser Form auszuführen; solange Sie sich noch nicht in einer
Arbeitskopie befinden, wird noch nichts passieren, wozu wir aber gleich kommen.)
  

  
  \par
  
Sowohl CVS als auch das Kommando können zusätzliche Optionen bekommen.
Optionen, die das Verhalten von CVS unabhängig von dem auszuführenden
Kommando verändern, heißen globale Optionen; kommandospezifische
Optionen heißen einfach Kommandooptionen. Globale Optionen stehen immer links 
des Kommandos; Kommandooptionen rechts davon. Also ist bei
  

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

  
  \par
  
{\bf -Q} eine globale Option und {\bf --p}  eine 
Kommandooption. (Falls es Sie
interessiert, {\bf --Q}  bedeutet {\bf leise} - also 
alle Diagnosemeldungen
unterdrücken und Fehlermeldungen nur dann ausgeben, wenn das Kommando aus 
irgendeinem Grund gar nicht verarbeitet werden
kann; {\bf --p}  bedeutet, das Ergebnis des 
{\bf Update}  auf der Standardausgabe
auszugeben, anstatt es in Dateien zu schreiben.)
  

 \section{Zugriff auf ein Archiv} \label{d74e417}
        

  


  
  \par
  
Bevor überhaupt irgendetwas ausgeführt werden kann, muss CVS der
Ursprungsort des Archivs, auf das zugegriffen werden soll, mitgeteilt
werden. Dies trifft dann nicht mehr zu, wenn schon eine Arbeitskopie
durch einen {\bf Checkout}   geholt wurde - jede Arbeitskopie weiß, aus
welchem Archiv sie stammt, wodurch CVS das Archiv automatisch aus
einer bestimmten Arbeitskopie ableiten kann. Nehmen wir aber dennoch
an, Sie haben noch keine Arbeitskopie und müssen daher CVS explizit
mitteilen, wohin es sich wenden soll. Dies geschieht mit der globalen
Option {\bf -d}   ({\bf -d}   steht für {\bf directory}, eine Option, für die es eine
historische Begründung gibt, obwohl {\bf -r}   für {\bf Repository} vielleicht
besser gewesen wäre), gefolgt von dem Pfad zu dem Archiv.
Nehmen wir zum Beispiel an, das Archiv befindet sich auf der lokalen
Maschine in {\bf /usr/local/cvs}   (ein Standardort):
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -d /usr/local/cvs Kommando
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
In vielen Fällen befindet sich das Archiv jedoch auf einer anderen
Maschine, und es muss daher über das Netzwerk zugegriffen werden. CVS
stellt mehrere Netzwerkzugriffsmethoden zur Verfügung; welche
eingesetzt werden soll, hängt von den Sicherheitsansprüchen des
Archivservers ab (im Folgenden {\bf der Server} genannt). Den Server für
verschiedene Zugriffsmethoden einzurichten, wird in Kapitel 4
beschrieben; an dieser Stelle soll nur der Teil des Clients behandelt
werden.
  

  
  \par
  
Glücklicherweise haben alle Netzwerkzugriffsmethoden eine gemeinsame
Aufrufsyntax. Grundsätzlich muss zur Spezifikation eines nicht lokalen
Archivs lediglich ein längerer Pfad zum Archiv angegeben werden.
Zuerst wird die Zugriffsmethode angegeben, zu beiden Seiten mit
Doppelpunkten abgetrennt, gefolgt von dem Benutzernamen und dem
Servernamen (zusammengesetzt mit einem @-Zeichen), einem weiteren
Doppelpunkt als Trenner und letztendlich dem Pfad des
Archivverzeichnisses auf dem Server.
  

  
  \par
  
Betrachten wir die pserver-Zugriffsmethode, die für
{\bf passwort-authentisierten Server} steht:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs login
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} (Logging in to jrandom@cvs.foobar.com)\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} CVS password: (hier das CVS Passwort eingeben)\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 

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

  
  \par
  
Der lange Archivname nach {\bf -d} sagte CVS, die pserver-Zugriffsmethode
mit dem Benutzernamen jrandom auf dem Server cvs.foobar.com zu
verwenden, der ein CVS-Archiv in {\bf /usr/local/cvs}  hat. Übrigens muss
der Hostname nicht {\bf cvs.irgendetwas.com} lauten; dies ist eine übliche
Übereinkunft, aber es hätte auch einfach folgendermaßen sein können:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -d :pserver:jrandom@fisch.foobar.org:/usr/local/cvs Kommando
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Das tatsächlich verwendete Kommando war {\bf login}, das verifiziert, ob Sie
autorisiert 

sind, mit dem Archiv zu arbeiten. Das {\bf login}-Kommando fragt
Sie anschließend nach einem Passwort und kontaktiert dann den Server,
um das Passwort zu verifizieren. Guter Unix-Sitte folgend, hat cvs
{\bf login} keine Ausgabe, wenn das Login korrekt ablief; wenn es
fehlschlägt, wird eine Fehlermeldung ausgegeben (zum Beispiel weil
das Passwort falsch war).
  

  
  \par
  
Man muss sich von seiner lokalen Maschine nur einmal bei einem
bestimmten CVS-Server anmelden. Nach einem erfolgreichen Login
speichert CVS das Passwort in der Datei {\bf .cvspass} in Ihrem
Home-Directory. Diese Datei wird anschließend immer wieder eingelesen,
wenn auf ein Archiv mit der pserver-Methode zugegriffen wird, wodurch
{\bf login} nur einmal beim ersten Zugriff auf einen bestimmten CVS-Server
von einer bestimmten Client-Maschine aus durchgeführt werden muss.
Natürlich kann cvs {\bf login} jederzeit wiederholt werden, wenn sich zum
Beispiel das Passwort geändert hat.
  

  
{\bf Bemerkung} \linebreak 

pserver ist derzeit die einzige Zugriffsmethode, die ein erstes Login
wie dieses benötigt; mit den anderen können direkt normale
CVS-Kommandos ausgeführt werden.
  

  
  \par
  
Ist einmal die Authentifizierungsinformation in der {\bf .cvspass}-Datei
gespeichert, können andere CVS-Kommandos in der gleichen
Kommandozeilensyntax ausgeführt werden:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs Kommando
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Die pserver-Methode unter Windows anzuwenden kann einen weiteren
Schritt benötigen. Windows kennt das Unix-Konzept der
Home-Verzeichnisse nicht, weshalb CVS nicht weiß, wo die
cvspass-Datei abgespeichert werden soll. Hierzu muss explizit ein
Verzeichnis angegeben werden. Normalerweise wird das Hauptverzeichnis
der Festplatte C: als Home-Verzeichnis angegeben:
  

  \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        
HOME-Verzeichnis
   \end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
     
C:\WINDOWS> set HOME=C:
C:\WINDOWS> cvs -d :pserver:jrandom@cvs.foobar.com:/usr/local/cvs login
(Logging in to jrandom@cvs.foobar.com)
CVS password: (hier Passwort eingeben)
C:\WINDOWS>
      
   \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}
 
  
  \par
  
Jedes Verzeichnis des Dateisystems ist möglich. Netzwerklaufwerke
sollten jedoch vermieden werden, da der Inhalt der {\bf .cvspass}-Datei dann
für jeden ersichtlich wäre, der Zugriff auf dieses Laufwerk hat.
  

  
  \par
  
Zusätzlich zu {\bf pserver} unterstützt CVS die {\bf ext}-Methode (die ein
externes Programm zur Verbindung benutzt, bspw. {\bf rsh} oder {\bf ssh}), {\bf kserver} 
(für das Kerberos-Sicherheitssystem Version 4) und {\bf gserver} (welches
das GSSAPI, oder auch Generic Security Services API, benutzt und auch
Kerberos Version 5 oder größer verwenden kann). Diese Methoden sind
ähnlich wie pserver, haben aber jede für sich ihre Eigenheiten.
  

  
  \par
  
Von diesen ist wohl die {\bf ext}-Methode die üblichste. Wenn Sie sich an
dem Server über rsh oder ssh anmelden können, können Sie die
{\bf ext}-Methode benutzen. Sie können dies folgendermaßen ausprobieren:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rsh -l jrandom cvs.foobar.com
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} Password hier Ihr Login-Passwort eingeben\linebreak\end{scriptsize} \end{tt} \linebreak
  

  
  \par
  
Angenommen, Sie konnten sich mit {\bf rsh} erfolgreich bei dem Server ein-
und ausloggen, dann sind Sie nun wieder zurück auf Ihrer
ursprünglichen Client-Maschine:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
CVS\_RSH=rsh; export CVS\_RSH
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -d :ext:jrandom@cvs.foobar.com:/usr/local/cvs Kommando
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Die erste Zeile setzt (in der Syntax der Unix Bourne-Shell) die
{\bf CVS\_RSH}-Umgebungsvariable auf {\bf rsh}, was CVS mitteilt, {\bf rsh} als das
Programm zur Verbindung zu benutzen. Die zweite Zeile kann irgendein
CVS-Kommando sein; Sie werden nach Ihrem Passwort gefragt, sodass CVS
das Login beim Server durchführen kann.
  

  
  \par
  
Wenn Sie eine C-Shell anstatt einer Bourne-Shell benutzen, versuchen
Sie Folgendes:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
setenv CVS\_RSH rsh
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
und unter Windows versuchen Sie dies:
  

  \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        
c:$\backslash$WINDOWS
   \end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
    
C:\WINDOWS> set CVS_RSH=rsh
    
   \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

  
  \par
  
Der Rest dieser Führung verwendet die Bourne-Shell-Syntax; Sie können
dies für Ihre Umgebung bei Bedarf umsetzen.
  

  
  \par
  
Um {\bf ssh} (die Secure-Shell) anstatt von {\bf rsh} zu benutzen, muss nur die
Umgebungsvariable CVS\_RSH entsprechend gesetzt werden:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
CVS\_RSH=ssh; export CVS\_RSH
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Lassen Sie sich nicht davon verwirren, dass die Variable {\bf CVS\_RSH}
heißt, Sie ihren Inhalt aber auf {\bf ssh} setzen. Es gibt historische
Gründe dafür (die allumfassende Unix-Entschuldigung, ich weiß).
{\bf CVS\_RSH} kann auf ein beliebiges Programm verweisen, welches das Login
auf einer anderen Maschine sowie Kommandos ausführen und deren
Ausgabe empfangen kann. Nach {\bf rsh} ist {\bf ssh} wohl das verbreitetste dieser
Programme, obwohl es sicherlich noch andere gibt. Wichtig ist, dass
diese Programme den Datenstrom in keiner Weise verändern dürfen. Dies
disqualifiziert die Windows-NT {\bf rsh}, weil diese zwischen den Unix- und
DOS-Zeilenumbrüchen konvertiert (oder es zumindest versucht). Sie
müssten sich in diesem Fall eine andere rsh für Windows besorgen oder
eine andere Zugriffsmethode benutzen.
  

  
  \par
  
Die {\bf gserver}- und {\bf kserver}- Methoden werden nicht so oft wie die anderen
benutzt und werden hier nicht behandelt. Diese sind bezüglich dessen,
was bisher behandelt wurde, recht ähnlich; Näheres findet sich im
Cederqvist.
  

  
  \par
  
Verwenden Sie nur ein Archiv und wollen nicht jedes Mal {\bf -d repos} 
eingeben, können Sie einfach die {\bf CVSROOT} -Umgebungsvariable (die
vielleicht {\bf CVSREPOS}  hätte genannt werden sollen, doch dafür ist es nun
zu spät) wie folgt setzen:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
CVSROOT=/usr/local/cvs
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
export CVSROOT
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
echo \$CVSROOT
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /usr/local/cvs\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
oder vielleicht
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
CVSROOT=:pserver:jrandom@cvs.foobar.com:/usr/local/cvs
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
export CVSROOT
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
echo \$CVSROOT
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} :pserver:jrandom@cvs.foobar.com:/usr/local/cvs\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Der Rest dieser Führung geht davon aus, dass {\bf CVSROOT}  auf das Archiv
verweist, sodass die Beispiele die {\bf -d}  Option nicht enthalten. Wenn auf
mehrere Archive zugegriffen werden soll, sollte die
{\bf CVSROOT} -Umgebungsvariable nicht gesetzt werden und anstatt dessen mit
{\bf -d repos}  das benötigte Archiv angegeben werden.
  

 \section{Ein neues Projekt beginnen} \label{d74e771}
        

  

  
  \par
  
Wenn Sie den Umgang mit CVS erlernen wollen, um mit einem Projekt zu
arbeiten, das bereits mit CVS verwaltet wird (das heißt, es befindet
sich bereits irgendwo in einem Archiv), dann sollten Sie vielleicht
mit dem Abschnitt »Eine Arbeitskopie auschecken« fortfahren. Möchten
Sie allerdings existierende Quelltexte unter die Kontrolle von CVS
stellen, ist dies der für Sie passende Abschnitt. Beachten Sie, dass
immer noch davon ausgegangen wird, dass Sie Zugriff auf ein bereits
bestehendes Archiv haben; wenn Sie zuerst eines anlegen müssen, lesen
Sie bitte Kapitel 4.
  

  
  \par
  
Ein bestehendes Projekt in CVS zu übernehmen, wird importieren
genannt. Das CVS-Kommando dazu lautet, wie Sie sich sicherlich bereits
gedacht haben,
  

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

  
  \par
  
abgesehen davon, dass es noch ein paar zusätzliche Optionen benötigt
(und an der richtigen Stelle im Dateisystem ausgeführt werden muss), 
um korrekt ausgeführt zu werden. Zuerst wechseln Sie in das Hauptverzeichnis
Ihres Projektes:
  

  
   \begin{tt} \begin{scriptsize} 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} README.txt a-subdir/ b-subdir/ hello.c\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Dieses Projekt besteht aus zwei Dateien - {\bf README.txt} und {\bf hello.c} - im
Hauptverzeichnis, zuzüglich zwei Unterverzeichnissen -
a{\bf -unterverzeichnis} und {\bf b-unterverzeichnis} - und noch einiger weiterer
Dateien in den Unterverzeichnissen, die hier nicht angezeigt werden.
Wenn ein Projekt importiert wird, importiert CVS alles aus der
Verzeichnisstruktur, ausgehend von dem aktuellen Verzeichnis den
ganzen Baum hinab. Daher sollten Sie sich vergewissern, dass sich nur
solche Dateien in den Verzeichnissen befinden, die auch permanenter
Bestandteil des Projektes werden sollen. Jegliche alten
Sicherheitskopien, Schmierdateien und so weiter sollten entfernt
werden.
  

  
  \par
  
Die allgemeine Syntax des {\bf import}-Kommandos ist
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs import -m ''log nachr.'' projname hersteller-marke versions-marke
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Die Option {\bf -m} (m = message, Nachricht) spezifiziert eine kurze
Nachricht, die den {\bf Import} beschreibt. Dies wird dann die erste
Log-Nachricht des gesamten Projektes; jeder nachfolgende {\bf Commit} wird
ebenfalls eine eigene Log-Nachricht bekommen. Diese Nachrichten sind
verpflichtend; wird die Option {\bf -m} nicht angegeben, startet CVS
automatisch einen Texteditor (unter Verwendung der Umgebungsvariablen 
\$EDITOR), damit Sie eine Log-Nachricht eingeben können.
Nachdem die Log-Nachricht abgespeichert wurde, wird der Import weiter
durchgeführt.
  

  
  \par
  
Das nächste Argument der Kommandozeile ist der Projektname (hier wird
»myproject« verwendet). Dies ist der Name, anhand dessen ein {\bf Checkout}
des Projektes aus dem Projektarchiv durchgeführt wird. (Was
tatsächlich passiert ist, dass ein Verzeichnis mit diesem Namen im
Archiv angelegt wird, doch mehr dazu in Kapitel 4). Der nun
auszuwählende Name muss nicht der gleiche wie der des aktuellen
Verzeichnisses sein, obwohl er es in den meisten Fällen wohl sein
wird.
  

  
  \par
  
Die Argumente hersteller-marke und versions-marke dienen nur als
Verwaltungsinformationen für CVS. Sie brauchen sich zu diesem
Zeitpunkt nicht darum zu kümmern; es spielt praktisch keine Rolle,
was Sie dafür wählen. In Kapitel 6 werden die seltenen Umstände
beschrieben, unter denen diese relevant sind. Hier werden wir einen
Benutzernamen und {\bf start} für diese Argumente benutzen.
  

  
  \par
  
Wir können nun den Import starten:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs import -m ''initial import into CVS'' myproj jrandom start
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} N myproj/hello.c\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} N myproj/README.txt\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} cvs import: Importing /usr/local/cvs/myproj/a-subdir\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} N myproj/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} cvs import: Importing /usr/local/cvs/myproj/a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} N myproj/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} cvs import: Importing /usr/local/cvs/myproj/b-subdir\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} N myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} No conflicts created by this import\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Herzlichen Glückwunsch! Wenn Sie dieses oder ein ähnliches Kommando
ausgeführt haben, haben Sie schlussendlich etwas ausgeführt, was das
Archiv verändert.
  

  
  \par
  
Wenn Sie sich die Ausgabe des {\bf Import}-Kommandos noch einmal durchlesen,
werden Sie feststellen, dass CVS den Dateinamen einen einzelnen
Buchstaben vorangestellt hat - in diesem Fall {\bf N} für {\bf neue Datei}.
Die Verwendung eines einzelnen Buchstabens an der linken Position, um
den Status einer Datei anzuzeigen, ist bei den Ausgaben eines Kommandos von CVS üblich. Wir werden dies auch später bei {\bf Checkout} und {\bf Update} sehen.
  

  
  \par
  
Sie könnten nun denken, dass Sie, nachdem Sie gerade das Projekt
importiert haben, in den aktuellen Verzeichnissen sofort arbeiten
können. Dies ist jedoch nicht der Fall. Das aktuelle Verzeichnis ist
immer noch keine CVS-Arbeitskopie. Es war die Quelle für das
{\bf import}-Kommando, richtig, aber es wurde nicht alleine durch die
Tatsache, in CVS importiert worden zu sein, auf magische Art und Weise
in eine Arbeitskopie verwandelt. Um eine Arbeitskopie zu erhalten,
müssen Sie eine aus dem Archiv auschecken.
  

  
  \par
  
Zuerst sollten Sie vielleicht jedoch den aktuellen Projektstamm
sichern. Der Grund dafür ist, dass, wenn die Quelltexte einmal im
CVS-Archiv liegen, Sie sich nicht selbst dadurch verwirren sollten,
indem Sie Kopien von Dateien modifizieren, die nicht der
Versionskontrolle unterliegen (und diese Veränderungen daher nicht
Teil der Projekthistorie werden). Sie sollten von nun an alle Ihre
Arbeiten an einer Arbeitskopie vornehmen. Sie sollten jedoch den
gerade importierten Verzeichnisbaum noch nicht entfernen, da Sie noch
nicht überprüft haben, ob das Archiv alle Dateien enthält. Natürlich
können Sie sich dessen zu 99,999 Prozent sicher sein, weil der
{\bf import}-Befehl ohne Fehler ablief, doch warum etwas riskieren? Paranoia
zahlt sich aus, wie jeder Programmierer weiß. Daher führen Sie etwa
wie folgt aus:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} README.txt a-subdir/ b-subdir/ hello.c\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
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
mv myproj was\_myproj
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} was\_myproj/\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
So. Die Originaldateien sind noch vorhanden, sind aber durch den Namen
klar als eine veraltete Version erkennbar, sodass sie nicht im Weg
sind, wenn eine richtige Arbeitskopie geholt wird. Nun kann ein
{\bf Checkout} durchgeführt werden.
  

  \section{Eine Arbeitskopie auschecken} \label{d74e977}
        

  

  
  \par
  
Das Kommando, um einen {\bf Checkout} durchzuführen, ist genau das, was Sie
sich sicherlich schon gedacht haben:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs checkout myproj
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} cvs checkout: Updating myproj\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} cvs checkout: Updating myproj/a-subdir\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} cvs checkout: Updating myproj/a-subdir/subsubdir\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} cvs checkout: Updating myproj/b-subdir\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 \~{}/ \$ 
ls
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} myproj/ was\_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/ README.txt a-subdir/ b-subdir/ hello.c\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Achtung - Ihre erste Arbeitskopie! Der Inhalt ist genau derselbe wie
der, den Sie gerade importiert haben, zuzüglich eines
Unterverzeichnisses {\bf CVS}. In diesem werden von CVS Informationen zur
Versionskontrolle gespeichert. Genauer gesagt, es existiert nun in
jedem Unterverzeichnis des Projektes ein CVS-Unterverzeichnis:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls a-subdir
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} CVS/ subsubdir/ whatever.c\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls a-subdir/subsubdir/
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} CVS/ fish.c\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls b-subdir
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} CVS/ random.c\end{scriptsize} \end{tt} \linebreak
  


  
{\bf Tipp} \linebreak 

Die Tatsache, dass CVS die Informationen zur Versionskontrolle in
Unterverzeichnissen namens CVS ablegt, bedeutet, dass Ihr Projekt
niemals eigene Unterverzeichnisse mit dem Namen CVS enthalten kann.
Ich habe aber praktisch noch nie davon gehört, dass dies ein Problem
gewesen wäre.
  

  
  \par
  
Bevor Dateien modifiziert werden, lassen Sie uns einen Blick in diese
Blackbox werfen:
  

  
   \begin{tt} \begin{scriptsize} 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} Entries Repository Root\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat Root
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /usr/local/cvs\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat Repository
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} myproject\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
Hier ist nichts besonders Mysteriöses. Die Datei {\bf Root} verweist auf das
Archiv, und die Datei {\bf Repository} verweist auf ein Projekt innerhalb
des Archivs. Lassen Sie es mich erklären, wenn dies auf Anhieb etwas
verwirrend erscheint.
  

  
  \par
  
Die Terminologie von CVS sorgt seit langem für Verwirrung. Der Begriff
»Archiv« wird für zwei unterschiedliche Dinge benutzt. Manchmal ist
damit das Hauptverzeichnis eines Archivs gemeint (zum Beispiel
{\bf /usr/local/cvs}), das mehrere Projekte enthalten kann; die Datei {\bf Root}
verweist dorthin. Doch manchmal ist damit ein projektspezifisches
Unterverzeichnis innerhalb des Archiv-Root gemeint (zum Beispiel
{\bf /usr/local/cvs/myproject}, {\bf /usr/local/cvs/deinprojekt},
{\bf /usr/local/cvs/Fisch}). Die Datei {\bf Repository} innerhalb des
CVS-Unterverzeichnisses hat diese Bedeutung.
  

  
  \par
  
Innerhalb dieses Buches bedeutet »Archiv« allgemein Root (also das
übergeordnete Hauptarchiv), obwohl es gelegentlich auch ein
projektspezifisches Unterverzeichnis bezeichnen kann. Sollte die
eigentliche Intention nicht aus dem Kontext hervorgehen, wird dies im
Text erklärt.
  

  
  \par
  
Beachten Sie, dass die Datei {\bf Repository} manchmal mit einem absoluten
Pfad anstatt eines relativen auf das Projekt verweist. Dies ist ein
wenig redundant mit der {\bf Root} Datei:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd CVS
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat Root
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} :pserver:jrandom@cvs.foobar.com:/usr/local/cvs\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat Repository
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /usr/local/cvs/myproject\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
In der Datei {\bf Entries} werden Informationen über die einzelnen Dateien
eines Projektes abgelegt. Jede Zeile beschäftigt sich dabei mit einer
Datei, und es finden sich dort auch nur Einträge für die Dateien und
Unterverzeichnisse des nächst übergeordneten Verzeichnisses. Hier die
Haupt-{\bf CVS/Entries}-Datei in myproject:
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat Entries
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} /hello.c/1.1.1.1/Sun Apr 18 18:18:22 1999//\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} D/a-subdir////\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} D/b-subdir////\end{scriptsize} \end{tt} \linebreak
  

  
  \par
  
Jede Zeile folgt dem Format
  

  
  \par
  
/dateiname/revisionsnummer/Zeitstempel//
      

  
  \par
  
und die Zeilen der Verzeichnisse werden mit einem {\bf D} eingeleitet.
(CVS verwaltet keine Historie über Veränderungen der Verzeichnisse
selbst, weshalb die Felder Revisionsnummer und Zeitstempel leer
bleiben.)
  

  
  \par
  
Die Zeitstempel bezeichnen Datum und Uhrzeit der letzten
Aktualisierung der Dateien in der Arbeitskopie (in universeller Zeit,
nicht lokaler Zeit). Auf diese Weise kann CVS einfach unterscheiden,
ob eine Datei seit dem letzten {\bf checkout}, {\bf update} oder {\bf commit} verändert
wurde. Wenn sich der Zeitstempel des Dateisystems von dem in der
{\bf CVS/Entries}-Datei unterscheidet, weiß CVS (ohne überhaupt das Archiv
zu überprüfen), dass die Datei wahrscheinlich verändert wurde.
  

  
  \par
  
Betrachtet man die {\bf CVS/*}-Dateien in den Unterverzeichnissen
  

  
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd a-subdir/CVS
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat Root
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /usr/local/cvs\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat Repository
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} myproj/a-subdir\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat Entries
    \end{scriptsize} \end{tt} \linebreak 
   \begin{tt} \begin{scriptsize} /whatever.c/1.1.1.1/Sun Apr 18 18:18:22 1999//\end{scriptsize} \end{tt} \linebreak
   \linebreak\begin{tt} \begin{scriptsize} D/subsubdir////\end{scriptsize} \end{tt} \linebreak
   \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
    \end{scriptsize} \end{tt} \linebreak 
  

  
  \par
  
stellt man fest, dass {\bf Root} immer noch auf das gleiche Archiv verweist,
{\bf Repository} jedoch auf die Position des Verzeichnisses innerhalb des
Projektes zeigt und die {\bf Entries}-Datei andere Einträge enthält.
  

  
  \par
  
Direkt nach einem {\bf Import} wird die Revisionsnummer einer jeden Datei
des Projektes mit 1.1.1.1 angezeigt. Diese initiale Revisionsnummer
ist eine Art Spezialfall, weshalb wir hier nicht näher darauf
eingehen; wir werden uns näher mit Revisionsnummern beschäftigen, wenn
ein {\bf Commit} von ein paar Veränderungen durchgeführt wurde.
  

  
  \par
  
   {\bf 
Version kontra Revision
   }
  

  
  \par
  
Die von CVS intern verwalteten Revisionsnummern sind unabhängig von
der Versionsnummer des Softwareproduktes, von dem diese ein Teil sind.
Nehmen wir zum Beispiel ein aus drei Dateien bestehendes Projekt,
deren Revisionsnummern am 3. Mai 1999 1.2, 1.7 und 2.48 waren. An
diesem Tag wird eine neue Version dieser Software zusammengepackt und
als SlickoSoft Version 3 freigegeben. Dies ist eine reine
Marketingentscheidung und beeinflusst die CVS-Revisionen überhaupt
nicht. Die CVS-Revisionsnummern sind für die Kunden nicht sichtbar
(es sei denn, sie haben Zugriff auf das Archiv); die einzig sichtbare
Nummer ist {\bf 3} in {\bf Version 3}. Soweit es CVS betrifft, hätte die
Version auch 1729 lauten können - die Versionsnummer (oder auch
{\bf Release}-Nummer) hat nichts mit der internen Verwaltung von
Veränderungen durch CVS zu tun.
  

  
  \par
  
Um Verwirrung zu vermeiden, werde ich den Begriff {\bf Revision}
verwenden, um mich einzig auf die internen Revisionsnummern von
Dateien unter der Kontrolle von CVS zu beziehen. Ich werde trotzdem
CVS ein {\bf Versionskontrollsystem} nennen, weil
{\bf Revisionskontrollsystem} doch etwas komisch klingt.
  

  \subsection{Eine Veränderung einbringen} \label{d74e1309}
        

   

   
  \par
  
Das Projekt, in seinem momentanen Zustand, macht noch nicht allzu
viel. Hier ist der Inhalt von {\bf hello.c}:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Lassen Sie uns nun die erste Veränderung seit dem {\bf Import} anbringen; es
wird die Zeile
   
    
   
    \begin{tt} \begin{scriptsize} printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
eingefügt, direkt nach {\bf Hello}, world!. Starten Sie Ihren bevorzugten
Texteditor und führen die Änderung durch:
   

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

   
  \par
  
Dies war eine recht simple Veränderung, eine, bei der man nicht so
schnell vergessen kann, was an getan hat. Bei einem größeren und
komplexeren Projekt ist es aber recht wahrscheinlich, dass man eine
Datei bearbeitet, von etwas anderem unterbrochen wird und erst einige
Tage später wieder dahin zurückkehrt und sich nun nicht mehr daran
erinnern kann, was man tatsächlich oder ob überhaupt verändert hat.
Dies bringt uns zur ersten Situation {\bf CVS rettet Dein Leben}: die
eigene Arbeitskopie mit dem Archiv vergleichen.
   

  

  \subsection{Herausfinden, was man selbst und andere getan haben: update und diff} \label{d74e1380}
        

   

   
  \par
  
Zuvor erwähnte ich {\bf Update} als eine Methode, Veränderungen aus dem
Archiv in die eigene Arbeitskopie einfließen zu lassen - also als eine
Methode, die Veränderungen anderer Entwickler zu bekommen. {\bf Update} ist
jedoch etwas komplexer; es vergleicht den Gesamtzustand der
Arbeitskopie mit dem Zustand des Projektes im Archiv. Auch wenn nichts
im Archiv seit dem letzten {\bf Checkout} verändert wurde, könnte sich
dennoch etwas in der Arbeitskopie verändert haben, und {\bf update} zeigt
dies dann auch auf:
   

   
    \begin{tt} \begin{scriptsize} 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} M hello.c\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
  
Das {\bf M} neben {\bf hello.c} bedeutet, dass die Datei seit dem letzten {\bf Checkout}
modifiziert wurde und die Veränderungen noch nicht mit {\bf Commit} in das
Archiv eingebracht wurden.
   

   
  \par
  
Manchmal ist alles, was man möchte, herauszufinden, welche Dateien man
bearbeitet hat. Möchte man jedoch einen detaillierteren Blick auf die
Veränderungen werfen, kann man einen kompletten Report im {\bf diff}-Format
anfordern. Das {\bf diff}-Kommando vergleicht die möglicherweise
modifizierten Dateien der Arbeitskopie mit den entsprechenden
Gegenstücken im Archiv und zeigt jegliche Unterschiede auf:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs diff
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs diff: Diffing .\end{scriptsize} \end{tt} \linebreak
    \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/cvs/myproj/hello.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} diff -r1.1.1.1 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} 6a7\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+>+ printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs diff: Diffing a-subdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs diff: Diffing a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs diff: Diffing b-subdir\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Dies hilft schon weiter, auch wenn es durch eine Menge überflüssiger
Ausgaben ein wenig obskur erscheinen mag. Für den Anfang können die
meisten der ersten paar Zeilen ignoriert werden. Diese benennen nur
die Datei des Archivs und zeigen die Nummer der letzten eingecheckten
Revision. Unter bestimmten Umständen kann auch das eine nützliche
Information sein (wir werden später genauer dazu kommen), sie wird
aber nicht gebraucht, wenn man nur einen Eindruck davon bekommen
möchte, welche Veränderungen an der Arbeitskopie stattgefunden haben.
   

   
  \par
  
Ein größeres Hindernis, den {\bf Diff} zu lesen, stellen die Meldungen von
CVS bei jedem Wechsel in ein Verzeichnis während des {\bf Updates} dar. Dies
kann während eines langen {\bf Updates} bei großen Projekten nützlich sein,
da es einen Anhaltspunkt bietet, wie lange das {\bf Update} wohl noch dauern
wird. Doch jetzt sind sie beim Lesen des {\bf Diff} schlicht im Weg. Also
sagen wir CVS mit der globalen {\bf -Q}-Option, dass es nicht melden soll,
wo es gerade arbeitet:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff
     \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/cvs/myproj/hello.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} diff -r1.1.1.1 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} 6a7\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+>+ printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Besser - zumindest ist ein Teil der überflüssigen Ausgaben weg.
Dennoch ist der {\bf Diff} noch schwer zu lesen. Er sagt aus, dass an Zeile
6 eine neue Zeile hinzugekommen ist (was also jetzt Zeile 7 ist), und
dass deren Inhalt
   

   
    \begin{tt} \begin{scriptsize} printf (''Goodbye, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
ist. Das vorangestellte {\bf \verb+>+} in dem {\bf Diff} bedeutet, dass diese Zeile in
der neuen Version vorhanden ist, nicht aber in der älteren.
   

   
  \par
  
Das Format kann jedoch noch lesbarer gemacht werden. Die meisten
empfinden das »Kontext«-{\bf Diff}-Format als leichter zu lesen, da es ein
paar Kontextzeilen zu beiden Seiten einer Veränderung mit anzeigt.
Kontext {\bf Diffs} werden durch die zusätzliche Option {\bf -c} zu {\bf diff} erzeugt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff -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/cvs/myproj/hello.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} diff -c -r1.1.1.1 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/19 02:17:07\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,7 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ---4,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\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
   

   
  \par
  
Nun, das ist Klarheit! Selbst wenn man nicht gewohnt ist,
Kontext-{\bf Diffs} zu lesen, macht ein kurzer Blick auf die vorangegangene
Ausgabe offensichtlich, was passiert ist: eine neue Zeile wurde
zwischen der Zeile, welche {\bf Hello, world!} ausgibt und der
abschließenden geschweiften Klammer hinzugefügt (das {\bf +} in der ersten
Spalte der Ausgabe markiert eine hinzugefügte Zeile).
   

   
  \par
  
Wir müssen Kontext-{\bf Diffs} nicht perfekt lesen können, dies ist die
Aufgabe von {\bf patch}, es lohnt sich aber dennoch, sich die Zeit zu
nehmen, um eine zumindest ansatzweise Gewöhnung an dieses Format
zu bekommen. Die ersten beiden Zeilen (nach der momentan nutzlosen
Einleitung) sind
   

   
    \begin{tt} \begin{scriptsize} *** hello.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/19 02:17:07\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
und sagen einem, was mit wem »gedifft« wurde. In diesem Fall wurde
Revision 1.1.1.1 von {\bf hello.c} mit einer modifizierten Version der
gleichen Datei verglichen (daher gibt es keine Revisionsnummer für
die zweite Zeile, weil die Veränderungen der Arbeitskopie noch nicht
mit einem {\bf Commit} in das Archiv aufgenommen wurden). Die Zeilen mit
Sternchen und Strichen markieren Teile im späteren Teil des {\bf Diffs}.
Anschließend wird ein Teil der Originaldatei von einer Zeile mit
Sternchen und einem eingefügten Zeilennummernbereich eingeleitet.
Danach folgt eine Zeile mit Strichen mit möglicherweise anderen
Zeilenummernbereichen, die einen Teil der modifizierten Datei
einleiten. Diese Bereiche sind in sich kontrastierenden Paaren
angeordnet (genannt {\bf Hunks}), die eine Seite von der alten Datei und
die andere Seite von der neuen.
   

   
  \par
  
Dieser Diff besteht aus einem Hunk:
   

   
    \begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,7 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 4,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\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
   

   
  \par
  
Der erste Teil dieses Hunks ist leer, was bedeutet, dass keine Teile
der Originaldatei entfernt wurden. Der zweite Teil zeigt an der
entsprechenden Stelle der neuen Datei, dass eine Zeile eingefügt
wurde; diese ist mit{\bf +} markiert. (Wenn diff Auszüge einer Datei
anführt, werden die ersten beiden linken Spalten für spezielle Codes
reserviert, wie bspw. das {\bf +}, sodass der gesamte Auszug um zwei
Zeichen eingerückt erscheint. Die zusätzliche Einrückung wird
natürlich entfernt, bevor der {\bf Diff} wieder als {\bf Patch} angewendet wird.)
   

   
  \par
  
Der Zeilennummernbereich zeigt den Bereich an, den der Hunk
einschließt, samt der Zeilen aus dem Kontext. In der Originaldatei
umfasste der Hunk die Zeilen 4 bis 7; in der neuen Datei sind dies
die Zeilen 4 bis 8 (weil eine Zeile hinzugefügt wurde). Zu beachten
ist, dass {\bf diff} keine Ausschnitte aus der Originaldatei angezeigt hat,
weil nichts entfernt wurde; es wurde nur der Bereich angezeigt und
dann zu der zweiten Hälfte des Hunks übergegangen.
   

   
  \par
  
Hier noch ein zweiter Kontext-{\bf Diff}  eines meiner Projekte:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff -c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Index: cvs2cl.pl\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} =======================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.76\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.76 cvs2cl.pl\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** cvs2cl.pl 1999/04/13 22:29:44 1.76\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- cvs2cl.pl 1999/04/19 05:41:37\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 212,218 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# can contain uppercase and lowercase letters, digits, '-',\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# and '\_'. However, it's not our place to enforce that, so\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# we'll allow anything CVS hands us to be a tag:\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /^$\backslash$s([^:]+): ([0-9.]+)\$/;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} push (@{\$symbolic\_names{\$2}}, \$1);\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} --- 212,218 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# can contain uppercase and lowercase letters, digits, '-',\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# and '\_'. However, it's not our place to enforce that, so\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# we'll allow anything CVS hands us to be a tag:\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /^$\backslash$s([^:]+): ([$\backslash$d.]+)\$/;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} push (@{\$symbolic\_names{\$2}}, \$1);\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
   

   
  \par
  
Das Ausrufungszeichen zeigt an, dass die markierte Zeile zwischen der
neuen und alten Datei unterschiedlich ist. Da keine {\bf +}- oder
{\bf -}-Zeichen vorhanden sind, wissen wir, dass die Gesamtzeilenzahl der
Datei gleich geblieben ist.
   

   
  \par
  
Hier ist noch ein Kontext-{\bf Diff} des gleichen Projektes, diesmal ein
wenig komplizierter:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff -c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Index: cvs2cl.pl\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ===========================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/kfogel/code/cvs2cl/cvs2cl.pl,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.76\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.76 cvs2cl.pl\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** cvs2cl.pl 1999/04/13 22:29:44 1.76\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- cvs2cl.pl 1999/04/19 05:58:51\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 207,217 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} else \# we're looking at a tag name, so parse \& store it\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - \# According to the Cederqvist manual, in node ''Tags'', ''Tag\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - \# names must start with an uppercase or lowercase letter and\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - \# can contain uppercase and lowercase letters, digits, '-',\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - \# and '\_'. However, it's not our place to enforce that, so\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - \# we'll allow anything CVS hands us to be a tag:\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /^$\backslash$s([^:]+): ([0-9.]+)\$/;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} push (@{\$symbolic\_names{\$2}}, \$1);\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ---- 207,212 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 223,228 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 218,225 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} if (/^revision ($\backslash$d$\backslash$.[0-9.]+)\$/) {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \$revision = ''\$1'';\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} + \# This line was added, I admit, solely for the sake of a diff example.\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# If have file name but not time and author, and see date or\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \# author, then grab them:\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Dieser {\bf Diff} hat zwei Hunks. Im ersten wurden fünf Zeilen entfernt
(diese Zeilen sind nur im ersten Teil des Hunks zu sehen, und die
Zeilenanzahl des zweiten Teils weist fünf Zeilen weniger auf). Eine
ununterbrochene Zeile von Sternchen markiert die Grenze zwischen
Hunks. Im zweiten Hunk ist zu sehen, dass zwei Zeilen hinzugefügt
wurden: eine Leerzeile und ein sinnloser Kommentar. Zu beachten ist,
wie die Zeilennummern durch die Effekte des ersten Hunks kompensiert
werden. In der Originaldatei war der Zeilennummernbereich des zweiten
Hunks 223 bis 228; in der neuen Datei, bedingt durch das Entfernen
von Zeilen durch den ersten Hunk, ist der Zeilennummernbereich 218 bis
225.
   

   
  \par
  
Herzlichen Glückwunsch! Sie sind wahrscheinlich nun Experte im Lesen
von Diffs, zumindest soweit Sie es aller Voraussicht nach benötigen
werden.
   

  

  \subsection{CVS und implizite Argumente} \label{d74e1934}
        

   

   
  \par
  
Sie haben vielleicht bemerkt, dass bei jedem bisher verwendeten
CVS-Kommando keine Dateien in der Kommandozeile angegeben wurden. Es
wurde
   

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

   
  \par
  
ausgeführt anstatt
   

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

   
  \par
  
und
   

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

   
  \par
  
anstatt von
   

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

   
  \par
  
Das Prinzip das dahinter steht, ist, dass, wenn keine Dateinamen
angegeben werden, CVS das Kommando auf alle Dateien anwendet, die dazu
als sinnvoll erscheinen. Dies schließt auch Dateien in
Unterverzeichnissen unterhalb des aktuellen Verzeichnisses ein; CVS
durchläuft automatisch auch alle Unterverzeichnisse des
Verzeichnisbaumes. Wenn zum Beispiel {\bf b-subdir/random.c} und
{\bf a-subdir/subsubdir/fish.c} verändert wurden, wäre das Resultat von
update folgendes:
   

   
    \begin{tt} \begin{scriptsize} 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} M hello.c\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} M a-subdir/subsubdir/fish.c\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
oder noch besser:
   

   
    \begin{tt} \begin{scriptsize} 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} M a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} M b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
{\bf Bemerkung} \linebreak 

Die {\bf -q}-Option ist eine Abschwächung der {\bf -Q}-Option. Hätten wir {\bf -Q}
verwendet, hätte das Kommando keinerlei Ausgabe gehabt, da die
Hinweise über Modifikationen als nicht essentielle Informationen
gehandhabt werden. Die {\bf -q}-Option ist weniger streng; Meldungen, die
wir wahrscheinlich sowieso nicht sehen wollten, werden unterdrückt,
und bestimmte nützlichere Meldungen werden durchgelassen.
   

   
  \par
  
Es können bei einem {\bf Update} auch bestimmte Dateien angegeben werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update hello.c b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} M hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} M b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Tatsächlich ist es aber üblich, update ohne Angabe bestimmter Dateien
zu starten. In den meisten Fällen wird man den gesamten
Verzeichnisbaum auf einmal aktualisieren wollen. Zu beachten ist,
dass alle bisherigen {\bf Updates} nur zeigten, dass einige Dateien lokal
modifiziert wurden, weil sich bisher noch nichts im Archiv verändert
hat. Wenn weitere Entwickler mit einem zusammen an dem Projekt
arbeiten, ist es immer möglich, dass {\bf update} Veränderungen aus dem
Archiv holt und in die lokalen Dateien einfließen lässt. In diesem
Fall kann es etwas nützlicher sein, die Dateien zum {\bf Update} explizit zu
benennen.
   

   
  \par
  
Das gleiche Prinzip kann auch auf andere CVS-Kommandos angewendet
werden. Zum Beispiel können die Veränderungen für eine Datei nach der
anderen mit {\bf diff} betrachtet werden
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs diff -c b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Index: b-subdir/random.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/cvs/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} diff -c -r1.1.1.1 random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** b-subdir/random.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- b-subdir/random.c 1999/04/19 06:09:48\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 1 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /* A completely empty C file. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 1,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /* Print out a random number. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} !\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} !\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''a random number$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! }\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
oder es können alle Veränderungen auf einmal angezeigt werden (bleiben
Sie sitzen, dies wird ein großer {\bf Diff}):
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff -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/cvs/myproj/hello.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} diff -c -r1.1.1.1 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/19 02:17:07\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,7 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 4,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\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} Index: 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} RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.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} diff -c -r1.1.1.1 fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** a-subdir/subsubdir/fish.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- a-subdir/subsubdir/fish.c 1999/04/19 06:08:50\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 1 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /* A completely empty C file. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 1,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! while (1) {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''fish$\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} Index: b-subdir/random.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/cvs/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} diff -c -r1.1.1.1 random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** b-subdir/random.c 1999/04/18 18:18:22 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- b-subdir/random.c 1999/04/19 06:09:48\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 1 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /* A completely empty C file. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 1,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! /* Print out a random number. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} !\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} !\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''a random number$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! }\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Wie aus den {\bf Diffs} klar hervorgeht, ist dieses Projekt produktionsreif.
Machen wir also einen {\bf Commit} der Änderungen in das Archiv.
   

  

  \subsection{Commit durchführen} \label{d74e2336}
        

   

   
  \par
  
Das {\bf commit}-Kommando schickt Veränderungen an das Archiv. Werden keine
Dateien angegeben, sendet {\bf commit} alle Veränderungen an das Archiv;
ansonsten kann einer oder können mehrere Dateinamen für den {\bf Commit}
angegeben werden (die anderen Dateien werden in diesem Fall
ignoriert).
   

   
  \par
  
Hier wird eine Datei direkt und zwei werden indirekt an {\bf commit} übergeben:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs commit -m ''print goodbye too'' 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/cvs/myproj/hello.c,v \verb+<+-- hello.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 commit -m ''filled out C code''
     \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/subsubdir/fish.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v \verb+<+-- fish.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 b-subdir/random.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nehmen Sie sich einen Augenblick Zeit, um die Ausgaben sorgfältig zu
lesen. Das meiste ist selbsterklärend. Es fällt jedoch auf, dass die
Revisionsnummern inkrementiert wurden (wie zu erwarten war), die
original Revisionen aber mit 1.1 anstatt 1.1.1.1, wie in der
anfänglich erwähnten {\bf Entries}-Datei, angezeigt wurden.
   

   
  \par
  
Es gibt eine Erklärung für diese Diskrepanz, auch wenn es nicht
sonderlich wichtig ist. Dies betrifft die besondere Bedeutung, die CVS
der Revision 1.1.1.1 beimisst. In den meisten Fällen kann man sagen,
dass Dateien bei einem {\bf Import} die Revisionsnummer 1.1 bekommen, diese
aber - aus Gründen, die nur CVS weiß - als 1.1.1.1 in der
{\bf Entries}-Datei bis zum ersten {\bf Commit} abgelegt wird.
   


   
  \par
  
{\bf Revisionsnummern}
   

   
  \par
  
Jede Datei eines Projektes hat eine eigene Revisionsnummer. Wenn eine
Datei wieder durch einen {\bf Commit} zurückgesendet wird, wird die letzte
Stelle der Revisionsnummer um eins inkrementiert. Daher haben die
verschiedenen Dateien, die ein Projekt bilden, möglicherweise sehr
unterschiedliche Revisionsnummern. Das bedeutet lediglich, dass
einige Dateien öfter verändert (und durch {\bf Commit} übertragen) wurden
als andere.
   

   
  \par
  
(Sie werden sich vielleicht fragen, was es nun mit dem linken Teil der
Revisionsnummern auf sich hat, wenn immer nur der rechte inkrementiert
wird. Tatsächlich wird dieser Teil von CVS nicht automatisch
inkrementiert, jedoch kann ein Benutzer dies anfordern. Dies ist eine
selten benutzte Funktion und wird daher in diesem Kapitel nicht
behandelt.)
   

   
  \par
  
Aus dem benutzten Beispielprojekt wurden gerade Veränderungen an drei
Dateien durch einen {\bf Commit} abgeschickt. Jede dieser Dateien hat nun
die Revisionsnummer 1.2, jedoch haben die restlichen Dateien noch
1.1. Wird ein {\bf Checkout} ausgeführt, werden nur die Dateien mit der
höchsten Revisionsnummer geholt. Die nachfolgende Ausgabe zeigt, was
der Benutzer qsmith sehen würde, wenn er zum aktuellen Zeitpunkt
einen {\bf Ckeckout} von myproject machen würde und die Revisionsnummern
des Hauptverzeichnisses ausgibt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q -d :pserver:qsmith@cvs.foobar.com:/usr/local/cvs 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/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/CVS
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat Entries
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /hello.c/1.2/Mon Apr 19 06:35:15 1999//\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/a-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/b-subdir////\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Unter anderem hat die Datei {\bf hello.c} nun die Revisionsnummer 1.2,
während {\bf README.txt} noch die ursprüngliche Revisionsnummer hat
(Revision 1.1.1.1 oder auch 1.1).
   

   
  \par
  
Wenn er nun die Zeile
   

   
    \begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
in {\bf hello.c} einfügen würde und durch einen {\bf Commit} an das Archiv sendet,
wird die Revisionsnummer wiederum um eins erhöht:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''added new middle line''
     \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 hello.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/hello.c,v \verb+<+-- hello.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nun hat {\bf hello.c} die Revision 1.3, {\bf fish.c} und {\bf random.c} haben noch 1.2,
und alle anderen Dateien haben 1.1.
   

   
{\bf Bemerkung} \linebreak 

Hier wurde das Kommando mit {\bf cvs ci} anstatt {\bf cvs commit} angegeben. Die
meisten CVS-Kommandos haben, um die Tipparbeit zu vereinfachen,
Kurzformen. Von {\bf checkout}, {\bf update} und {\bf commit} sind die Kurzformen {\bf co},
{\bf up} und {\bf ci}. Eine Liste aller Kurzformen kann mit dem Befehl {\bf cvs
--help-}synonyms abgefragt werden.
   

   
  \par
  
In den meisten Fällen kann die Revisionsnummer einer Datei ignoriert
werden. Meistens werden diese Nummern nur für interne
Verwaltungsaufgaben von CVS selbst automatisch verwendet. Dennoch
sind Revisionsnummern sehr nützlich, wenn man ältere Versionen einer
Datei holen möchte (oder dagegen einen {\bf Diff} macht).
   

   
  \par
  
Die Untersuchung der {\bf Entries}-Datei ist nicht die einzige Möglichkeit,
Revisionsnummern herauszubekommen. Dazu kann auch das {\bf status}-Kommando
verwendet werden.
   

   
    \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} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.3 Tue Apr 20 02:34:42 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.3 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
   

   
  \par
  
Dies gibt, wenn es ohne bestimmte Dateinamen aufgerufen wird, den
Status aller Dateien eines Projektes aus:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs status
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs status: Examining.\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ============================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: README.txt Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.1.1.1 Sun Apr 18 18:18:22 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.1.1.1 /usr/local/cvs/myproj/README.txt,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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} \end{scriptsize} \end{tt} \linebreak
    \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.3 Tue Apr 20 02:34:42 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.3 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs status: Examining a-subdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ===============================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: whatever.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.1.1.1 Sun Apr 18 18:18:22 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.1.1.1 /usr/local/cvs/myproj/a-subdir/whatever.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs status: Examining a-subdir/subsubdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ===============================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: fish.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 06:35:27 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} a-subdir/subsubdir/fish.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs status: Examining b-subdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ==============================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: random.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 06:35:27 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Ignorieren Sie einfach alle Teile, die Sie nicht verstehen.
Tatsächlich gilt dies grundsätzlich für CVS. Oft wird die kleine
Information, die Sie benötigen, von Unmengen an Informationen
flankiert, die Sie entweder gar nicht interessieren oder vielleicht
auch gar nicht verstehen. Das ist völlig normal. Suchen Sie sich
einfach das heraus, was Sie brauchen, und ignorieren Sie den Rest.
   

   
  \par
  
Im vorangegangenen Beispiel besteht der interessante Teil aus den
ersten drei Zeilen (die Leerzeile nicht mitgezählt) der Statusausgabe
jeder Datei. Die erste Zeile ist die wichtigste; dort stehen der
Dateiname und der Status der Datei innerhalb der Arbeitskopie. Zurzeit
sind alle Dateien auf dem gleichen Stand mit dem Archiv, daher steht
überall der Status Up-to-date. Wenn jedoch {\bf random.c} modifiziert und
noch nicht an das Archiv mittels {\bf Commit} übertragen worden wäre,
könnte dies so aussehen:
   

   
    \begin{tt} \begin{scriptsize} ====================================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: random.c Status: Locally Modified\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 06:35:27 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
   

   
  \par
  
Working revision und Repository revision zeigen an, ob die Datei mit
dem Archiv übereinstimmt. Zurück bei der original Arbeitskopie (die
Kopie von jrandom, welche die aktuellen Änderungen von {\bf hello.c} noch
nicht hat) wird Folgendes ausgegeben:
   

   
    \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: Needs Patch\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 02:17:07 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.3 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dies besagt nun, dass jemand eine Veränderung an {\bf hello.c} durchgeführt
und mittels {\bf Commit} eingefügt hat und damit die Revision des Archivs zu
1.3 wurde, diese Arbeitskopie aber noch Revision 1.2 hat. Die Zeile
Status: Needs Patch besagt, dass beim nächsten {\bf Update} die Änderungen
vom Archiv geholt und mittels {\bf patch} in die Arbeitskopie
eingearbeitet würden.
   

   
  \par
  
Nehmen wir aber zunächst an, wir wüssten nichts von den Änderungen,
die qsmith an {\bf hello.c} durchgeführt hat und führen daher auch nicht
{\bf status} oder {\bf update} aus. Stattdessen wird die Datei ebenfalls
bearbeitet und eine geringfügig andere Veränderung an der gleichen
Stelle der Datei durchgeführt. Dies führt zu dem ersten Konflikt.
   

   
  \par
  
{\bf Konflikte erkennen und auflösen}
   

   
  \par
  
Einen Konflikt zu erkennen ist einfach. Wird update ausgeführt, gibt
CVS diesbezüglich sehr eindeutige Meldungen aus. Doch lassen Sie uns
zuerst einen Konflikt erzeugen. Dazu bearbeiten wir {\bf hello.c} und fügen
folgende Zeile ein:
   

   
    \begin{tt} \begin{scriptsize} printf (''this change will conflict$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
und zwar genau an der Stelle, an der qsmith
   

   
    \begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
einfügte. Zu diesem Zeitpunkt ist der {\bf Status} unserer Kopie von {\bf hello.c}
   

   
    \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: Needs Merge\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 02:17:07 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.3 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Das bedeutet, dass sowohl die Version der Datei im Archiv als auch die
lokale Arbeitskopie verändert wurde und diese Veränderungen
zusammengeführt werden müssen (merge). (CVS weiß noch nicht, dass
diese Veränderungen einen Konflikt ergeben werden, da noch kein {\bf Update}
durchgeführt wurde.) Wird das {\bf Update} durchgeführt, erscheint folgende
Ausgabe:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/hello.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} retrieving revision 1.3\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.2 and 1.3 into hello.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: conflicts found in hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} C hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Die letzte Zeile ist das kleine Geschenk von CVS. Das {\bf C} in der ersten
Spalte eben dem Dateinamen bedeutet, dass die Veränderungen zwar
zusammengeführt wurden, aber ein Konflikt entstand. Die Datei {\bf hello.c}
beinhaltet nun beide Veränderungen:
   

   
    \begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize}  printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+ hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize}  printf (''this change will conflict$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} =======\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+ 1.3\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
   

   
  \par
  
Konflikte werden durch Konfliktmarkierungen in folgendem Format angezeigt:
   

   
    \begin{tt} \begin{scriptsize} \verb+<+\verb+<+\verb+<+\verb+<+\verb+<+\verb+<+ (Dateiname)\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} die noch nicht durch Commit abgeschickten Änderungen der Arbeitskopie\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} blah blah lah\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} die neuen Änderungen aus dem Archiv\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} blah blah blah\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} und so weiter\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+ (letzte Revisionsnummer des Archivs)\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
In der {\bf Entries}-Datei wird ebenfalls vermerkt, dass sich die Datei
derzeit in einem nur halbwegs fertigen Zustand befindet:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat CVS/Entries
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/a-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/b-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /hello.c/1.3/Result of merge+Tue Apr 20 03:59:09 1999//\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Um den Konflikt zu beseitigen, muss die Datei so bearbeitet werden,
dass der entsprechende Quelltext erhalten bleibt, die Konfliktmarkierungen entfernt werden und diese erneute Veränderung
mittels {\bf Commit} an das Archiv gesendet wird. Dies bedeutet nicht
notwendigerweise, die eine Veränderung gegen die andere abwägen zu
müssen; es könnte auch der gesamte Abschnitt (oder gar die gesamte
Datei) neu geschrieben werden, weil eventuell beide Veränderungen
nicht ausreichend sind. In diesem Fall soll die erste Veränderung den
Zuschlag bekommen, jedoch mit etwas anderer Groß- und Kleinschreibung
und Punktuation als die Version von qsmith:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
emacs hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (die Veränderungen anbringen ...)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''BETWEEN HELLO AND GOODBYE.$\backslash$n'');\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''adjusted middle line''
     \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 hello.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/hello.c,v \verb+<+- hello.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

  

  \subsection{Herausfinden, wer was gemacht hat: Log-Nachrichten lesen} \label{d74e3212}
        

   

   
  \par
  
Das Projekt hat mittlerweile einige Veränderungen durchgemacht. Möchte
man nun einen Überblick darüber bekommen, was bisher geschah, so
möchte man wahrscheinlich nicht jeden einzelnen {\bf Diff} im Detail
betrachten. Einfach die Log-Nachrichten durchlesen zu können, wäre
ideal und kann auch einfach mit dem {\bf log}-Kommando erreicht werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs log
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Seiten über Seiten an Ausgaben ausgelassen)\end{scriptsize} \end{tt} \linebreak
   
  
   
  \par
  
Die Log-Ausgabe ist tendenziell etwas ausführlich. Sehen wir uns die
Log-Nachrichten nur für eine Datei an:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs log hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/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.4\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}  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: 5; selected revisions: 5\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} revision 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/20 04:14:37; author: jrandom; state: Exp; lines: +1 -1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} adjusted middle line\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.3\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/20 02:30:05; author: qsmith; state: Exp; lines: +1 -0\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} added new middle line\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/19 06:35:15; author: jrandom; state: Exp; lines: +1 -0\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} print goodbye too\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} branches: 1.1.1;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Initial revision\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp; lines: +0 -0\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} initial import into CVS\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} =========================================\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Wie üblich, steht am Anfang eine Menge an Informationen, die einfach
ignoriert werden kann. Die richtig guten Sachen kommen nach den Zeilen
mit den Strichen, und das Format ist eigentlich selbsterklärend.
   

   
  \par
  
Wenn mehrere Dateien mit einem {\bf Commit} abgeschickt wurden, erscheint
dafür nur eine Log-Nachricht; dies kann beim Nachvollziehen von
Veränderungen nützlich sein. Zum Beispiel haben wir zuvor {\bf fish.c} und
{\bf random.c} gleichzeitig mit einem {\bf Commit} abgeschickt. Dies geschah wie
folgt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs commit -m ''filled out C code''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Checking in a-subdir/subsubdir/fish.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v \verb+<+- fish.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 b-subdir/random.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Das Ergebnis war, beide Dateien mit der gleichen Log-Nachricht durch
{\bf commit} abzuschicken: »filled out C code.« (So, wie es hier geschah,
kamen damit beide Dateien von Revision 1.1 zu 1.2, aber dies ist nur
ein Zufall. Hätte {\bf random.c} Revision 1.29 gehabt, wäre daraus mit
diesem {\bf Commit} 1.30 geworden, und diese Revision 1.30 hätte die gleiche
Log-Nachricht wie {\bf fish.c} in der Revision 1.2 bekommen.)
   

   
  \par
  
Wird darauf {\bf cvs log} angewendet, werden die gemeinsamen Log-Nachrichten
angezeigt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs log a-subdir/subsubdir/fish.c b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/a-subdir/subsubdir/fish.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working file: a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} head: 1.2\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} 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: 3; selected revisions: 3\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} revision 1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/19 06:35:27; author: jrandom; state: Exp; lines: +8 -1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} filled out C code\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ---------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} branches: 1.1.1;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Initial revision\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp; lines: +0 -0\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} initial import into CVS\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} =================================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working file: b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} head: 1.2\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} 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: 3; selected revisions: 3\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} revision 1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/19 06:35:27; author: jrandom; state: Exp; lines: +8 -1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} filled out C code\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} branches: 1.1.1;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Initial revision\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ----------------\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} revision 1.1.1.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/18 18:18:22; author: jrandom; state: Exp; lines: +0 -0\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} initial import into CVS\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ============================================\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
In dieser Ausgabe kann man sehen, dass die beiden Revisionen Teil des gleichen
{\bf Commits} waren. (Die Tatsache, dass die Zeitstempel der beiden
Revisionen gleich sind, oder zumindest sehr nahe beieinander liegen,
ist ein weiterer Beweis.)
   

   
  \par
  
Log-Nachrichten durchzusehen ist eine gute Methode, um einen Überblick
darüber zu bekommen, was in einem Projekt vorgegangen oder was mit
einer Datei zu einem bestimmten Zeitpunkt passiert ist. Es gibt auch
freie Werkzeuge, um die rohe {\bf cvs log}-Ausgabe in ein kompakteres und
lesbareres Format umzuwandeln (wie beispielsweise das GNU
ChangeLog-Format); diese Werkzeuge werden an dieser Stelle nicht
behandelt, werden aber in Kapitel 10 eingeführt.
   
  

  \subsection{Veränderungen untersuchen und zurücknehmen} \label{d74e3604}
        

   

   
  \par
  
Stellen Sie sich vor, dass qsmith in den Log-Nachrichten sieht, dass
jrandom die letzten Veränderungen an {\bf hello.c} vorgenommen hat:
   

   
    \begin{tt} \begin{scriptsize} revision 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} date: 1999/04/20 04:14:37; author: jrandom; state: Exp; lines: +1 -1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} adjusted middle line\end{scriptsize} \end{tt} \linebreak
   \verb+>+

   
  \par
  
und sich fragt, was jrandom getan hat. Formal gesprochen fragt sich
qsmith: {\bf Was ist der Unterschied zwischen meiner Revision (1.3) von
hello.c und der darauf folgenden von jrandom (1.4)?} Dies kann mit dem
{\bf diff}-Kommando herausgefunden werden, indem nun zwei unterschiedliche
Revisionen durch die zusätzliche Kommandooption {\bf -r} verglichen werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs diff -c -r 1.3 -r 1.4 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/cvs/myproj/hello.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.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.3 -r1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/20 02:30:05 1.3\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/20 04:14:37 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,9 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize}  main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''between hello and goodbye$\backslash$n'');\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} --- 4,9 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''BETWEEN HELLO AND GOODBYE.$\backslash$n'');\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Auf diese Weise betrachtet ist die Veränderung sofort klar. Und weil
die Revisionsnummern in chronologischer Reihenfolge angegeben werden
(grundsätzlich eine gute Idee), wird auch der {\bf Diff} in korrekter
Reihenfolge gezeigt. Wird nur eine Revisionsnummer angegeben, benutzt
CVS die Revision der aktuellen Arbeitskopie als zweites Argument.
   

   
  \par
  
Als qsmith diese Veränderung sieht, entscheidet er sich spontan zu
Gunsten seiner eigenen Version und beschließt, ein {\bf undo} durchzuführen
- also eine Revision zurückzugehen.
   

   
  \par
  
Dies bedeutet aber nicht, dass er seine Revision 1.4 verlieren möchte.
Obwohl es, rein technisch gesprochen, mit CVS sicherlich möglich wäre, diesen Effekt zu erreichen, gibt es dazu meist keinen Grund. Es
ist vielmehr sinnvoller, Revision 1.4 in der Historie beizubehalten
und eine neue Revision 1.5 zu erzeugen, die genau wie 1.3 aussieht. So
wird das {\bf undo}-Ereignis ein Teil der Historie der Datei.
   

   
  \par
  
Es bleibt nur die Frage, wie der Inhalt der Revision 1.3
wiederhergestellt werden und Revision 1.5 daraus entstehen kann?
   

   
  \par
  
In diesem speziellen Fall könnte qsmith die Datei einfach per Hand
bearbeiten, den Stand der Revision 1.3 abbilden und wieder {\bf commit}
ausführen. Wenn die Veränderungen jedoch komplexer sind (wie sie es
im wahren Leben normalerweise sind), ist der Versuch, die alte
Version von Hand wiederherzustellen, hoffnungslos und fehlerträchtig.
Daher soll qsmith CVS benutzen, um die ältere wiederherzustellen und
erneut durch {\bf commit} an das Archiv zu senden.
   

   
  \par
  
Es gibt dafür zwei gleichwertige Wege: den langsamen, mühevollen Weg
und den schnellen, schönen Weg. Wir werden den langsamen und
mühevollen zuerst betrachten.
   


   
  \par
  
{\bf Die langsame Methode des Zurücknehmens}
   

   
  \par
  
Diese Methode verwendet die {\bf -p}-Option für update in Verbindung mit {\bf -r}.
Die {\bf -p}-Option sorgt dafür, dass der Inhalt der angegebenen Revision
auf der Standardausgabe erscheint. An sich ist dies noch nicht
sonderlich hilfreich; der Inhalt der Datei rauscht über den
Bildschirm, und die Arbeitskopie bleibt unverändert. Wenn jedoch die
Ausgabe in die Datei umgeleitet wird, enthält die Datei wieder den
Inhalt der alten Revision. Es ist, als wäre die Datei von Hand zum
alten Stand {\bf zurück bearbeitet} worden.
   

   
  \par
  
Zuerst muss qsmith jedoch seine Arbeitskopie mit dem Archiv abgleichen:
   

   
    \begin{tt} \begin{scriptsize} 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} U hello.c\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''BETWEEN HELLO AND GOODBYE.$\backslash$n'');\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Als Nächstes führt er {\bf update -p} aus, um sicherzustellen, dass die
Revision 1.3 tatsächlich die ist, die er haben möchte:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -p -r 1.3 hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} =========================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Checking out hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS: /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} VERS: 1.3\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\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
   

   
  \par
  
Huch, da sind noch ein paar überflüssige Zeilen am Anfang der Ausgabe.
Diese kamen eigentlich nicht über die Standardausgabe, sondern über
Standard-Error, sind also harmlos. Nichtsdestotrotz erschweren diese
das Lesen und können mit {\bf -Q} unterdrückt werden:
   

   
     \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q update -p -r 1.3 hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nun - dies ist genau das, was qsmith bekommen wollte. Der nächste
Schritt ist, die Ausgabe mit Hilfe einer Unix-Ausgabeumleitung in die
Datei der Arbeitskopie zu bekommen (dies erledigt das {\bf \verb+>+}):
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q update -p -r 1.3 hello.c \verb+>+ hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} 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} M hello.c\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Wenn nun {\bf update} ausgeführt wird, wird die Datei als modifiziert
markiert, was auch Sinn hat, da der Inhalt verändert wurde. Speziell
ist der Inhalt der gleiche wie der der älteren Revision 1.3 (nicht
dass CVS sich darüber bewusst wäre, dass diese identisch mit einer
älteren Revision ist - CVS merkt nur, dass die Datei verändert wurde).
Wenn qsmith ganz sichergehen wollte, könnte er einen {\bf Diff} zur
Überprüfung machen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q diff -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/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.4 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/20 04:14:37 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/20 06:02:25\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,9 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''BETWEEN HELLO AND GOODBYE.$\backslash$n'');\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} --- 4,9 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize}  main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''between hello and goodbye$\backslash$n'');\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Ja, dies ist genau das, was er wollte: eine klare Umkehrung -
tatsächlich ist dies das Gegenteil des {\bf Diff}, den er zuvor bekommen
hatte. Zufrieden führt er einen {\bf Commit} aus:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''reverted to 1.3 code''
     \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 hello.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/hello.c,v \verb+<+- hello.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   


   
  \par
  
{\bf Die schnelle Methode des Zurücknehmens}
   

   
  \par
  
Die schnelle, schöne Methode des {\bf Update} ist es, die Option {\bf -j} (für
{\bf join}) zu dem {\bf update}-Kommando zu verwenden. Diese Option verhält sich
wie die {\bf -r}-Option, da sie zwei Revisionsnummern als Argumente
verwendet und bis zu zwei Mal {\bf -j} angegeben werden kann.
   

   
  \par
  
CVS bestimmt dann die Unterschiede zwischen den beiden angegebenen
Revisionen und wendet diese als Patch auf die fragliche Datei an. (Die
Reihenfolge, in der die Revisionen angegeben werden, ist daher von
entscheidender Bedeutung.)
   

   
  \par
  
Angenommen, die Kopie von qsmith ist aktuell, so folgt daraus, dass er
einfach Folgendes ausführen könnte:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -j 1.4 -j 1.3 hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.4\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.3\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.4 and 1.3 into hello.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} M hello.c\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''reverted to 1.3 code'' 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/cvs/myproj/hello.c,v \verb+<+-- hello.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Wenn nur eine Datei in einen vorherigen Zustand zurückgeführt werden
soll, gibt es eigentlich keinen großen Unterschied zwischen der
mühevollen und der schnellen Methode. Sie werden später im Buch
sehen, dass die schnelle Methode wesentlich besser dazu geeignet ist,
mehrere Dateien gleichzeitig zurückzuführen. In der Zwischenzeit
können Sie einfach die Methode verwenden, die Ihnen am besten
gefällt.
   


   
  \par
  
{\bf Zurückführung ist kein Ersatz für Kommunikation}
   

   
  \par
  
Aller Wahrscheinlichkeit nach war das, was qsmith in diesem Beispiel
getan hat, sehr gemein. Arbeitet man mit anderen Leuten an einem
wirklichen Projekt und ist man der Meinung, dass jemand eine
Veränderung eingebracht hat, die nicht so gut war, sollte man zuerst
mit ihm oder ihr darüber reden. Vielleicht gibt es einen guten Grund
für diese Veränderung, oder sie oder er hat einfach nicht genau
darüber nachgedacht. Auf jeden Fall gibt es keinen Grund, diese
sofort rückgängig zu machen. Alle Revisionen werden von CVS permanent
gespeichert, und man kann daher jederzeit wieder zu einer älteren
Revision zurückkehren, nachdem man sich mit dem entsprechend
Verantwortlichen abgesprochen hat.
   

   
  \par
  
Wenn Sie ein Projektleiter sind und Abgabefristen einzuhalten haben
oder meinen, das Recht und die Notwendigkeit dazu zu haben, dann
machen Sie es so - aber schicken Sie direkt anschließend eine E-Mail
an den Autor der zurückgenommenen Veränderung, und erklären Sie ihm,
warum Sie es getan haben und was Ihrer Meinung nach korrigiert werden
müsse, damit die Veränderung wieder einfließen kann.
   
  
 \section{Andere nützliche CVS-Kommandos} \label{d74e4183}
        

  

  
  \par
  
Zu diesem Zeitpunkt sollten Sie mit CVS schon recht gut vertraut sein.
Daher werde ich an dieser Stelle mit dem Führungsstil aufhören und
einige weitere nützliche Kommandos zusammenfassend erläutern.
  


  
  \subsection{Dateien hinzufügen} \label{d74e4193}
        

   

   
  \par
  
Dateien hinzuzufügen geschieht in zwei Schritten: Zuerst wird das
{\bf add}-Kommando ausgeführt und anschließend das {\bf Commit}. Die Datei wird
erst tatsächlich im Archiv erscheinen, wenn das {\bf Commit} ausgeführt
wurde:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs add newfile.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs add: scheduling file 'newfile.c' for addition\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs add: use 'cvs commit' to add this file permanently\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''added newfile.c'' newfile.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/newfile.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Checking in newfile.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/newfile.c,v \verb+<+ó newfile.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} initial 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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

  

  \subsection{Verzeichnisse hinzufügen} \label{d74e4249}
        

   

   
  \par
  
Im Gegensatz zum Hinzufügen einer Datei verläuft das Hinzufügen eines
Verzeichnisses in einem Schritt; das anschließende {\bf Commit} entfällt
hier:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
mkdir c-subdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
\$ cvs add c-subdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Directory /usr/local/cvs/myproj/c-subdir added to the repository\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Betrachtet man nun den Inhalt des neuen Verzeichnisses der
Arbeitskopie, so sieht man, dass durch das {\bf add}-Kommando automatisch
ein CVS-Unterverzeichnis darin angelegt wurde:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls c-subdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} CVS/\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls c-subdir/CVS
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Entries Repository Root\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nun können, wie in jedem anderen Verzeichnis der Arbeitskopie, Dateien
(oder neue Unterverzeichnisse) angelegt werden.
   


   
  \par
  
{\bf CVS und Binärdateien}
   

   
  \par
  
Bisher habe ich ein kleines schmutziges Geheimnis von CVS ausgelassen,
nämlich dass CVS Binärdateien nicht gut verwalten kann (nun, es gibt
noch andere kleine schmutzige Geheimnisse von CVS, aber dies zählt
bestimmt zu den schmutzigsten). Es ist nicht so, dass CVS Binärdateien
gar nicht behandeln könnte; es kann dies nur nicht so elegant.
   

   
  \par
  
Alle Dateien, mit denen wir bisher zu tun hatten, waren einfache
Textdateien. CVS benutzt einige spezielle Tricks für Textdateien. Zum
Beispiel konvertiert CVS automatisch Zeilenumbrüche, wenn zwischen
einem Unix-Archiv und Windows oder Macintosh Arbeitskopien
ausgetauscht werden. Unter Unix ist zum Beispiel üblich, nur ein
Linefeed- (LF-)Zeichen am Ende einer Zeile zu verwenden, wohingegen
Windows am Ende einer Zeile ein Carriage Return (CR) und ein Linefeed
(LF) erwartet. Daher haben die Dateien einer Arbeitskopie auf einem
Windows-Rechner die CRLF-Kombination am Ende der Zeilen, wohingegen
die Arbeitskopie des gleichen Projektes auf einem Unix-Rechner nur
die LF-Zeilenenden hat (das Archiv selbst hat nur LF-Zeilenenden).
   

   
  \par
  
Ein weiterer Trick ist, dass CVS spezielle Zeichenketten, auch
RCS-Schlüsselwörter genannt, in Textdateien erkennt und diese durch
Revisionsinformationen und andere nützliche Dinge ersetzt. Wenn eine
Datei beispielsweise
   

   
  \par
  
\$Revision: 1.5 \$
   

   
  \par
  
enthält, ersetzt CVS dies bei jedem {\bf Commit} durch die Revisionsnummer,
also könnte es beispielsweise so aussehen:
   

   
  \par
  
\$Revision: 1.5 \$
   

   
  \par
  
CVS aktualisiert diese Zeichenkette während der Entwicklung. (Die
verschiedenen Schlüsselwörter sind in Kapitel 6 und 10 dokumentiert.)
   

   
  \par
  
Diese Wortersetzung ist bei Textdateien sehr nützlich, da man dadurch
die Revisionsnummer und andere Informationen direkt beim Bearbeiten
sehen kann. Doch was passiert, wenn die Datei ein JPEG-Bild ist? Oder
ein übersetztes ausführbares Programm? In dieser Art von Dateien
könnte CVS erheblichen Schaden anrichten, wenn es einfach blind alle
Schlüsselwörter, die es findet, ersetzt. In Binärdateien können
solche Zeichenketten einfach zufällig auftauchen.
   

   
  \par
  
Daher muss, wenn eine Binärdatei hinzugefügt werden soll, CVS
mitgeteilt werden, sowohl die Schlüsselwortersetzung als auch die
Zeilenendenumwandlung zu unterlassen. Dies erfolgt mit der Option
{\bf -kb}:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs add -kb filename
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''added blah'' filename
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (etc)\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
In manchen Fällen, wie bei Textdateien, die wahrscheinlich verstreute
Schlüsselwörter enthalten könnten, kann es sinnvoll sein, nur die
Schlüsselwortersetzung auszuschalten. Dies geschieht mit der Option
{\bf -ko}:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs add -ko filename
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''added blah'' filename
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (etc)\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
(Tatsächlich wäre dieses Kapitel schon wegen des darin enthaltenen
Beispiels {\bf \$Revision: 1.5 \$} ein Fall für eine solche Textdatei.)
   

   
  \par
  
Zu bemerken ist auch, dass kein aussagekräftiger {\bf cvs diff} zwischen
zwei Revisionen einer Binärdatei durchgeführt werden kann. {\bf Diff}
benutzt einen textbasierten Algorithmus, der bei Binärdateien
lediglich die Aussage treffen kann, ob sich diese unterscheiden,
nicht aber worin. Zukünftige Versionen von CVS werden vielleicht einen
binären {\bf Diff} unterstützen.
   

  

  \subsection{Dateien entfernen} \label{d74e4398}
        

   

   
  \par
  
Eine Datei zu entfernen ist ähnlich, wie eine hinzuzufügen, bis auf
einen zusätzlichen Schritt: Die Datei muß zuerst aus der Arbeitskopie
entfernt werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rm newfile.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs remove newfile.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs remove: scheduling 'newfile.c' for removal\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs remove: use 'cvs commit' to remove this file permanently\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''removed newfile.c'' newfile.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} Removing newfile.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/newfile.c,v \verb+<+- newfile.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} new revision: delete; 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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Zu beachten ist, dass bei dem zweiten und dritten Kommando {\bf newfile.c}
explizit angegeben wird, obwohl dies in der Arbeitskopie gar nicht
mehr existiert. Natürlich muss man dies bei dem {\bf Commit} nicht
unbedingt, solange man nichts dagegen hat, dass dann auch weitere
Dateien in den {\bf Commit} einbezogen werden.
   

  

  \subsection{Verzeichnisse entfernen} \label{d74e4454}
        

   

   
  \par
  
Wie schon zuvor erwähnt, stehen Verzeichnisse nicht unter der
Versionskontrolle von CVS. Stattdessen, als eine Art billiger Ersatz,
bietet es eine Reihe seltsamer Verhaltensweisen, die meistens {\bf das
Richtige} ausführen. Eine dieser Seltsamkeiten ist, dass leere
Verzeichnisse besonders behandelt werden können. Soll ein Verzeichnis
aus einem Projekt entfernt werden, werden zuerst alle Dateien daraus
entfernt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd dir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rm file1 file2 file3
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs remove file1 file2 file3
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''removed all files'' file1 file2 file3
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
und dann in dem übergeordneten Verzeichnis update mit der {\bf -P}-Option ausgeführt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd ..
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -P
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Die {\bf -P}-Option bedeutet für update, leere Verzeichnisse zu reduzieren -
diese also aus der Arbeitskopie zu entfernen. Ist dies einmal
ausgeführt, kann das Verzeichnis als gelöscht angesehen werden; alle
Dateien sind weg und das Verzeichnis selbst ebenfalls (zumindest in
der Arbeitskopie, dennoch existiert ein leeres Verzeichnis in dem
Archiv).
   

   
  \par
  
Ein interessantes Gegenstück zu diesem Verhalten ist, dass CVS bei
einem einfachen {\bf update} keine neuen Verzeichnisse aus dem Archiv in die
Arbeitskopie einfügt. Es gibt dafür eine Reihe von Begründungen, von
denen an dieser Stelle keine besonders erwähnenswert ist. Kurz
zusammengefasst kann man sagen, dass Sie von Zeit zu Zeit {\bf update} mit
der Option {\bf -p} ausführen sollten, damit neue Verzeichnisse aus dem
Archiv in Ihre Arbeitskopie eingefügt werden.
   

  

  \subsection{Dateien und Verzeichnisse umbenennen} \label{d74e4525}
        

   

   
  \par
  
Eine Datei umzubenennen ist das Gleiche, wie diese zu löschen und
unter einem neuen Namen anzulegen. Unter Unix sind die Befehle dazu:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cp oldname newname
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
rm oldname
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Und hier ist das CVS-Äquivalent:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
mv oldname newname
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs remove oldname
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs add newname
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''renamed oldname to newname'' oldname newname
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} (Ausgabe ausgelassen)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Bezüglich Dateien ist das alles, was zu tun ist. Verzeichnisse
umzubenennen ist nicht sonderlich anders: das neue Verzeichnis
anlegen, {\bf cvs add} ausführen, alle Dateien des alten Verzeichnisses in
das neue bewegen, {\bf cvs remove} ausführen, um diese aus dem alten
Verzeichnis zu entfernen, {\bf cvs add} ausführen, um diese in dem neuen
Verzeichnis hinzuzufügen, {\bf cvs commit} ausführen, damit auch alles dem
Archiv mitgeteilt wird, und dann {\bf cvs update -P} ausführen, damit das
nun leere Verzeichnis auch aus der Arbeitskopie verschwindet. Also:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
mkdir newdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs add newdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
mv olddir/* newdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} mv: newdir/CVS: cannot overwrite directory\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd olddir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs rm foo.c bar.txt
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd ../newdir
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs add foo.c bar.txt
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cd ..
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs commit -m ''moved foo.c and bar.txt from olddir to newdir''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -P
     \end{scriptsize} \end{tt} \linebreak 
   

   
{\bf Bemerkung} \linebreak 

Beachten Sie die Warnmeldung nach dem dritten Befehl. Diese besagt,
dass das CVS/Unterverzeichnis von {\bf olddir} nicht in {\bf newdir} kopiert
werden kann, da in {\bf newdir} schon ein solches existiert. Dies ist
auch gut so, da man sowieso das CVS/ Unterverzeichnis in {\bf newdir}
unverändert beibehalten möchte.
   

   
  \par
  
Ganz offensichtlich ist das Verschieben von Dateien etwas umständlich.
Die beste Methode ist es, schon beim ersten {\bf Import} des Projektes ein
gutes Verzeichnislayout zu haben, sodass später möglichst selten
ganze Verzeichnisse verschoben werden müssen. Später werden Sie eine
etwas drastischere Methode zum Verschieben von Verzeichnissen
kennenlernen, welche die Veränderungen direkt im Archiv vornimmt.
Diese Methode sollte jedoch für Notfälle aufgehoben werden; nach
Möglichkeit sollte alles mit CVS-Operationen innerhalb der
Arbeitskopie behandelt werden.
   

  

  \subsection{Optionsmüdigkeit vermeiden} \label{d74e4654}
        

   

   
  \par
  
Die meisten Benutzer sind es recht bald leid, zu jedem Befehl immer
wieder die gleichen Optionen eingeben zu müssen. Wenn man im Vorhinein
weiß, dass immer die globale Option {\bf -Q} oder die Option {\bf -c} im
Zusammenhang mit {\bf diff} angegeben werden soll, warum sollte dies dann
immer wieder eingegeben werden müssen?
   

   
  \par
  
Doch dafür gibt es glücklicherweise Abhilfe. CVS überprüft dazu die
Datei {\bf .cvsrc} im Home-Verzeichnis des Benutzers. In dieser Datei können
standardmäßige Optionen zu bestimmten Kommandos angegeben werden, die
immer ausgeführt werden, wenn CVS aufgerufen wird. Hier eine solche
beispielhafte Datei:
   

   \begin{tabular}{|l|}
                  \hline
                  \begin{tt} 
        
.cvsrc
    \end{tt} \\ 
                  \hline
                  \begin{minipage}{130mm} 
                  \begin{scriptsize} 
                  \begin{verbatim} 
        
     
diff -c
update -p
cvs -q
      
    \end{verbatim} 
                  \end{scriptsize} 
                  \end{minipage} \\
                  \hline
                  \end{tabular}

   
  \par
  
Entspricht die linke Spalte einem angegebenen CVS-Kommando (in der
nicht gekürzten Form), werden die entsprechenden Optionen jedes Mal,
wenn CVS verwendet wird, angewendet. Globale Optionen können mit cvs
angegeben werden. In diesem Beispiel wird also jedes Mal wenn {\bf diff}
ausgeführt wird, die Option {\bf -c} automatisch mit ausgeführt.
   

  

  \subsection{Momentaufnahmen (Zeitstempel und Marken)} \label{d74e4698}
        

   

   
  \par
  
Gehen wir noch einmal zu dem Beispiel zurück, in dem ein Programm
gerade nicht lauffähig ist, wenn eine bestimmte Fehlerbeschreibung
eines Benutzers eintrifft und diese daher nicht überprüft werden
kann. Der Entwickler braucht dann plötzlich Zugriff auf das gesamte
Projekt in dem Zustand, in dem es war, als die letzte Version
freigegeben wurde. Viele Dateien sind seitdem verändert worden, und
die meisten Revisionsnummern unterscheiden sich. Es wäre viel zu
aufwändig, die gesamten Log-Nachrichten durchzulesen, um
herauszufinden, welche Revisionsnummer eine jede Datei zum Zeitpunkt
der letzten Freigabe einer Version hatte, um dann {\bf update} (unter Angabe
einer Revisionsnummer mittels {\bf -r}) auf jede Datei anzuwenden. Bei
mittleren oder großen Projekten (einige Dutzend bis zu Tausende
Dateien) wäre dies ein hoffnungsloser Versuch.
   

   
  \par
  
CVS bietet daher die Möglichkeit, vorangegangene Revisionen aller
Dateien auf einmal zu holen. Tatsächlich gibt es dafür zwei Methoden:
nach Datum, dies wählt die zu holende Revision basierend auf dem
Datum des Zeitpunkts ihres {\bf Commit} aus, und mit Hilfe von Marken, was
eine Momentaufnahme eine Projektes holt, die durch eine Marke
gekennzeichnet wurde.
   

   
  \par
  
Welche der beiden Methoden zum Einsatz kommt, hängt von der Situation
ab. Das datumsbasierte Holen einer Revision wird durch die Option {\bf -D}
zu {\bf update} erreicht, die ähnlich zu {\bf -r} ist, aber als Argument ein
Datum und nicht eine Revisionsnummer benötigt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -D ''1999-04-19''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 

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

   
  \par
  
Mit der {\bf -D}-Option holt update die höchste Revision einer jeden Datei
eines gegebenen Datums und überführt, wenn nötig, die Dateien der
Arbeitskopie in vorangegangene Revisionen.
   

   
  \par
  
Zusätzlich zu einem Datum kann, und sollte meistens auch, eine Uhrzeit
angegeben werden. Zum Beispiel endete das vorangegangene Beispiel
darin, vor allem die Revision 1.1 zu holen (nur drei der Dateien
waren verändert, da alle anderen noch Revision 1.1 hatten). Als
Beweis hier die Statusausgabe für die Datei {\bf hello.c}:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -Q 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.1.1.1 Sat Apr 24 22:45:03 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.1.1.1 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Date: 99.04.19.05.00.00\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Ein Blick in die Log-Nachrichten zeigt jedoch, dass Revision 1.2 von
{\bf hello.c} definitiv am 19. April 1999 durch einen {\bf Commit} entstand. Also
warum wurde jetzt Revision 1.1 anstatt 1.2 geholt?
   

   
  \par
  
Das Problem ist, dass das Datum {\bf 1999-04-19} als {\bf Mitternacht
beginnend am 19.4.1999} interpretiert wurde - also der erste Zeitpunkt
dieses Tages. Das ist wahrscheinlich nicht das, was man möchte. Das
Commit der Revision 1.2 fand später an diesem Tag statt. Durch nähere
Spezifizierung des Datums kann auch Revision 1.2 geholt werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -D ''1999-04-19 23:59:59''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak 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: Locally Modified\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Sat Apr 24 22:45:22 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Date: 99.04.20.04.59.59\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Options: (none)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Wir sind fast am Ziel. Betrachten wir nun Datum und Uhrzeit in der
Zeile Sticky Date näher, scheint dort 4:59:59 a.m. Uhr zu stehen und
nicht 11:59 Uhr, wie es durch den Befehl angefordert wurde (wir
kommen später dazu, was {\bf sticky} bedeutet). Wie Sie vielleicht schon
erraten haben, liegt dieser Unterschied in der Differenz zwischen der
lokalen Zeit und der Universal Coordinated Time (auch {\bf Greenwich Mean
Time} genannt) begründet. Im Archiv werden alle Zeitstempel immer in
Universal Time gespeichert, CVS benutzt jedoch auf der Seite des
Clients die lokale Zeitzone. Im Falle von {\bf -D} ist dies etwas
unglücklich, da man wahrscheinlich mit den Daten und Zeiten des
Archivs vergleichen möchte und die Systemzeit des lokalen Systems
egal ist. Dies kann umgangen werden, indem bei dem Befehlsaufruf
zusätzlich die GMT-Zeitzone angegeben wird:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -D ''1999-04-19 23:59:59 GMT''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q 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.2 Sun Apr 25 22:38:53 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Date: 99.04.19.23.59.59\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Options: (none)\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Endlich - dies brachte nun die Arbeitskopie auf den letzten, durch
{\bf Commit} erreichten Stand vom 19. April (es sei denn, es hätte noch
weitere Beiträge durch {\bf Commit} in der letzten Sekunde des Tages
gegeben, was aber nicht der Fall ist).
   

   
  \par
  
Was passiert, wenn nun update ausgeführt wird?
   

   
    \begin{tt} \begin{scriptsize} 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} 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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Es passiert gar nichts. Wir wissen aber, dass es aktuellere Versionen
der letzten drei Dateien gibt. Warum sind diese nicht in der
Arbeitskopie enthalten?
   

   
  \par
  
Genau dies ist die Bedeutung von {\bf sticky} (bindend). Eine
Aktualisierung ({\bf Rückterminierung}?) mit der {\bf -D}-Option bewirkt, dass
die Arbeitskopie auf dieses vergangene Datum festgelegt wird. In der
Terminologie von CVS spricht man davon, dass für diese Arbeitskopie
ein {\bf bindendes Datum} gesetzt wurde. Hat eine Arbeitskopie einmal eine
bindenden Eigenschaft bekommen, bleibt diese so lange erhalten, bis
sie explizit zurückgenommen wird. Daher werden nun folgende {\bf Updates}
nicht mehr die aktuellste Version holen. Stattdessen bleiben diese
bei diesem bindenden Datum. Bindende Eigenschaften können mit dem {\bf cvs
status}-Kommando angezeigt oder direkt in der {\bf CVS/Entries}-Datei
nachgelesen werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -D ''1999-04-19 23:59:59 GMT''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat CVS/Entries
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} D/a-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/b-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/c-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//D99.04.19.23.59.59\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /hello.c/1.2/Sun Apr 25 23:07:29 1999//D99.04.19.23.59.59\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Sollte man nun {\bf hello.c} modifiziert haben und einen {\bf Commit} versuchen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} M hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''trying to change the past''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs commit: cannot commit with sticky date for file 'hello.c'\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs [commit aborted]: correct above errors first!\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
so würde CVS das nicht zulassen, da dies so wäre, als erlaube man, in
der Zeit zurück zu reisen und die Vergangenheit zu ändern. CVS basiert
auf chronologischen Aufzeichnungen und kann dies daher nicht
zulassen.
   

   
  \par
  
Das bedeutet aber nicht, dass CVS auf einmal nichts mehr von allen
anderen Revisionen weiß, die seitdem per {\bf Commit} eingeflossen sind. Man
kann immer noch die mit einem bindenden Datum versehene Arbeitskopie
mit anderen Revisionen vergleichen, zukünftige eingeschlossen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q diff -c -r 1.5 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/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.5 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/24 22:09:27 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/04/25 00:08:44\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 3,9 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} - printf (''between hello and goodbye$\backslash$n'');\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} --- 3,9 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize}  void\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} + /* this line was added to a downdated working copy */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\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
   

   
  \par
  
Der {\bf Diff} zeigt auf, dass, ausgehend vom 19. April 1999, die Zeile
{\bf between hello and goodbye} noch nicht hinzugefügt wurde. Er zeigt
auch die Modifikation, die wir in der Arbeitskopie gemacht haben (der
in dem vorangegangenen Quelltextauszug gezeigte zusätzliche
Kommentar).
   

   
  \par
  
Das bindende Datum sowie alle anderen bindenden Eigenschaften können
mit der Option {\bf -A} ({\bf -A} steht für {\bf reset}, fragen Sie mich nicht, warum)
zu {\bf update} entfernt werden, was die Arbeitskopie wieder auf den
aktuellsten Stand bringt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -A
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak 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.5 Sun Apr 25 22:50:27 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.5 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
{\bf Gültige Datumsformate}
   

   
  \par
  
CVS akzeptiert eine breite Auswahl an Datumsformaten. Mit dem ISO
8691-Format liegt man nie falsch (also Standard \#8601 der
International Standards Organization, siehe auch
www.saqqara.demon.co.uk/datefmt.htm), das auch in den vorangegangenen
Beispielen verwendet wurde. Es können auch die E-Mail-Formate für
Datum und Uhrzeit verwendet werden, die in RFC 822 und RFC 1123 (siehe
www.rfc-editor.org/rfc/) beschrieben sind. Letztendlich können
eindeutige Konstruktionen der englischen Datumsformate verwendet
werden, um Daten relativ zum aktuellen Datum zu beschreiben.
   

   
  \par
  
Sie werden sicherlich nicht alle möglichen Formate benötigen, dennoch
hier noch ein paar Beispiele, um Ihnen eine Vorstellung davon zu
geben, welche Formate CVS akzeptiert:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''19 Apr 1999''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''19 Apr 1999 20:05''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''19/04/1999''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''3 days ago''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''5 years ago''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''19 Apr 1999 23:59:59 GMT''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -D ''19 Apr''
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Die Anführungszeichen dienen lediglich dazu, der Unix-Shell
mitzuteilen, dass es sich jeweils um ein Argument handelt, obwohl
Leerzeichen enthalten sind. Die Anführungszeichen stören auch dann
nicht, wenn das Datum keine Leerzeichen enthält. Daher ist es
sinnvoll, immer welche zu verwenden.
   

   
  \par
  
{\bf Einen Zeitpunkt markieren (Marken)}
   

   
  \par
  
Mittels eines Datums Dateien wieder zu holen ist nützlich, wenn
lediglich der zeitliche Verlauf von hauptsächlichem Interesse ist.
Öfter jedoch soll ein Projekt wieder in einen Zustand gebracht
werden, in dem es zu einem bestimmten Ereignis war, beispielsweise dem
Zeitpunkt einer Veröffentlichung einer bekanntermaßen stabilen
Version oder dem Zeitpunkt, zu dem größere Teile hinzugefügt oder
weggenommen wurden.
   

   
  \par
  
Sich diese speziellen Daten einfach zu merken oder diese anhand der
Log-Dateien wiederzufinden, wäre ein langwieriger und schwieriger
Prozess. Vermutlich wurde ein solcher Zeitpunkt, weil er wichtig war,
in der Revisionshistorie markiert. CVS bietet dazu das Markieren
(engl. {\bf tagging}) an.
   

   
  \par
  
Markierungen unterscheiden sich von einem normalen {\bf Commit}, indem keine
Veränderungen an den Quelltexten an sich gespeichert werden, sondern
lediglich eine Veränderung der Einschätzung der Dateien durch den
Entwickler. Eine Markierung zeichnet eine Gruppe von Revisionen,
repräsentiert durch die Arbeitskopie eines Entwicklers, besonders aus
(für gewöhnlich ist dabei diese Arbeitskopie vollständig aktuell,
sodass der Name dieser Markierung der {\bf letzten und höchsten} Revision
des Archivs hinzugefügt wird).
   

   
  \par
  
Eine Markierung zu setzen ist einfach:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q tag Release-1999\_05\_01
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} T README.txt\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dieser Befehl verbindet den symbolischen Namen {\bf Release-1999\_05\_01}
mit der Momentaufnahme, die durch die aktuelle Arbeitskopie
repräsentiert wird. Formell definiert bezeichnet eine Momentaufnahme
eine Gruppe von Dateien und ihre Revisionsnummern innerhalb des
Projektes. Diese Revisionsnummern müssen nicht in allen Dateien
gleich sein, was sie für gewöhnlich auch nicht sind. Wäre zum
Beispiel eine Markierung in dem Beispielprojekt, das wir in diesem
ganzen Kapitel verwendet haben, gesetzt worden und die Arbeitskopie
wäre auf dem letzten Stand, dann wäre der symbolische Name
{\bf Release-1999\_05\_01} zu {\bf hello.c} mit Revision 1.5, {\bf fish.c} mit Revision
1.2, {\bf random.c} mit Revision 1.2 und allen anderen mit Revision 1.1
hinzugefügt worden.
   

   
Bild Kap_02-1.png
    

   
  \par
  
Wie eine Markierung in Relation zur Revisionshistorie eines Projektes
stehen kann.
   

   
Bild Kap_02-2.png
    

   
  \par
  
Eine Markierung ist eine {\bf Gerade Aussicht} durch die Revisionshistorie.
   

   
  \par
  
Vielleicht ist es hilfreich, sich eine Markierung als einen Pfad oder
eine {\bf Schnur} vorzustellen, welche die verschiedenen Revisionen der
Dateien miteinander verbindet.
   

   
  \par
  
Wird die Schnur gerade gezogen und sieht man direkt an ihr entlang, so
sieht man einen bestimmten Moment aus der Projektgeschichte - nämlich
genau den Moment, zu dem die Markierung gesetzt wurde (Abbildung
2.2).
   

   
  \par
  
Werden nun weiter Dateien verändert und die Veränderung durch einen
{\bf Commit} dem Archiv zur Verfügung gestellt, so wird die Markierung nicht
mit den wachsenden Revisionsnummern mit bewegt. Diese bleibt fest,
gebunden zu der Revisionsnummer einer jeden Datei, zu der diese
Markierung gesetzt wurde.
   

   
  \par
  
Durch ihre erläuternde Bedeutung ist es etwas unglücklich, dass
Markierungen keine langen Nachrichten oder ganze Paragraphen mit
Fließtext enthalten können. In dem vorangegangenen Beispiel besagte
die Markierung einfach und offensichtlich, dass sich das Projekt zu
diesem Datum in einem zu veröffentlichenden Zustand befand.
Manchmal möchte man jedoch komplexere Zustände markieren, was in sehr
unvorteilhaften Markierungen mündet:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs tag testing-release-3\_pre-19990525-public-release
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Als allgemeine Regel sollte gelten, Markierungsnamen so knapp wie
möglich zu halten, aber trotzdem alle notwendigen Informationen über
das spezielle Ereignis, das aufgezeichnet werden soll, zu enthalten.
Seien Sie im Zweifelsfall lieber zu ausführlich - Sie werden es sich
später selbst danken, wenn Sie anhand eines Markierungsnamens exakt
aussagen können, was denn genau aufgezeichnet wurde (oder werden
sollte).
   

   
  \par
  
Ihnen ist vielleicht schon aufgefallen, dass keine Punkte oder
Leerzeichen in den Markierungsnamen enthalten waren. CVS ist in der
Bewertung, was einen gültigen Markierungsnamen darstellt, ziemlich
streng. Die Regeln besagen, dass dieser mit einem Buchstaben beginnen
muss und Buchstaben, Ziffern, Bindestriche ({\bf -}) und Unterstriche
({\bf \_}) enthalten darf. Es dürfen keine Leerzeichen, Punkte,
Doppelpunkte, Kommas oder andere Symbole verwendet werden.
   

   
  \par
  
Um eine Momentaufnahme mittels eines Markierungsnamens aus dem Archiv
zu holen, wird dieser wie eine Revisionsnummer benutzt. Es gibt zwei
Möglichkeiten, eine Momentaufnahme zu bekommen: Man kann eine
neue Arbeitskopie mit einer bestimmten Markierung auschecken
({\bf Checkout}), oder man kann eine existierende Arbeitskopie mit Hilfe der
Markierung dahin überführen. Beide Wege führen zu einer Arbeitskopie,
deren Dateien den Revisionen entsprechen, die durch die Markierung
spezifiziert wurden.
   

   
  \par
  
Meistens wird man versuchen, einen Blick in das Projekt in dem Zustand
zu werfen, in dem es zum Zeitpunkt der Markierung war. Dies wird man
nicht notwendigerweise mit seiner Hauptarbeitskopie machen wollen,
die wahrscheinlich noch nicht per {\bf commit} abgeschickte Veränderungen
enthält. Nehmen wir also an, es soll eine separate Arbeitskopie per
{\bf Checkout} unter Verwendung der Markierung geholt werden. Dies
geschieht folgendermaßen (stellen Sie jedoch sicher, dass Sie dies
irgendwo anders als in Ihrer existierenden Arbeitskopie oder dem
darüber liegenden Verzeichnis ausführen!):
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs checkout -r Release-1999\_05\_01 myproj
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs checkout: Updating myproj\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj/README.txt\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj/hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs checkout: Updating myproj/a-subdir\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} cvs checkout: Updating myproj/a-subdir/subsubdir\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} cvs checkout: Updating myproj/b-subdir\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs checkout: Updating myproj/c-subdir\end{scriptsize} \end{tt} \linebreak
   

   
  \par
  
Die {\bf -r}-Option wurde bereits für das {\bf update}-Kommando verwendet und
spezifizierte dort eine Revisionsnummer. Eine Markierung verhält sich
in vielen Belangen wie eine Revisionsnummer, da eine bestimmte
Markierung für eine bestimmte Datei genau einer Revisionsnummer
entspricht. (Es ist grundsätzlich nicht möglich, zwei gleich lautende
Markierungen innerhalb eines Projektes zu verwenden.) Tatsächlich kann
man überall dort, wo auch eine Revisionsnummer benutzt werden kann,
einen Markierungsnamen als Teil eines CVS-Befehls verwenden
(vorausgesetzt, die Markierung wurde vorher gesetzt). Soll ein {\bf Diff}
des aktuellen Zustands einer Datei relativ zu einem Zustand einer
veröffentlichten Version gemacht werden, kann dies so erfolgen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs diff -c -r Release-1999\_05\_01 hello.c
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Und wenn vorübergehend zu dieser Revision zurückgegangen werden soll,
geht dies so:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -r Release-1999\_05\_01 hello.c
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Die Austauschbarkeit von Revisionsnummern mit Markierungen ist ein
Grund für die strengen Regeln der zulässigen Namen. Stellen Sie sich
einmal vor, dass Punkte in den Namen erlaubt wären; es könnte dann
eine Markierung geben, die {\bf 1.3} heißt und zu einer tatsächlichen
Revisionsnummer {\bf 1.47} gehören soll. Würde dann Folgendes ausgeführt
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -r 1.3 hello.c
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
wie sollte CVS dann wissen, ob sich dies nun auf die Markierung {\bf 1.3}
oder die viel frühere Revision {\bf 1.3} von {\bf hello.c} bezieht? Daher werden
die Markierungsnamen derart eingeschränkt und können so einfach von
Revisionsnummern unterschieden werden. Eine Revisionsnummer hat einen
Punkt, eine Markierung nicht. (Es gibt auch für die anderen
Einschränkungen Gründe, und die meisten haben damit zu tun, dass
dadurch die Markierungsnamen für CVS einfacher zu lesen sind.)
   

   
  \par
  
Wie Sie sich sicherlich haben denken können, besteht die zweite
Methode, eine Momentaufnahme zu bekommen - also eine bestehende
Arbeitskopie in die markierte Revision zu überführen - ebenfalls
darin, {\bf update} auszuführen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -r Release-1999\_05\_01
     \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
    \linebreak\begin{tt} \begin{scriptsize} cvs update: Updating c-subdir\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Der vorangegangene Befehl ist der gleiche wie der, der verwendet
wurde, um {\bf hello.c} zu Release-1999\_05\_01 zurückzuführen, bis darauf, dass
der Dateiname ausgelassen wurde, da das gesamte Projekt zurückgeführt
werden soll. (Sie können auch, wenn Sie dies wollen, nur einen Zweig
der Verzeichnisstruktur des Projektes zurückführen, indem der
vorangegangene Befehl in dem entsprechenden Unterverzeichnis anstatt
im Hauptverzeichnis ausgeführt wird, obwohl Sie dies wohl nie richtig
wollen werden.)
   

   
  \par
  
Beachten Sie, dass anscheinend bei dem {\bf Update} keine Dateien verändert
wurden. Die Arbeitskopie war völlig aktuell, als die Marke gesetzt
wurde, und es wurden seitdem keine Veränderungen vorgenommen.
   

   
  \par
  
Dies bedeutet jedoch nicht, dass sich gar nichts verändert hat. Die
Arbeitskopie ist nun markiert. Wird nun eine Veränderung gemacht und
versucht, diese mit {\bf commit} an das Archiv zu schicken (nehmen wir an,
es wurde {\bf hello.c} verändert):
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} M hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q ci -m ''trying to commit from a working copy on a tag''
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} cvs commit: sticky tag 'Release-1999\_05\_01' for file 'hello.c' is not\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} a branch\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} cvs [commit aborted]: correct above errors first!\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
so lässt CVS dies nicht zu. (Kümmern Sie sich erst einmal nicht um die
genaue Bedeutung obiger Fehlermeldung - wir werden Verzweigungen als
Nächstes in diesem Kapitel behandeln). Es spielt dabei keine Rolle,
ob die Markierung aus einem {\bf Checkout} oder {\bf Update} resultiert. Ist diese
einmal markiert, sieht CVS die Arbeitskopie als eine statische
Momentaufnahme der Vergangenheit an und erlaubt Ihnen nicht mehr, die
Vergangenheit zu ändern, zumindest nicht so einfach. Wird {\bf cvs status}
ausgeführt oder wenn Sie sich die {\bf CVS/Entries}-Datei ansehen, werden
Sie feststellen, dass für jede Datei eine bindende Markierung
({\bf sticky tag}) gesetzt ist. Zum Beispiel ist dies die
Haupt-Entries-Datei:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cat CVS/Entries
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} D/a-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/b-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} D/c-subdir////\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /README.txt/1.1.1.1/Sun Apr 18 18:18:22 1999//TRelease-1999\_05\_01\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /hello.c/1.5/Tue Apr 20 07:24:10 1999//TRelease-1999\_05\_01\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Markierungen werden genau wie andere bindende Eigenschaften mit der
{\bf -A}-Option von {\bf update} entfernt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -A
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} M hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Die Veränderungen an {\bf hello.c} gehen jedoch nicht verloren; CVS erkennt
immer noch, dass die Datei bezüglich des Archivs verändert wurde:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q diff -c 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/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.5 hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/04/20 06:12:56 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/05/04 20:09:17\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 6,9 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 6,10 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''between hello and goodbye$\backslash$n'');\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} + /* a comment on the last line */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nun, da durch {\bf update} alle bindenden Eigenschaften entfernt wurden,
akzeptiert CVS auch wieder einen {\bf Commit}:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs ci -m ''added comment to end of main function''
     \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} cvs commit: Examining c-subdir\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/cvs/myproj/hello.c,v \verb+<+- hello.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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Die Markierung {\bf Release-1999\_05\_01} gehört selbstverständlich immer
noch zu Revision 1.5. Vergleichen Sie den Status der Datei vor und
nach dieser Umkehrung zu dieser Markierung:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q 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.6 Tue May 4 20:09:17 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.6 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: (none)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q update -r Release-1999\_05\_01
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q 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.5 Tue May 4 20:21:12 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.5 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: Release-1999\_05\_01 (revision: 1.5)\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Nun, da ich Ihnen gesagt habe, dass CVS Sie die Geschichte nicht
verändern lässt, zeige ich Ihnen, wie Sie die Geschichte verändern können.
   

  

  \subsection{Verzweigungen} \label{d74e5756}
        

   

   
  \par
  
Bisher wurde CVS als eine intelligente und koordinierende Bibliothek
betrachtet. Man kann sich CVS aber auch als eine Zeitmaschine
vorstellen (Danke schön an Jim Blandy für diese Analogie). Bisher
haben wir nur gesehen, wie die Vergangenheit mit CVS betrachtet werden
kann, ohne darauf irgendeinen Einfluss zu nehmen. Doch wie alle guten
Zeitmaschinen erlaubt CVS jedoch auch in der Zeit zurückzugehen und
die Vergangenheit zu verändern. Was ist das Resultat daraus? Wie jeder
Science Fiction-Fan weiß, ist die Antwort darauf: ein weiteres
Universum, parallel zu unserem, aber genau an dem Punkt divergierend,
an dem die Vergangenheit verändert wurde. Eine Verzweigung im Sinne
von CVS spaltet die Entwicklung eines Projektes in getrennte,
parallele Historien. Veränderungen in einem Zweig betreffen den
anderen nicht mehr.
   

   
  \par
  
Warum ist dies nützlich?
   

   
  \par
  
Kehren wir für einen Moment noch einmal zu dem Szenario zurück, in dem
ein Entwickler, mitten in der Entwicklung einer neuen Version eines
Programmes, eine Fehlerbeschreibung über eine ältere Version bekommt.
Angenommen, der Entwickler behebt das Problem, so muss er diese
Korrektur immer noch dem Kunden zukommen lassen. Es ist sicherlich
nicht sonderlich hilfreich, eine ältere Kopie ausfindig zu machen,
darin den Fehler zu beheben, ohne dies CVS wissen zu lassen, und das
Ganze dem Kunden zu schicken. Es gäbe anschließend keinerlei
Aufzeichnung der durchgeführten Änderung; CVS hätte keine
Informationen darüber; und wenn später festgestellt würde, dass auch
die Fehlerbehebung einen Fehler hat, hätte niemand einen Ansatzpunkt,
um das Problem zu reproduzieren
   

   
  \par
  
Noch schlimmer wäre es, den Fehler in der aktuellen und instabilen
Version der Quelltexte zu beheben und dies dem Kunden zu schicken.
Sicherlich könnte der Fehler behoben sein, doch der Rest des
Quelltextes ist in einem instabilen und noch nicht getesteten Zustand.
Sie könnte laufen, aber sie ist sicherlich noch nicht
produktionsreif.
   

   
  \par
  
Weil die letzte veröffentlichte Version, von eben dem einen Fehler
abgesehen, als stabil angesehen wird, ist die beste Lösung
zurückzugehen und den Fehler in dieser älteren Version zu beheben -
also ein weiteres Universum zu schaffen, in dem die letzte
veröffentlichte Version die Fehlerbeseitigung beinhaltet.
   

   
  \par
  
Dies ist der Punkt, an dem Verzweigungen ins Spiel kommen. Der
Entwickler spaltet einen Zweig ab, der in der Hauptentwicklungslinie
(engl. {\bf trunk}) verwurzelt ist, aber nicht mit den aktuellen
Revisionen, sondern zurück zu dem Zeitpunkt der letzten
Veröffentlichung. Dann kann er einen {\bf Checkout} einer Arbeitskopie
dieses Zweiges machen, die zur Fehlerbeseitigung notwendigen
Veränderungen anbringen und diese durch einen {\bf Commit} wieder CVS
mitteilen, sodass davon Aufzeichnungen existieren. Nun kann er eine
Zwischenversion zusammenstellen, die auf diesem Zweig basiert, und
diese an den Kunden ausliefern.
   

   
  \par
  
Seine Veränderungen beeinflussen die Quelltexte der
Hauptentwicklungslinie nicht, was er auch sicherlich nicht wollte,
ohne sich vorher zu vergewissern, dass diese die gleiche Art von
Fehlerbereinigung benötigen. Sollte dies aber doch der Fall sein, kann
er die Veränderungen des Zweiges wieder in die Hauptentwicklungslinie
integrieren ({\bf merge}). Bei einem {\bf Merge} bestimmt CVS die
Veränderungen, die seit dem Zeitpunkt der Aufspaltung von der
Hauptentwicklungslinie bis zu der aktuellen Spitze (der aktuellste
Stand des Zweiges) stattgefunden haben, und bringt diese Veränderung
an dem Projekt zum Stand der Spitze des Zweiges an. Der Unterschied
zwischen der Wurzel des Zweiges und der Spitze stellt sich,
natürlich, als eben die Bereinigung des Fehlers heraus.
   

   
  \par
  
Ein {\bf Merge} kann auch als ein Spezialfall des {\bf update} angesehen werden.
Der Unterschied beim {\bf Merge} ist, dass die wieder zu integrierenden
Veränderungen von Wurzel und Spitze des Zweiges abgeleitet werden und
nicht durch den Vergleich einer Arbeitskopie mit dem Archiv.
   

   
  \par
  
Der Akt des {\bf Update} an sich ist ähnlich wie die Patches von den
jeweiligen Autoren direkt zu bekommen und diese per Hand einzufügen.
Tatsächlich bestimmt CVS, um {\bf update} durchzuführen, den Unterschied
(also wie mit dem {\bf diff}-Programm selbst) zwischen der Arbeitskopie und
dem Archiv und wendet diesen {\bf Diff} auf die Arbeitskopie genau so an,
wie es auch das {\bf patch}-Kommando machen würde. Dieses spiegelt die Art
und Weise wieder, in der ein Entwickler Veränderungen von außerhalb
annimmt, nämlich manuell die von den anderen Autoren erhaltenen
{\bf patch}-Dateien einzufügen.
   

   
  \par
  
Daher ist das Zusammenführen des Zweiges mit der Fehlerbereinigung mit
der Hauptentwicklungslinie wie das Einfügen eines
Fehlerbereinigungs-Patches von Dritten außerhalb des Projektes. Ein
solcher Dritter würde den Patch gegen die letzte veröffentlichte
Version machen, genau wie die Veränderungen des Zweiges gegen diese
Version gemacht werden. Wenn sich dieser Bereich der Quelltexte seit
der letzten Veröffentlichung nicht stark verändert hat, wird das
Zusammenführen ohne Probleme ablaufen. Wenn sich der Quelltext jedoch
in einem substanziell anderen Zustand befindet, wird die
Zusammenführung mit Konflikten fehlschlagen (der Patch wird also
zurückgewiesen), und man wird per Hand daran herumfummeln müssen.
Üblicherweise wird dann die betreffende Stelle gelesen, die
notwendigen Veränderungen werden per Hand eingefügt und ein {\bf Commit}
ausgeführt. Abbildung 2.3 zeigt das Vorgehen bei einer Verzweigung
und Zusammenführung.
   

   
  \par
  
Wir werden nun die notwendigen Schritte, um das in der Abbildung
Gezeigte zu erreichen, durchgehen. Denken Sie daran, dass von links
nach rechts betrachtet nicht die Zeit voranschreitet, sondern dies
vielmehr die Revisionshistorie widerspiegelt. Die Verzweigung fand
nicht zum Zeitpunkt der Veröffentlichung statt, sondern wurde nur
etwas später dort angesetzt.
   

   
Kap_02-3.png 
    

   
  \par
  
{\bf Verzweigung und Zusammenführung}
   

   
  \par
  
Nehmen wir für unseren Fall an, dass die Dateien vielen Veränderungen
unterworfen waren, bis sie als {\bf Release-1999-05-01} markiert, wurden
und sogar einige hinzugekommen sind. Als die Fehlerbeschreibung über
die alte veröffentlichte Version hereinkommt, wird das Erste, was wir
machen wollen, folgendes sein: einen Zweig zu erzeugen, der auf diesem
Veröffentlichungsstand basiert und den wir praktischerweise mit
{\bf Release-1999-05-01} markieren.
   

   
  \par
  
Ein Weg, dies zu erreichen ist, eine Arbeitskopie basierend auf dieser
Markierung per {\bf checkout} zu holen und diese mit der {\bf -b}-Option erneut zu
markieren:
   

   
    \begin{tt} \begin{scriptsize} 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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q checkout -d myproj\_old\_release -r Release-1999\_05\_01 myproj
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U myproj\_old\_release/README.txt\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj\_old\_release/hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj\_old\_release/a-subdir/whatever.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj\_old\_release/a-subdir/subsubdir/fish.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U myproj\_old\_release/b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
ls
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} myproj\_old\_release/\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cd myproj\_old\_release
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
ls
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} CVS/ README.txt a-subdir/ b-subdir/ hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q tag -b Release-1999\_05\_01-bugfixes
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} T README.txt\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Sehen Sie sich das letzte Kommando gut an. Es mag etwas willkürlich
erscheinen, {\bf tag} zur Erzeugung eines Zweiges zu verwenden, doch es gibt
einen Grund dafür: Der Markierungsname wird als eine Bezeichnung
verwendet, anhand derer der Zweig später aus dem Archiv geholt werden
kann. Zweigmarkierungen unterscheiden sich nicht von
Nicht-Zweigmarkierungen und unterliegen damit den gleichen
Restriktionen für die Namensvergabe. Manche Benutzer fügen das Wort
{\bf branch} (engl. Zweig) in den Markierungsnamen ein (zum Beispiel
{\bf Release-1999\_05\_01-bugfix-branch}), sodass die Zweigmarkierungen
leichter von anderen Markierungen unterschieden werden können.
Vielleicht möchten Sie dies auch, wenn Sie des öfteren die falsche
Markierung holen.
   

   
  \par
  
(Und wo wir gerade dabei sind, beachten Sie die {\bf -d
myproj\_old\_release}-Option des {\bf checkout}-Befehls beim ersten CVS-Aufruf.
Diese sagt {\bf checkout}, die Arbeitskopie in das Verzeichnis namens
{\bf myproj\_old\_release} abzulegen, damit wir diese nicht mit der aktuellen
Version in myproj durcheinander bringen. Achten Sie darauf, diese
Verwendung von {\bf -d} nicht mit der globalen Option gleichen Namens oder
der {\bf -d}-Option von {\bf update} zu verwechseln.)
   

   
  \par
  
Natürlich macht die Ausführung dieses {\bf tag}-Kommandos die aktuelle
Arbeitskopie nicht automatisch zu einem Zweig. Das Markieren betrifft
nie die Arbeitskopie; es zeichnet lediglich einige zusätzliche
Informationen im Archiv auf, damit diese Revision der Arbeitskopie
später wieder geholt werden kann (als ein fester Punkt in der
Geschichte oder als ein Zweig, wie es der Fall sein kann).
   

   
  \par
  
Das Holen kann auf zwei Wegen erfolgen (Sie werden sich vielleicht
schon daran gewöhnt haben!). Es kann aus diesem Zweig eine neue
Arbeitskopie per {\bf checkout} geholt werden:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /home/whatever\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs co -d myproj\_branch -r Release-1999\_05\_01-bugfixes myproj
     \end{scriptsize} \end{tt} \linebreak 
   

   
   
  \par
  
oder eine bereits existierende Arbeitskopie kann dazu gemacht werden:
   

   
   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /home/whatever/myproj\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs update -r Release-1999\_05\_01-bugfixes
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Das Resultat ist das gleiche (nun ja, der Name des übergeordneten
Verzeichnisses der neuen Arbeitskopie kann unterschiedlich sein, doch
dies ist für die Zwecke von CVS unwichtig). Sollte Ihre Arbeitskopie
noch nicht durch {\bf commit} abgeschickte Veränderungen beinhalten, sollten
Sie für den Zugriff auf den Zweig besser {\bf checkout} anstatt {\bf update}
verwenden. Sonst würde CVS versuchen, Ihre Änderungen in die
Arbeitskopie einfließen zu lassen, während es diese zu einem Zweig
macht. In diesem Fall könnten Konflikte auftreten, auch wenn nicht,
wäre das Ergebnis ein unreiner Zweig. Dieser würde nicht dem
tatsächlichen Zustand des Programmes mit der angegebenen Markierung
entsprechen, da einige Dateien der Arbeitskopie Ihre Veränderungen
beinhalten würden.
   

   
  \par
  
Wie auch immer, nehmen wir an, dass Sie auf die eine oder andere Weise
eine Arbeitskopie des gewünschten Zweiges bekommen haben:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q 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.5 Tue Apr 20 06:12:56 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.5 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: Release-1999\_05\_01-bugfixes\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} (branch: 1.5.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q status b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} ==================================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: random.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2 Mon Apr 19 06:35:27 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2 /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: Release-1999\_05\_01-bugfixes (branch: 1.2.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
(Der Inhalt der Sticky Tag-Zeilen wird gleich erläutert.) Wenn Sie nun
{\bf random.c} und {\bf hello.c} modifizieren und {\bf commit} ausführen
   

   
    \begin{tt} \begin{scriptsize} 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} M b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs ci -m ''fixed old punctuation bugs''
     \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 hello.c;\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} /usr/local/cvs/myproj/hello.c,v \verb+<+ó hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} new revision: 1.5.2.1; previous revision: 1.5\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/cvs/myproj/b-subdir/random.c,v \verb+<+ó random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} new revision: 1.2.2.1; previous revision: 1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} done\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
werden Sie feststellen, dass etwas Lustiges mit den Revisionsnummern
vor sich geht:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q status hello.c b-subdir/random.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.5.2.1 Wed May 5 00:13:58 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.5.2.1 /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: Release-1999\_05\_01-bugfixes (branch: 1.5.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} ===================================================\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} File: random.c Status: Up-to-date\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Working revision: 1.2.2.1 Wed May 5 00:14:25 1999\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Repository revision: 1.2.2.1 /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Sticky Tag: Release-1999\_05\_01-bugfixes (branch: 1.2.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Diese haben nun vier Ziffern anstatt zwei!
   

   
  \par
  
Ein näherer Blick zeigt, dass die Revisionsnummer jeder Datei
lediglich aus der Zweignummer (wie in der Sticky Tag-Zeile angegeben)
und einer extra Ziffer am Ende besteht.
   

   
  \par
  
Was Sie hier sehen, ist ein Stück von CVS innerer Arbeitsweise. Obwohl
Sie sicherlich immer eine Verzweigung benutzen werden, um eine
projektweite Aufspaltung zu markieren, zeichnet CVS dies jedoch auf
Basis der einzelnen Dateien auf. Dieses Projekt beinhaltete zum
Zeitpunkt der Verzweigung fünf Dateien, und es wurden daher fünf
individuelle Zweige erzeugt, alle mit dem gleichen Markierungsnamen:
{\bf Release-1999\_05\_01-bugfixes}.
   

   
{\bf Bemerkung} \linebreak 

Die meisten Benutzer sehen dies als eine eher unelegante
Implementierung seitens CVS an. Hier scheint ein Teil des alten
RCS-Vermächtnisses durch - RCS konnte Dateien nicht in Projekte
gruppieren, und obwohl CVS dies nun kann, benutzt CVS dennoch
Programmteile zur Verwaltung der Verzweigungen, die von RCS geerbt
wurden.
   

   
  \par
  
Für gewöhnlich brauchen Sie sich nicht darum zu kümmern, wie CVS
intern arbeitet, doch in diesem Fall ist es hilfreich zu wissen, in
welcher Beziehung Zweignummern und Revisionsnummern stehen.
Betrachten wir {\bf hello.c}; alles, was ich über {\bf hello.c} aussagen werde,
trifft ebenso auf die anderen Dateien des Zweiges zu
(Revisions-/Zweignummern entsprechend angepasst).
   

   
  \par
  
Zu dem Zeitpunkt, auf dem der Zweig basiert, hatte {\bf hello.c} Revision
1.5. Als der Zweig geschaffen wurde, wurde an das Ende eine neue
Ziffer angehängt, um die Zweignummer zu bilden (CVS verwendet dazu
die noch nicht benutzte erste, gerade, ganze Zahl, die nicht null
ist). Daher wurde die Zweignummer in diesem Fall {\bf 1.5.2}. Die
Zweignummer an sich ist keine Revisionsnummer, sie ist aber die Wurzel
(also das Präfix) aller weiteren Revisionsnummern von {\bf hello.c}
innerhalb dieses Zweiges.
   

   
  \par
  
Als jedoch das erste Mal der CVS-Status der verzweigten Arbeitskopie
abgefragt wurde, wurde als Revisionsnummer nur {\bf 1.5} anstatt {\bf 1.5.2.0}
oder etwas Ähnlichem angezeigt. Dies liegt daran, dass die erste
Revisionsnummer innerhalb eines Zweiges immer gleich mit der Revision
der Datei in der Hauptentwicklungslinie ist, von welcher der Zweig
stammt. Daher zeigt CVS in Statusberichten die Revisionsnummer der
Datei in der Hauptentwicklungslinie an, solange die Dateien des
Zweiges und der Hauptentwicklungslinie identisch sind.
   

   
  \par
  
Als eine neue Revision per {\bf commit} an das Archiv übertragen wurde, war
{\bf hello.c} in dem Zweig nicht mehr identisch mit der
Hauptentwicklungslinie - der Inhalt der Datei im Zweig wurde
verändert, wohingegen der der Hauptentwicklungslinie identisch blieb.
Dementsprechend wurde {\bf hello.c} seine erste Zweigrevisionsnummer
zugeordnet. Dies ist in der Statusausgabe nach dem {\bf commit}-Kommando zu
sehen, die nun die Revisionsnummer {\bf 1.5.2.1} zeigt.
   

   
  \par
  
Das Gleiche gilt auch für die Datei {\bf random.c}. Deren Revisionsnummer
war zum Zeitpunkt der Verzweigung {\bf 1.2}, damit ist dessen erste
Verzweigung {\bf 1.2.2}. Der erste {\bf Commit} von {\bf random.c} innerhalb dieses
Zweiges bekam die Revisionsnummer {\bf 1.2.2.1}.
   

   
  \par
  
Es gibt keinen numerischen Zusammenhang zwischen {\bf 1.5.2.1} und
{\bf 1.2.2.1} - betrachtet man nur diese Revisionsnummern, gibt es keinen
Grund anzunehmen, dass diese Teil der gleichen Verzweigung sind, bis
auf dass beide Dateien mit {\bf Release-1999\_05\_01-bugfixes} markiert sind
und diese Markierung zu Verzweigungsnummer 1.5.2 und 1.2.2 der
jeweiligen Dateien gehört. Daher ist der Markierungsname die einzige
Möglichkeit, den Zweig projektweit zu identifizieren. Obwohl es
absolut möglich ist, eine Datei in einen Zweig anhand ihrer
Revisionsnummer zu verschieben
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs update -r 1.5.2.1 hello.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
ist davon fast immer abzuraten. Man würde dadurch die verzweigten
Revisionen einer Datei mit den nicht verzweigten Revisionen anderer
vermischen. Wer weiß, welche Verluste daraus entstehen? Es ist
besser, die Zweigmarkierung zur Referenz auf den Zweig zu verwenden
und alle Dateien auf einmal zu bearbeiten, als eine bestimmte Datei
zu spezifizieren. Auf diese Weise braucht man die tatsächliche
Zweigrevisionsnummer einer Datei gar nicht zu wissen oder sich darum
zu kümmern.
   

   
  \par
  
Es ist auch möglich, dass Zweige sich wieder verzweigen bis zu jeder
beliebig absurden Stufe. Eine Datei mit der Revisionsnummer
1.5.4.37.2.3.12.1 wird in Abbildung 2.4 grafisch dargestellt.
   

   
  \par
  
Zugegeben sind Umstände, in denen eine solche Verzweigungstiefe
notwendig ist, nur schwer vorstellbar, doch ist es nicht schön zu
wissen, dass CVS so weit gehen kann, wie man dies selbst möchte?
Eingebettete Verzweigungen werden genau wie jeder andere Zweig
angelegt: Eine Arbeitskopie eines Zweiges N per {\bf Checkout} holen, {\bf cvs
tag -b} Zweigname darin ausführen und damit Zweig N.M im Archiv
erstellen (wobei {\bf N} die zugehörige Zweigrevisionsnummer jeder Datei
ist, wie beispielsweise {\bf 1.5.2.1}, und {\bf M} den nächstmöglichen Zweig
am Ende dieser Zahl angibt, wie beispielsweise {\bf 2}).
   

   
Kap_02-4.png
    

   
  \par
  
Ein absurd hochgradiger Verzweigungsgrad
   


   
  \par
  
{\bf Veränderungen zwischen Zweig und Stamm verschmelzen}
   

   
  \par
  
Nun, da die Fehlerbereinigung durch {\bf commit} in den Zweig kam, lassen
Sie uns aus der Arbeitskopie die höchste Revision der
Hauptentwicklungslinie machen und nachsehen, ob diese
Fehlerbereinigung dort auch angebracht werden muss. Dazu entfernen wir
unsere Arbeitskopie durch {\bf update -A} wieder von dem Zweig
(Zweigmarkierungen verhalten sich diesbezüglich wie andere bindende
Eigenschaften) und führen dann einen {\bf Diff} gegen die
Hauptentwicklungslinie, die wir gerade verlassen haben, durch:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -A
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} U hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} U b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q diff -c -r Release-1999\_05\_01-bugfixes
     \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/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.6\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} diff -c -r1.5.2.1 -r1.6\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** hello.c 1999/05/05 00:15:07 1.5.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- hello.c 1999/05/04 20:19:16 1.6\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,9 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''between hello and good-bye$\backslash$n'');\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} --- 4,10 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Hello, world!$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''between hello and goodbye$\backslash$n'');\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} + /* a comment on the last line */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Index: b-subdir/random.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/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2.2.1\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.2.1 -r1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** b-subdir/random.c 1999/05/05 00:15:07 1.2.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- b-subdir/random.c 1999/04/19 06:35:27 1.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ***************\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} *** 4,8 ****\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''A random number.$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} --- 4,8 ----\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} ! printf (''a random number$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Der {\bf Diff} zeigt, dass good-bye in der verzweigten Revision von {\bf hello.c}
mit einem Bindestrich geschrieben wird und dass die Revision der
Hauptentwicklungslinie dieser Datei einen Kommentar am Ende enthält,
den die andere Revision nicht enthält. Außerdem enthält die
Zweigrevision von {\bf random.c} ein großes {\bf A} und einen Punkt, wohingegen
die Hauptentwicklungslinie dies nicht beinhaltet.
   

   
  \par
  
Um die Veränderungen des Zweiges tatsächlich in die aktuelle
Arbeitskopie zu übernehmen, wird das {\bf update}-Kommando mit der Option {\bf -j}
verwendet (das gleiche {\bf j} für {\bf join}, das wir auch verwendet haben um
eine Datei in eine ältere Revision zurückzuführen):
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -j Release-1999\_05\_01-bugfixes
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.5 and 1.5.2.1 into hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/b-subdir/random.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} retrieving revision 1.2.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.2 and 1.2.2.1 into random.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} M b-subdir/random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cvs -q ci -m ''merged from branch Release-1999\_05\_01-bugfixes''
     \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/cvs/myproj/hello.c,v \verb+<+ó hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} new revision: 1.7; previous revision: 1.6\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/cvs/myproj/b-subdir/random.c,v \verb+<+ó random.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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dies bringt die Veränderungen von der Wurzel des Zweiges an dessen
Spitze und verschmilzt diese mit der aktuellen Arbeitskopie (die
daraufhin diese Veränderungen genau so aufweist, als wäre
sie von Hand in diesen Zustand gebracht worden). Diese Veränderungen
werden dann per {\bf commit} in die Hauptentwicklungslinie eingebunden, da
sich nichts im Archiv verändert, wenn merge auf eine Arbeitskopie
angewendet wird.
   

   
  \par
  
Obwohl in diesem Beispiel keine Konflikte auftraten, ist es möglich
(sogar wahrscheinlich), dass welche bei einem normalen {\bf Merge}
auftreten. Ist dies der Fall, müssen diese, wie bei jedem anderen
Konflikt auch, zuerst aufgelöst und dann wieder per {\bf commit} eingebracht
werden.
   


   
  \par
  
{\bf Mehrfache Verschmelzung}
   

   
  \par
  
Manchmal wird ein Zweig aktiv weiterentwickelt, obwohl die
Hauptentwicklungslinie bereits damit verschmolzen wurde. Dies kann zum
Beispiel dann vorkommen, wenn ein zweiter Fehler in der letzten
veröffentlichten Version gefunden wird und in dem Zweig behoben werden
muss. Vielleicht hat jemand den Witz in {\bf random.c} nicht verstanden,
also muss in dem Zweig eine Zeile zu dessen Erklärung eingefügt
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /home/whatever/myproj\_branch\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /* Print out a random number. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void main ()\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} {\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''A random number.$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Get the joke?$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
und per {\bf commit} abgeschickt werden. Wenn diese Fehlerbereinigung nun
auch in der Hauptentwicklungslinie durchgeführt werden muss, könnte
man versucht sein, das gleiche {\bf update}-Kommando wie zuvor in der
Arbeitskopie der Hauptentwicklungslinie durchzuführen, um eine
{\bf Neuverschmelzung} zu machen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -j Release-1999\_05\_01-bugfixes
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/hello.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.5.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.5 and 1.5.2.1 into hello.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/b-subdir/random.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} retrieving revision 1.2.2.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.2 and 1.2.2.2 into random.c\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 \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Wie Sie sehen können, hatte dies nicht den erwarteten Effekt - es wird
ein Konflikt angezeigt, obwohl die Kopie der Hauptentwicklungslinie
nicht verändert wurde und deshalb keine Konflikte zu erwarten waren.
   

   
  \par
  
Das Problem ist, dass das {\bf update}-Kommando genau wie beschrieben
arbeitete: Es versuchte alle Veränderungen zwischen der Zweigwurzel
und -spitze mit der aktuellen Arbeitskopie zu verschmelzen. Das
Problem hier ist, dass einige dieser Veränderungen bereits mit dieser
Arbeitskopie verschmolzen wurden. Daher der Konflikt:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
pwd
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /home/whatever/myproj\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
cat b-subdir/random.c
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} /* Print out a random number. */\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \#include \verb+<+stdio.h\verb+>+\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} void main ()\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+<+\verb+<+ random.c\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''A random number.$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} =======\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''A random number.$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} printf (''Get the joke?$\backslash$n'');\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} \verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+\verb+>+ 1.2.2.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} }\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Man könnte nun alle diese Konflikte durchgehen und von Hand auflösen -
es ist gewöhnlich nicht schwer herauszufinden, was in jeder Datei
verändert werden muss. Dennoch ist es besser, Konflikte von
vornherein zu verhindern. Durch die Angabe von zwei {\bf -j}-Optionen
anstatt von einer kann man nur jene Veränderungen bekommen, die nach
dem Zeitpunkt der letzten Verschmelzung mit der Spitze stattgefunden
haben, anstatt alle Veränderungen des Zweiges von der Wurzel bis zur
Spitze. Das erste {\bf -j} gibt dabei den Startpunkt auf dem Zweig und das
zweite einfach den Zweignamen an (welcher die Spitze des Zweiges
einschließt).
   

   
  \par
  
Die Frage ist nur, wie kann der Punkt der letzten Verschmelzung auf
dem Zweig spezifiziert werden? Eine Möglichkeit ist, ein Datum mit dem
Namen der Zweigmarkierung anzugeben. CVS bietet dafür eine eigene
Syntax:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -j ''Release-1999\_05\_01-bugfixes:2 days ago'' $\backslash$
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} -j Release-1999\_05\_01-bugfixes\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2.2.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.2.2.1 and 1.2.2.2 into random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Folgt dem Namen der Zweigmarkierung ein Doppelpunkt und ein Datum (in
einem der üblichen CVS-Datumsformate), werden von CVS nur
Veränderungen berücksichtigt, die nach diesem Datum stattfanden. Wenn
Sie also wissen, dass die ursprüngliche Bereinigung des Fehlers vor
drei Tagen per {\bf commit} in den Zweig einfloss, würde das vorstehende
Kommando nur die neue Fehlerbereinigung einfließen lassen.
   

   
  \par
  
Ein besserer Weg ist, wenn man im Voraus plant, den Zweig nach jeder
Fehlerbereinigung zu markieren (nur eine normale Markierung, es soll
kein neuer Zweig oder etwas Ähnliches damit begonnen werden). Stellen
Sie sich vor, nach der Beseitigung des Fehlers in dem Zweig und
anschließendem {\bf commit} führen Sie Folgendes in der Arbeitskopie des
Zweiges aus:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q tag Release-1999\_05\_01-bugfixes-fix-number-1
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} T README.txt\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
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dann, wenn es Zeit wird, die zweite Veränderung mit der
Hauptentwicklungslinie zu verschmelzen, können Sie diese sinnvoll
platzierte Markierung verwenden, um die vorangegangenen Revisionen
einzuschränken:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs -q update -j Release-1999\_05\_01-bugfixes-fix-number-1 $\backslash$
     \end{scriptsize} \end{tt} \linebreak 
    \begin{tt} \begin{scriptsize} -j Release-1999\_05\_01-bugfixes\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} RCS file: /usr/local/cvs/myproj/b-subdir/random.c,v\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2.2.1\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} retrieving revision 1.2.2.2\end{scriptsize} \end{tt} \linebreak
    \linebreak\begin{tt} \begin{scriptsize} Merging differences between 1.2.2.1 and 1.2.2.2 into random.c\end{scriptsize} \end{tt} \linebreak
    \begin{tt} \begin{scriptsize}  \linebreak user@linux \~{}/ \$ 
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dies ist natürlich wesentlich besser als zu versuchen, sich daran zu
erinnern, wann man die eine oder andere Veränderung gemacht hat,
funktioniert aber nur, wenn man daran denkt, den Zweig jedes Mal zu
markieren, wenn man ihn mit der Hauptentwicklungslinie verschmolzen
hat. Die Lehre daraus ist also früh zu markieren, und das oft! Es ist
besser, sich durch zu viele Markierungen kämpfen zu müssen (solange
diese aussagekräftige Namen haben), als zu wenige zu haben. In den
vorangegangenen Beispielen war es zum Beispiel nicht notwendig, dass
die neuen Zweigmarkierungen einen ähnlichen Namen wie der Zweig selbst
haben. Obwohl ich diesen {\bf Release-1999\_05\_01-bugfixes-fix-number-1}
genannt habe, könnte er genauso gut {\bf fix1} heißen. Dennoch ist die
erste Variante vorzuziehen, weil diese den Namen des Zweiges
beinhaltet und so kaum mit einer Markierung eines anderen Zweiges
verwechselt werden kann. (Denken Sie daran, dass Markierungsnamen
lediglich innerhalb einer Datei einmalig sind, nicht innerhalb von
Zweigen. Es kann keine zwei Markierungen {\bf fix1} innerhalb einer Datei
geben, auch wenn sich diese auf Revisionen in unterschiedlichen
Zweigen beziehen.)
   


   
  \par
  
{\bf Markierungen und Zweige ohne Arbeitskopie erstellen}
   

   
  \par
  
Wie vorher schon erwähnt, beeinflusst das Markieren das Archiv, nicht
die Arbeitskopie. Dies wirft die Frage auf: Warum wird überhaupt eine
Arbeitskopie zum Markieren benötigt? Der einzige Zweck ist, das
Projekt und die Revisionen der verschiedenen Dateien innerhalb des
Projektes anzugeben, auf die sich die Markierung beziehen soll. Würde
man das Projekt und die Revisionen unabhängig von der Arbeitskopie
angeben, würde gar keine Arbeitskopie benötigt.
   

   
  \par
  
Dies ist auch möglich: mit dem {\bf rtag}-Kommando ({\bf repository tag},
Markieren im Archiv). Dies ist sehr ähnlich zu {\bf tag}; ein paar Beispiele
sollen seine Verwendung erläutern. Gehen wir zurück zu dem
Augenblick, als der erste Fehler berichtet wurde und wir einen Zweig
erzeugen mussten, der zum Zeitpunkt der letzten veröffentlichten
Version seine Wurzel hatte. Es wurde dann ein {\bf Checkout} mit der
Markierung der Veröffentlichung gemacht und anschließend {\bf tag -b}
darauf angewendet:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs tag -b Release-1999\_05\_01-bugfixes
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Dies erstellte einen Zweig, der bei {\bf Release-1999\_05\_01} verwurzelt
ist. Wir kannten allerdings die Markierung der Veröffentlichung und
hätten diese mit dem {\bf rtag}-Kommando verwenden können, um die Wurzel
der Verzweigung anzugeben, ohne uns um eine Arbeitskopie kümmern zu
müssen:
   

   
    \begin{tt} \begin{scriptsize} user@linux \~{}/ \$ 
cvs rtag -b -r Release-1999\_05\_01 Release-1999\_05\_01-bugfixes myproj
     \end{scriptsize} \end{tt} \linebreak 
   

   
  \par
  
Das ist alles. Dieses Kommando kann von überall innerhalb oder
außerhalb einer Arbeitskopie ausgeführt werden. Die
{\bf CVSROOT}-Umgebungsvariable müsste natürlich schon auf das Archiv
verweisen, oder man könnte dies mit der {\bf -d}-Option direkt angeben.
Dies funktioniert ebenso bei Nicht-Verzweigungsmarkierungen, ist aber
weniger nützlich, da dann die Revisionsnummern jeder Datei
nacheinander angegeben werden müssten.(Man könnte sich natürlich
auch mittels einer Markierung darauf beziehen, doch dann wäre ja schon
eine Markierung dieser Revision vorhanden, und warum sollte man dann
noch eine hinzufügen?)
   

   
  \par
  
Sie wissen nun genug, um mit CVS zurechtzukommen, und vielleicht auch
schon genug, um mit anderen Leuten an einem Projekt zu arbeiten. Es
gibt immer noch einige weniger wichtige Funktionen, die noch nicht
eingeführt wurden, genau wie einige noch nicht erwähnte nützliche
Optionen zu Funktionen, die bereits erwähnt wurden. Diese werden alle
in entsprechenden, noch kommenden Kapiteln vorgestellt, zusammen mit
Szenarien, in denen sowohl gezeigt wird, wie und wann man diese
einsetzt. Im Zweifelsfall konsultieren Sie das Cederqvist-Handbuch;
dies ist eine unverzichtbare Quelle für jeden ernsthaften CVS-Benutzer.
   

   \newcounter{d6e6967} 
                  \begin{list}{\arabic{d6e6967}}
                  {\usecounter{d6e6967}}
        
    
	\item 
Anm. d. Übers.: engl. {\bf directory} = Verzeichnis
    
    
	\item 
Anm. d. Übers.: engl. {\bf hunk} = Stück
    
    
	\item 
Anm. d. Übers.: engl. {\bf sticky} = bindend, anhaftend, klebrig
    
    
	\item 
Anm. d. Übers.: engl. {\bf branch} = Zweig, Verzweigung
    
   \end{list}

  

 
	\ref{inhalt.tex}


	\end{document}
	
