Introduction à RCS (Revision Control System) Frédéric Couchet mad@bocal.cs.univ-paris8.fr APRIL http://www.april.org v 1.1, 23 septembre 1997 Ce document présente l'utilisation des outils RCS pour le contrôle de version. ______________________________________________________________________ Table of Contents: 1. Introduction 2. Principe général 3. Commandes de base 4. Identification automatique 5. Verrouillage ( locking ) 6. L'arbre de révision 7. Autres commandes utiles 8. Ce que ne fait pas RCS 9. Emacs et RCS 10. Copyright ______________________________________________________________________ 11.. IInnttrroodduuccttiioonn Un problème majeur dans le développement et la maintenance des programmes est le contrôle de version. C'est-à-dire la conservation bien organisée de tous les changements effectués sur les programmes développés. Au final, un programme est constitué de différentes versions. L'un des programmes standard pour faire du contrôle de version est GNU's RRCCSS, qui signifie _R_e_v_i_s_i_o_n _C_o_n_t_r_o_l _S_y_s_t_e_m. RCS est un ensemble de commandes effectuant ce travail. Il automatise le stockage, la récupération, la tenue d'un journal et l'identification des différentes révisions de fichiers de différents types (texte de n'importe quel format, et même binaire si les outils associés, tel que diff, peuvent gérer ce type de fichiers). Cependant, la principale utilisation d'un système de contrôle de version s'inscrit dans le cadre de fichiers sources ou de documentations. RCS ne conserve pas une copie entière de chaque nouvelle version. Il stocke des _d_e_l_t_a_s, c'est à dire les différences entre les révisions successives. Pour cela, les changements au fichier _f_i_l_e_n_a_m_e sont conservés dans le fichier _f_i_l_e_n_a_m_e_,_v. Parmi les principales caractéristiques de RCS, on peut noter qu'il permet d'extraire une version antérieure des fichiers, de conserver des journaux des modifications apportées, de conserver l'identification des personnes ayant fait les modifications. RCS permet également de comparer deux versions et fournit un mécanisme pour fusionner deux branches de développement différentes d'un fichier. RCS permet également le verrouillage (_l_o_c_k) d'un fichier, de telle façon qu'une seule personne puisse apporter des changements (les autres personnes peuvent toujours utiliser le fichier, par exemple pour le compiler). RCS a été principalement développé par Walter F. Tichy et Paul Eggert. La version actuelle est la 5.7, elle est disponible sur ftp://prep.ai.mit.edu/pub/gnu/rcs-5.7.tar.gz ou sur ftp://ftp.ibp.fr/pub/gnu/rcs-5.7.tar.gz. Nous verrons, sans toutefois entrer dans les détails, le principe général du contrôle de version, et les principales commandes constituant RCS. Notons qu'il existe d'autres outils de ce type. 22.. PPrriinncciippee ggéénnéérraall La fonction principale de RCS est de gérer des _g_r_o_u_p_e_s _d_e _r_é_v_i_s_i_o_n_s. On peut définir une révision comme un ensemble de textes appelés révisions, qui évoluent les uns après les autres. Une nouvelle révision est créée en éditant une révision actuelle. La révision initiale est la racine de l'arbre des révisions. En effet, RCS organise les révisions en un arbre ancestral. La révision initiale de l'arbre (la racine) est normalement numérotée 1.1 et les révisions successives sont numérotées 1.2, 1.3, 1.4 ... Le premier champ du numéro de révision est appelé le _r_e_l_e_a_s_e _n_u_m_b_e_r, et le second champ est appelé le _l_e_v_e_l _n_u_m_b_e_r. RCS assigne un nouveau numéro de révision en incrémentant le _l_e_v_e_l _n_u_m_b_e_r de la révision précédente. Le _r_e_l_e_a_s_e _n_u_m_b_e_r doit être incrémenté explicitement (uniquement dans le cas d'une transition majeure dans le développement, par exemple lorsqu'une nouvelle release du produit a été complétée). 33.. CCoommmmaannddeess ddee bbaassee L'interface de RCS est relativement simple et seulement deux commandes sont suffisantes dans la plupart des cas : ci et co. Nous supposons l'existence d'un fichier nommé foo.c. Le source du fichier est le suivant : #include main() { printf("Hello world\n"); } Nous allons créer un nouveau groupe de révisions avec foo.c comme révision initiale (1.1). Le groupe sera stocké dans le fichier nommé foo.c,v. Par défaut, le fichier foo.c sera effacé. Pour cela, nous utilisons la commande de _c_h_e_c_k_-_i_n: ci foo.c Cette commande demande également une description pour le groupe. foo.c,v <-- foo.c enter description, terminated with single '.' or end of file: NOTE: This is NOT the log message! >> Par exemple, nous tapons : >> programme hello world. >> . Le texte décrit ce que le programme fait, et ci rappelle que ce n'est pas une entrée dans le journal. Les commandes ci ultérieures demanderont une entrée du journal, laquelle résumera les modifications apportées. Ces messages doivent être brefs, décrivant les changements apportés. Pour extraire la dernière révision dans un groupe, on utilise la commande de _c_h_e_c_k_-_o_u_t : co foo.c Le fichier foo.c est recréé, mais en lecture seule. On peut utiliser le fichier pour le compiler par exemple. En effet, pour installer un logiciel, on extrait toutes les sources en lecture seule, on les compile et on efface les sources. Pour pouvoir modifier le fichier foo.c, nous devons utiliser l'option -l : co -l foo.c Cela permet d'extraire le fichier foo.c mais en posant un verrou sur le fichier. Cela signifie que vous, et vous seul, avez la permission de faire un _c_h_e_c_k_-_i_n sur une nouvelle révision du fichier (ce qui est nécessaire dans le cas où plusieurs personnes travaillent sur un même groupe de révisions). Le fichier foo.c en alors en écriture pour vous. Le système peut être configuré avec la caractéristique du 'strict locking'. Tous les fichiers RCS sont initialisés de telle sorte que les opérations de _c_h_e_c_k_-_i_n nécessitent un verrou sur la révision précédente. Pour désactiver le _s_t_r_i_c_t _l_o_c_k_i_n_g sur un fichier individuel, utilisez la commande : rcs -U foo.c Pour activer le _s_t_r_i_c_t _l_o_c_k_i_n_g sur un fichier individuel, utilisez la commande : rcs -L foo.c La notion de _l_o_c_k_i_n_g est abordée dans un chapitre ultérieur. Nous pouvons alors éditer le fichier foo.c, et une fois les modifications effectuées, en faire une nouvelle révision par la commande: ci foo.c La commande nous demande alors le message du journal : foo.c,v <-- foo.c new revision: 1.2; previous revision: 1.1 enter log message, terminated with single '.' or end of file: >> Affichage 10 fois du message >> . La nouvelle révision est la 1.2. Si vous obtenez lors du ci, le message d'erreur suivant (en supposant que le login de l'utilisateur soit _m_a_d): ci error: no lock set by mad c'est que vous avez oublié de verrouiller la révision lors du co. On peut noter, que pour des raisons pratiques, tous les fichiers RCS (*,v) peuvent être déplacés dans un sous-répertoire nommé RCS. Donc, prenez l'habitude de créer ce répertoire avant de commencer à créer vos révisions. RCS l'utilisera automatiquement. Si vous ne désirez pas effacer le fichier de travail lors du _c_h_e_c_k_-_i_n, vous devez utiliser soit l'option -l, qui entraîne un _l_o_c_k_i_n_g, soit l'option -u qui n'entraîne pas de locking (ces options entraînent un appel à la commande co): ci -l foo.c Les commandes ci et co sont les deux commandes de bases de RCS. 44.. IIddeennttiiffiiccaattiioonn aauuttoommaattiiqquuee Avec RCS, vous pouvez marquer vos fichiers sources et objets par des chaines d'identification spéciales. RCS utilise pour ça la substitution de mots-clés. Par exemple, pour obtenir l'identification, placez le mot-clé suivant dans le texte d'une révision (par exemple dans un commentaire en C): $Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $. La commande co remplacera ce mot-clé avec une chaine de la forme : $Id filename revisionnumber date time author state locker$. Cette chaine décrit le nom du fichier, la révision, la date et l'heure du _c_h_e_c_k_- _o_u_t, l'auteur, l'état (par exemple, _E_x_p pour expérimental) et celui qui verrouille le fichier. Cette chaine sera mise à jour automatiquement à chaque fois. Pour obtenir la même chose dans les fichiers objets, utilisez une chaine littérale de caractères, par exemple en C : static char rcsid[]="$Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $"; Cette possibilité est très importante pour la maintenance des programmes. Ainsi, la commande ident extrait ces mots-clés à partir de n'importe quel fichier (donc les fichiers objets). Cela permet de retrouver quelles révisions de quels modules ont été utilisées pour construire le programme. Il existe d'autres mots-clés d'identification ($Author: seb $, $Date: 2007-05-02 00:23:59 +0200 (mer, 02 mai 2007) $, $Header$, $Locker$ ,$Name$ ,$RCSfile$, $Revision: 12671 $, $Source$, $State$, voir la page de manuel de co). Un mot-clé important est $Log$ voir la page de manuel de co). Un mot-clé important est Revision 1.1 1999/08/18 21:06:08 cvs voir la page de manuel de co). Un mot-clé important est Initial revision voir la page de manuel de co). Un mot-clé important est voir la page de manuel de co). Un mot-clé important est Revision 1.1.1.1 1999/07/27 10:53:43 http voir la page de manuel de co). Un mot-clé important est Web APRIL importe voir la page de manuel de co). Un mot-clé important est voir la page de manuel de co). Un mot-clé important est Revision 1.1.1.1 1999/07/26 21:06:01 cvs voir la page de manuel de co). Un mot-clé important est Arborescence du serveur Web d'APRIL. voir la page de manuel de co). Un mot-clé important est voir la page de manuel de co). Un mot-clé important est Revision 1.1.1.1 1999/07/24 16:16:59 cvs voir la page de manuel de co). Un mot-clé important est Creation du master pour le Web voir la page de manuel de co). Un mot-clé important est. Son fonctionnement est un peu différent, il permet d'accumuler les messages du journal saisis lors du processus de _c_h_e_c_k_-_i_n. On utilise ce mot-clé dans un commentaire en début de fichier source. Ainsi le fichier source est auto-documenté. Prenons un exemple concret, voilà le début du fichier foo.c lors de sa création : /* * $Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $ * * $Log$ * Revision 1.1 1999/08/18 21:06:08 cvs * Initial revision * * Revision 1.1.1.1 1999/07/27 10:53:43 http * Web APRIL importe * * Revision 1.1.1.1 1999/07/26 21:06:01 cvs * Arborescence du serveur Web d'APRIL. * * Revision 1.1.1.1 1999/07/24 16:16:59 cvs * Creation du master pour le Web * * */ static char rcsid[] = "$Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $"; Lorsque nous extrayons la révision 1.2, nous obtenons en début du fichier foo.c : /* * $Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $ * * $Log$ * Revision 1.1 1999/08/18 21:06:08 cvs * Initial revision * * Revision 1.1.1.1 1999/07/27 10:53:43 http * Web APRIL importe * * Revision 1.1.1.1 1999/07/26 21:06:01 cvs * Arborescence du serveur Web d'APRIL. * * Revision 1.1.1.1 1999/07/24 16:16:59 cvs * Creation du master pour le Web * * Revision 1.2 1997/08/23 20:33:36 mad * Affichage 10 fois du message * * Revision 1.1 1997/08/23 20:33:05 mad * Initial revision * * */ static char rcsid[] = "$Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $"; Si nous compilons foo.c pour obtenir a.out, et que nous utilisons la commande : ident a.out nous obtenons : a.out: $Id: rcs.txt 12671 2007-05-01 22:23:59Z seb $ Ce qui nous permet de savoir que le fichier a.out a été obtenu à partir de la révision 1.2 de foo.c. 55.. VVeerrrroouuiillllaaggee (( lloocckkiinngg )) Le problème posé est le suivant : au moins deux personnes veulent déposer des modifications d'une même révision. Si nous supposons deux programmeurs apportant des modifications à une même révision (par exemple la 2.5). Le programmeur A fait un ci sur sa révision avant le programmeur B. Le programmeur B n'a pas vu les modifications de A, donc l'effet est que les changements de A sont couverts par les modifications de B. RCS prévient ce conflit par le verrouillage. Lorsque quelqu'un veut éditer une révision pour la modifier, la révision doit être extraite et verrouillée, en utilisant l'option -l de co. La prochaine opération de _c_h_e_c_k_-_i_n effacera le verrou. Au plus un programmeur à la fois peut verrouiller une révision, et seulement ce programmeur peut la déverrouiller. Par exemple, supposons que le programmeur mad a mis un verrou par la commande co -l foo.c. Maintenant, si le programmeur gunsman veut extraire la révision pour la modifier par la même commande, il aura le message d'erreur suivant : foo.c,v --> foo.c co: foo.c,v: Revision 1.2 is already locked by mad. De plus, chaque fichier RCS possède également une liste d'accès, qui spécifie quels utilisateurs peuvent effectuer des opérations de mise à jour. 66.. LL''aarrbbrree ddee rréévviissiioonn Nous avons vu que les numéros de révisions démarraient à 1.1 et, qu'ensuite, la commande ci incrémentait uniquement le _l_e_v_e_l _n_u_m_b_e_r. Pour incrémenter le _r_e_l_e_a_s_e _n_u_m_b_e_r il faut le faire explicitement avec l'option -r de ci, par exemple : ci -r2.1 foo.c assigne le numéro 2.1 à la nouvelle révision. Un nouveau _c_h_e_c_k_-_i_n donnera ensuite 2.2. Un arbre de révision est constitué normalement d'une branche unique appelée tronc. On peut néanmoins créer des branches latérales. Par exemple, supposons l'arbre de révision suivant : 1.1 -> 1.2 -> 1.3 -> 1.4 -> 2.1 -> 2.2 -> 2.3 ... Cet arbre a 7 révisions groupées en 2 _r_e_l_e_a_s_e_s. La release 1.4 est en activité sur un site client, tandis que la _r_e_l_e_a_s_e 2 est en développement. Imaginons maintenant que le client demande une correction dans la révision 1.4. Nous allons alors créer une branche à la révision 1.4, et insérer les corrections sur cette branche. La première branche démarrant à 1.4 est numérotée 1.4.1 et les révisions sur cette branche sont numérotées 1.4.1.1, 1.4.1.2, 1.4.1.3 ... Cette notation permet de créer d'autres branches à partir de 1.4. Les étapes nécessaires sont les suivantes : co -r1.4 foo.c editer foo.c pour apporter les corrections ci -r1.4.1 foo.c Nous obtenons alors l'arbre suivant : 1.1 -> 1.2 -> 1.3 -> 1.4 -> 2.1 -> 2.2 -> 2.3 ... \ \ \> 1.4.1.1 -> ... Il peut être nécessaire d'incorporer les différences entre 1.4 et 1.4.1.1 dans une révision de la release 2. Pour cela, il faut utiliser la commande rcsmerge qui automatise le processus. Une autre raison de créer une branche est liée au problème posé par un programmeur ayant verrouillé une révision pour la modifier et qui n'a pas encore effectué le dépôt de ses modifications. Si un autre programmeur désire modifier cette révision, il ne peut pas le faire tant que le verrou n'est pas supprimé. La solution est de créer une branche, et ensuite, une fois que le premier programmeur a effectué son _c_h_e_c_k_-_i_n, utiliser la commande rcsmerge pour incorporer les deux révisions. 77.. AAuuttrreess ccoommmmaannddeess uuttiilleess La commande _r_c_s_d_i_f_f permet de comparer la version actuelle avec la plus récente version qui est sauvegardée : rcsdiff -u foo.c L'option -u de rcsdiff permet d'utiliser un diff unifié (_u_n_i_f_i_e_d _d_i_f_f). En fait, rcsdiff accepte toutes les options que la commande diff accepte. Par exemple, pour générer un _c_o_n_t_e_x_t _d_i_f_f, on utilisera l'option -c. Par exemple, nous extrayons la révision 1.2 de foo.c, nous effectuons une petite modification (correction d'un bug), et rcsdiff foo.c nous donne: =================================================================== RCS file: foo.c,v retrieving revision 1.2 diff -u -r1.2 foo.c --- foo.c 1997/08/23 20:33:36 1.2 +++ foo.c 1997/08/24 20:36:20 @@ -24,7 +24,7 @@ main() { int i = 0; - for(i = 0; i <= 10; i++) + for(i = 0; i < 10; i++) printf("Hello world\n"); } Les lignes qui n'ont pas été modifiées sont affichées avec une simple espace en début de ligne. Les lignes effacées dans la version la plus récente ont un - en début de ligne, et les lignes qui ont été ajoutées ont un + en début de ligne. Comme on peut le remarquer, les lignes modifiées sont considérées comme effacées dans l'ancienne version, et les changements sont considérés comme ajoutés dans la nouvelle. Dans le cas d'un _c_o_n_t_e_x_t _d_i_f_f, les lignes marquées avec un ! indiquent un changement, et les lignes marquées avec un + indiquent des lignes ajoutées. On peut également comparer deux versions : rcsdiff -u -r1.2 -r1.4 foo.c Une utilisation de rcsdiff est la génération de mises à jour appliquées par des _p_a_t_c_h. Par exemple, une fois un programme terminé, effectuez un _c_h_e_c_k_-_i_n sur tous les fichiers avec un nouveau numéro de release, par exemple 2.0. Puis, pour la release suivante (par exemple la 3.0), lancez rcsdiff sur la révision 2.0 pour tous les fichiers : rcsdiff -c -r2.0 RCS/* > monprog-3.0.patch 2>&1 Vous obtiendrez ainsi un fichier _p_a_t_c_h que vous pourrez distribuer aux personnes possédant la version 2.0. Une autre possibilité intéressante est de consulter le journal des modifications effectuées. Pour cela on utilise la commande rlog, qui donne l'historique des changements effectués : rlog foo.c Ce qui nous donne : RCS file: foo.c,v Working file: foo.c head: 1.3 branch: locks: strict mad: 1.3 access list: symbolic names: keyword substitution: kv total revisions: 3; selected revisions: 3 description: programme hello world ---------------------------- revision 1.3 date: 1997/08/23 20:36:20; author: mad; state: Exp; lines: +6 -1 Correction d'un petit bug ---------------------------- revision 1.2 date: 1997/08/23 20:33:36; author: mad; state: Exp; lines: +8 -3 Affichage 10 fois du message ---------------------------- revision 1.1 date: 1997/08/23 20:33:05; author: mad; state: Exp; Initial revision ============================================================================= Le plus intéressant ce sont les commentaires saisis lors d'un ci. Par exemple, on peut noter que pour la révision 1.3, le commentaire est _C_o_r_r_e_c_t_i_o_n _d_'_u_n _p_e_t_i_t _b_u_g. A noter que les dates et heures sont en UTC, et non pas dans la zone locale. Cela permet à des développeurs de différentes zones géographiques de pouvoir collaborer. La commande rcs est utilisée pour modifier l'état des fichiers RCS. Par exemple, pour bloquer un fichier qui ne l'est pas ou pour casser un verrou. 88.. CCee qquuee nnee ffaaiitt ppaass RRCCSS Une des choses que RCS ne permet pas de gérer est la modification simultanée de mêmes fichiers par plusieurs utilisateurs. Un autre outil est disponible pour effectuer ce travail, c'est CVS (_C_o_n_c_u_r_r_e_n_t _V_e_r_s_i_o_n _S_y_s_t_e_m). CVS, qui est basé sur RCS, permet également le management des différents release d'un logiciel. CVS est disponible sur ftp://prep.ai.mit.edu/pub/gnu/cvs-1.6.tar.gz ou sur ftp://ftp.ibp.fr/pub/gnu/cvs-1.6.tar.gz. 99.. EEmmaaccss eett RRCCSS L'éditeur GNU Emacs est capable de reconnaître automatiquement un fichier géré par RCS lorsque vous l'éditez. Emacs fournit alors une interface aux commandes de contrôle de version. Vous n'avez donc qu'à apprendre seulement quelques commandes (la plupart débutent par "C-x v") pour faire du contrôle de version, ou alors utiliser le menu _T_o_o_l_s, puis le sous-menu _V_e_r_s_i_o_n _C_o_n_t_r_o_l. Le manuel Emacs contient de la documentation dans le noeud _v_e_r_s_i_o_n _c_o_n_t_r_o_l. 1100.. CCooppyyrriigghhtt Copyright (c) 1997 Frederic Couchet, association APRIL. Ce document peut être reproduit et distribué dans son intégralité ou partiellement, par quelque moyen physique que ce soit. Il reste malgré tout sujet aux conditions suivantes : · La mention du copyright doit être conservée, et la présente section préservée dans son intégralité sur toute copie intégrale ou partielle. · Si vous distribuez ce travail en partie, vous devez mentionnez comment obtenir une version intégrale de ce document et être en mesure de la fournir. · De petites portions de ce document peuvent être utilisées comme illustrations d'une présentation ou comme remarques sans autorisation préalable si les citations d'usage sont réalisées.