SelfLinux Programmierung CVS CVS für Fortgeschrittene Abschnitt 4 | SelfLinux-0.13.0

| CVS für Fortgeschrittene | Autor Formatierung GPL

#### 6 Überblick über die Projekthistorie

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

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

user@linux ~$ cd /usr/local/newrepos/CVSROOT user@linux ~$ ls -l history ls: history: No such file or directory

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

user@linux ~$ touch history user@linux ~$ ls -l history -rw-r--r-- 1 jrandom cvs 0 Jul 22 14:57 history

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

user@linux ~$ chmod a+rw history user@linux ~$ ls -l history -rw-rw-rw- 1 jrandom cvs 0 Jul 22 14:57 history

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

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

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

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

Ist doch alles klar verständlich, oder?

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

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

Das generelle Format der Ausgabe ist

KÜRZEL DATUM BENUTZER [REVISION] [DATEI] PFAD_IM_ARCHIV NAME_DER_ARBEITSKOPIE

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

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

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

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

Tabelle 6.1 Die Bedeutung der Buchstabenkürzel

Option | Bedeutung
--- | ---
-m MODUL | Zeige Vorgänge, die MODUL betreffen
-c | Zeige Commit-Vorgänge
-o | Zeige Checkout-Vorgänge
-T | Zeige alle Vorgänge, die Marken (Tags) betreffen
-x KÜRZEL | Zeige alle Vorgänge, die vom Typ KÜRZEL sind (mindestens eines aus OTFWUGCMARE, siehe Tabelle 6.1)
-e | Zeige einfach alle Vorgänge

Tabelle 6.2 Optionen, die nach Ereignistyp filtern

Option | Bedeutung
--- | ---
-a | Zeige die Aktionen aller Benutzer
-w | Zeige nur die Aktionen, die aus dieser Arbeitskopie heraus vorgenommen wurden
-l | Zeige nur die letzte solche Aktion dieses Benutzers
-u BENUTZER | Zeige die Einträge für BENUTZER

Tabelle 6.3 Optionen, die Benutzer heraus filtern

#### 7 Detaillierter Überblick über Projektaktivitäten: Anmerkungen

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

user@linux ~$ floss$ cvs annotate
Annotations for README.txt
***************
1.14 (jrandom 25-Jul-99): blah
1.13 (jrandom 25-Jul-99): test 3 for history
1.12 (qsmith 19-Jul-99): test 2
1.11 (qsmith 19-Jul-99): test
1.10 (jrandom 12-Jul-99): blah
1.1 (jrandom 20-Jun-99): Just a test project.
1.4 (jrandom 21-Jun-99): yeah.
1.5 (jrandom 21-Jun-99): nope.
Annotations for hello.c
***************
1.1 (jrandom 20-Jun-99): #include <stdio.h>
1.1 (jrandom 20-Jun-99):
1.1 (jrandom 20-Jun-99): void
1.1 (jrandom 20-Jun-99): main ()
1.1 (jrandom 20-Jun-99): {
1.15 (jrandom 25-Jul-99): /* another test for history */
1.13 (qsmith 19-Jul-99): /* random change number two */
1.10 (jrandom 12-Jul-99): /* test */
1.21 (jrandom 25-Jul-99): printf ("Hellooo, world!\n");
1.3 (jrandom 21-Jun-99): printf ("hmmm\n");
1.4 (jrandom 21-Jun-99): printf ("double hmmm\n");
1.11 (qsmith 18-Jul-99): /* added this comment */
1.16 (qsmith 25-Jul-99): /* will merge these changes */
1.18 (jrandom 25-Jul-99): /* will merge these changes too */
1.2 (jrandom 21-Jun-99): printf ("Goodbye, world!\n");
1.1 (jrandom 20-Jun-99): }
Annotations for a-subdir/whatever.c
***************
1.3 (jrandom 25-Jul-99): /* A completely non-empty C file. */
Annotations for a-subdir/subsubdir/fish.c
***************
1.2 (jrandom 25-Jul-99): /* An almost completely empty C file. */
Annotations for b-subdir/random.c
***************
1.1 (jrandom 20-Jun-99): /* A completely empty C file. */

Die Ausgabe von annotate lässt sich intuitiv erfassen. Links sind Revisionsnummer, Entwickler und da Datum, zu dem die fragliche Zeile hinzugefügt oder verändert wurde. Rechts sieht man die eigentliche Zeile zur jeweils aktuellen Revision. Da jede Zeile mit Anmerkungen (also Revisionsnummer, Entwickler und Datum) versehen ist, bekommt man den ganzen Inhalt der Datei aufgelistet, aber um die Anmerkungen nach rechts verschoben.

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

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

printf ("Hellooo, world!\n");

Ein Weg herauszufinden, was sie getan hat, ist, sich den diff10 zwischen dieser Revision und der vorangegangenen anzusehen:

user@linux ~$ cvs diff -r 1.20 -r 1.21 hello.c index: hello.c =============================================== RCS file: /usr/local/newrepos/myproj/hello.c,v retrieving revision 1.20 retrieving revision 1.21 diff -r1.20 -r1.21 9c9 < printf ("Hello, world!\n"); -- > printf ("Hellooo, world!\n");

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

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

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

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

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

user@linux ~$ cvs annotate -D "5 weeks ago" hello.c
Annotations for hello.c
***************
1.1 (jrandom 20-Jun-99): #include <stdio.h>
1.1 (jrandom 20-Jun-99):
1.1 (jrandom 20-Jun-99): void
1.1 (jrandom 20-Jun-99): main ()
1.1 (jrandom 20-Jun-99): {
1.1 (jrandom 20-Jun-99): printf ("Hello, world!\n");
1.1 (jrandom 20-Jun-99): }
floss$ cvs annotate -D "3 weeks ago" hello.c
Annotations for hello.c
**************
1.1 (jrandom 20-Jun-99): #include <stdio.h>
1.1 (jrandom 20-Jun-99):
1.1 (jrandom 20-Jun-99): void
1.1 (jrandom 20-Jun-99): main ()
1.1 (jrandom 20-Jun-99): {
1.1 (jrandom 20-Jun-99): printf ("Hello, world!\n");
1.3 (jrandom 21-Jun-99): printf ("hmmm\n");
1.4 (jrandom 21-Jun-99): printf ("double hmmm\n");
1.2 (jrandom 21-Jun-99): printf ("Goodbye, world!\n");
1.1 (jrandom 20-Jun-99): }

Anmerkungen und Verzweigungen

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

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

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

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

oder auch eine vollständige Revisionsnummer innerhalb der Zweigversion:

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

Wenn Sie so vorgehen, vergessen Sie nicht, dass die Nummern nur für diese eine Datei gültig sind. Generell ist es wohl besser, - sofern möglich - den Namen der Zweigversion zu benutzen.