2. Le gestionnaire de sources Subversion (en abrégé SVN)

Cette partie du TP est une présentation de l'utilisation de SVN en interaction avec un dépôt hébergé sur la forge SourceSup. Subversion est encore de nos jours l'un des SCM les plus utilisés au monde. Toujours maintenu et régulièrement amélioré, il est d'une utilisation simple et facilite le développement de logiciel informatique. Le TP explorera les principales commandes de SVN.

Documentation officielle de Subversion : https://subversion.apache.org/

2.1 Description

SVN est un gestionnaire de sources. Son principe est d'héberger les sources d'un projet sur un serveur central, et de les rendre accessibles aux personnes souhaitant travailler dessus. Chaque utilisateur possède une copie de travail en local d'une des version du dépôt, souvent la dernière, afin de pouvoir effectuer des développements. Une fois les modifications effectuées, l'utilisateur les transmets au dépôt pour que les autres développeurs puissent les récupérer.

Glossaire de termes utilisés par Subversion

  • repository (dépôt) : lieu central (a priori distant) où sont stockés les fichiers, leurs versions, ainsi que les branches et les tags.
  • working copy (copie de travail) : copie locale d'une version d'un projet sur laquelle vous travaillez.
  • checkout (récupérer) : opération par laquelle vous copiez localement pour la première fois une version du dépôt, pour vous créer une copie de travail.
  • import (importer) : opération inverse qui consiste à copier sur le dépôt un ou plusieurs fichiers que vous avez créés localement.
  • update (mettre à jour) : récupère sur votre copie de travail les dernières modifications publiées sur le dépôt, faisant éventuellement apparaître des conflits.
  • commit (appliquer) : opération qui copie dans le dépôt vos fichiers modifiés du projet, créant ainsi une nouvelle version.

2.2 Objectif de cette partie du TP

Le but est d'être capable d'utiliser un client “ligne de commande” Subversion et d'interagir sur les sources du projet, comprendre le fonctionnement des branches, de leur fusion, ainsi que la gestion des conflits. Pour cela, nous nous appuierons sur une petite application web PHP très simple, sur laquelle sera effectuée des modifications.

  • Dans toute cette documentation, le numéro du poste sera est XXX ;
  • Un petit script permet d'entrer votre numéro de poste et de remplacer toutes les occurrences de XXX dans cette page ;
  • Vous pourrez à tout moment corriger ou refaire l'opération en tapant simplement votre nom de poste dans le champs ci dessous.
  • Le numéro du poste sera à déterminer au début du TP

Mettez le numéro du poste dans le champ suivant :

Numéro de votre poste :

2.3 Application servant de support

Pour expliciter la formation sur Subversion, nous nous appuierons sur le développement d'une petite application PHP. Cette application permettra d'afficher, de créer, modifier, supprimer des objets “person” représentant des personnes.

Le code est fourni dans le TP au fur et à mesure, ainsi que la création de la base.

  • Installons maintenant les différents packets nécessaire à notre TP
# yum install emacs apache php mysql-server php-mysql
  • Installer le repo WANDISCO permettant d'installer la dernière version de subversion sur CentOS
    • Créer le fichier /etc/yum.repos.d/wandisco-svn.repo et éditer le
# touch /etc/yum.repos.d/wandisco-svn.repo
# emacs /etc/yum.repos.d/wandisco-svn.repo
/etc/yum.repos.d/wandisco-svn.repo
[WandiscoSVN]
name=Wandisco SVN Repo
baseurl=http://opensource.wandisco.com/centos/6/svn-1.8/RPMS/$basearch/
enabled=1
gpgcheck=0
  • Installer Subversion
# yum clean all
# yum install subversion

2.4 Dépôt Subversion du TP

Pour ce TP nous allons utiliser un dépôt SVN situé sur la plateforme SourceSup. Ce dépôt est lié à un projet SourceSup qui permet de gérer les droits d'accès à ce dépôt.

Les stagiaires effectuant le TP vont chacun se créer un projet sur la forge, et ainsi auront un dépôt pour leur usage.

  • Se rendre sur la page d'accueil de la forge
  • S'authentifier via son établissement ou un compte CRU
  • Cliquer sur l'onglet “Ma page”
  • Cliquer sur le menu “Enregistrer un projet”
  • Remplir les champs du formulaire avec les valeurs suivantes :
Nom complet du projet : form-svn-XXX
Etablissement : GIP RENATER 
Description publique du projet : projet support du TP de la formation SVN
Nom Unix du projet : form-svn-XXX 
Code source : choisir subversion
  • Valider
  • Votre projet est maintenant créé
  • Le dépôt va être prochainement initialisé avec un petit délai

2.4.1 Activer des plugins

  • Cliquer sur le lien “Administration”, puis le sous onglet “Outils”
  • Cocher les cases des plugins :
    • Sympa - Permet de gérer des listes de diffusion hébergées sur un serveur Sympa
    • Scmhook - active des hooks pré-définis sur les dépôts SVN/Git

2.5 Récupération des sources d'un dépôt existant

Nous placerons les copies de travail dans le dossier /opt/ de la machine locale.

# cd /opt/

Afin de pouvoir travailler sur les sources du projet, il va falloir récupérer les sources du dépôt en local. Pour cela, une commande du client SVN permet de se connecter au serveur, de s'authentifier, puis de télécharger les fichiers. Le dépôt venant juste d'être créé et initialisé, il ne contient que la première révision.

Dans certaines commandes, il est indiquer le mot clé USERNAME. Il est à remplacer par votre nom de compte unix. Vous pouvez retrouver ce nom en accédant à la page "Mon compte" sur la forge.
# svn checkout --username USERNAME https://subversion.renater.fr/authscm/USERNAME/svn/form-svn-XXX /opt/form-svn-XXX
 
A    form-svn/trunk
A    form-svn/branches
A    form-svn/tags
Checked out revision 1.
Répondre “non” à la question du stockage du mot de passe en clair sur le poste. Pour éviter cette question à l'avenir :
  • Modifier le fichier ~/.subversion/servers et décommenter la propriété “store-plaintext-passwords” tout en bas du fichier :
~/.subversion/servers
...
[global]
store-plaintext-passwords = no
...

Les sources du projet ont été récupérées en local, nous allons pouvoir travailler dessus, ajouter et modifier des fichiers. Comme indiqué dans le résultat de la commande, un dossier a été créé sur la copie de travail locale, et 3 sous dossiers sont également présents :

  • trunk : dossier où se trouve la dernière version du dépôt
  • branches : dossier servant à regrouper l'ensemble des branches créées
  • tags : dossier regroupant les étiquettes créées

Maintenant que notre copie locale est fonctionnelle, nous allons pouvoir effectuer des modifications dessus.

2.6 Créer des fichiers/dossier sur la copie de travail

Nous allons commencer par créer l'arborescence et les fichiers nécessaires à notre application PHP.

  • Le dossier db contiendra les scripts de création de la base de données
  • Le dossier apache-conf regroupera les fichiers de configuration pour le serveur web Apache
  • Le dossier common contiendra les sources métiers de l'application
# cd /opt/form-svn-XXX/trunk
# mkdir db apache-conf common

Un nouveau dossier ou fichier ne fait pas parti automatiquement du suivi de version. Il faut l'ajouter explicitement, avec la commande “svn add”. Cette commande permet de marquer un fichier/dossier “pour ajout”. C'est à dire qu'il sera transmis au dépôt central lors de la prochaine propagation.

# svn add db apache-conf common
 
A         db
A         apache-conf
A         common

Si on ajoute un répertoire au suivi de version, par défaut tous les fichiers situés dedans sont ajouté automatiquement au suivi de version également. Nous allons initialiser une page d'accueil pour notre application ainsi qu'une page permettant de créer des objets. Pour regrouper ces pages, nous créons le dossier “www”.

# mkdir www
# touch www/index.php
# svn add www
 
A         www
A         www/index.php

On voit que tous les fichiers situés dans le dossier “www” sont marqués également “pour ajout”.

Il est possible de créer un dossier et d'indiquer directement à SVN de le marquer pour ajout, avec la commande :
# svn mkdir nom_dossier.
Si vous rencontrez ce besoin, il est aussi possible de marquer “pour ajout” un dossier sans inclure les fichiers et sous dossiers qu'il contient en précisant à la commande “svn add” l'argument “–depth empty” :
# svn add --depth empty mon_dossier

2.7 Status des fichiers dans le suivi de versions

Nos fichiers sont maintenant marqués comme “à ajouter” lors du prochain commit. La commande “status” permet d'afficher l'état des fichiers de la copie de travail.

# svn status
 
A       apache-conf
A       common
A       db
A       www
A       www/index.php

Le status 'A' indique que le fichier sera transmis au dépôt distant lors de la prochaine exécution de la commande “commit”.

Un rappel des status les plus courant est présent dans la partie Annexe

2.8 Propager les modifications vers le dépôt

Les fichiers et dossiers créés sur la copie de travail restent pour l'instant en local et le dépôt distant n'en a pas connaissance. Pour les transmettre à celui-ci, l'utilisateur doit exécuter la commande SVN “commit”. Celle-ci communique avec le dépôt, compare la dernière révision avec celle présente en locale. Deux cas se présente alors :

  • cas 1 : Si la version est la même, le client SVN transmet les modifications au dépôt, et une nouvelle révision est créée sur celui-ci.
  • cas 2 : Si la version du dépôt est plus récente, l'utilisateur doit d'abord récupérer les dernières mises à jour avant de pouvoir transmettre les siennes.

Pour l'instant, nous somme dans le cas 1, mais pour prendre l'habitude des bonnes pratiques SVN, lançons la commande “update” avant de propager nos développements.

# svn update .
 
Updating '.':
At revision 1.

Aucune mise à jour récupérer, nous pouvons exécuter notre “commit” :

# svn commit -m "ajout de l'arborescence" 
 
Adding         apache-conf
Adding         common
Adding         db
Adding         www
Adding         www/index.php
Transmitting file data ...
Committed revision 2.
L'argument '-m' est important, car il permet de décrire pourquoi un utilisateur a effectué un commit. Un commit doit représenter une correction, une évolution, une fonctionnalité. La signification du commit peut alors être évoquée dans son commentaire. Dans la vie d'un projet, il est fort peu probable que les développeurs se souviennent pourquoi chaque révisions ont été faites si aucun commentaire n'est présent pour l'expliquer.
Si le message à associer au commit est long ou complexe, l'option –file (-F) permet d'indiquer quel fichier contenant le message, est a associé au commit.

2.9 Information sur la copie locale et le dépôt

Une nouvelle révision a été créée, la numéro 2. La copie de travail locale ainsi que le dépôt distant sont passés tous les deux à cette nouvelle révision. Il est possible de le vérifier avec la commande SVN “info”

# svn info .
 
Path: .
Working Copy Root Path: /trunk/form-svn-XXX
URL: https://subversion.renater.fr/form-svn-XXX
Repository Root: https://subversion.renater.fr/form-svn-XXX
Repository UUID: ....
Revision: **1**
Node Kind: directory
Schedule: normal
Last Changed Author: ....
Last Changed Rev: **1**
Last Changed Date: ....

Cette commande affiche les informations de la copie de travail locale.

Remarquer que la révision locale est toujours 1, et non 2. En effet la commande commit provoque la création d'une nouvelle révision sur le dépôt distant. Pour passer à la révision suivante localement, il faut exécuter un “svn update”.
# svn update .
 
At revision 2.
# svn info .
 
Path: .
Working Copy Root Path: /trunk/form-svn-XXX
URL: https://subversion.renater.fr/form-svn-XXX
Repository Root: https://subversion.renater.fr/form-svn-XXX
Repository UUID: ....
Revision: **2**
Node Kind: directory
Schedule: normal
Last Changed Author: ....
Last Changed Rev: **2**
Last Changed Date: ....
  • Il est possible aussi d'afficher les informations du dépôt distant :
# svn info https://subversion.renater.fr/form-svn-XXX
 
Path: form-svn-XXX
URL: https://subversion.renater.fr/form-svn-XXX
Repository Root: https://subversion.renater.fr/form-svn-XXX
Repository UUID: ....
Revision: 2
Node Kind: directory
Last Changed Author: ....
Last Changed Rev: 2
Last Changed Date: ....

2.10 Effectuer des modifications

Commençons le développement de notre application. Nous allons éditer les fichiers que nous avons créé. Mais avant cela, nous allons simuler que nous ne sommes pas seul à travailler sur ce dépôt, mais deux développeurs. En effet il est fort probable que plusieurs personnes travaillent sur le même projet et donc le même dépôt.

Une première copie de travail existe déjà sur notre machine locale, nous allons en créer une seconde, indépendante de la première.

# cd /opt/
# svn checkout https://subversion.renater.fr/form-svn-XXX /opt/form-svn-XXX_copie2
 
A    form-svn-XXX_copie2/trunk
A    form-svn-XXX_copie2/trunk/apache-conf
A    form-svn-XXX_copie2/trunk/www
A    form-svn-XXX_copie2/trunk/www/index.php
A    form-svn-XXX_copie2/branches
A    form-svn-XXX_copie2/tags
Checked out revision 2.
  • Notre deuxième copie est prête.
  • plaçons nous dans cette deuxième copie de travail
# cd /opt/form-svn-XXX_copie2/trunk
  • Nous allons écrire la classe représentant les objets “person”
# touch common/person.class.php
# emacs common/person.class.php
/opt/form-svn-XXX_copie2/common/person.class.php
<?php
/**
* Class Person
*/
class Person {
 
        var $id;
        var $prenom;
        var $nomfamille;
        var $adresse;
        var $ville;
 
        function __construct() {
        }
}
?>
  • Ajoutons au suivi de version et commitons notre fichier
# svn add common/person.class.php 
A         common/person.class.php
# svn ci -m "ajout de la classe person"
Adding         common/person.class.php
Transmitting file data .
Committed revision 3.
  • Revenons maintenant sur notre première copie de travail
# cd /opt/form-svn-XXX/trunk
  • Complétons également la classe “person” en créant le fichier common/person.class.php
# touch common/person.class.php
# emacs common/person.class.php 
common/person.class.php
<?php
 
/**
* Class Person
*/
class Person {
 
        var $id;
        var $firstname;
        var $lastname;
        var $address;
        var $city;
 
        function __construct() {
        }
}
?>
  • essayons de commiter notre fichier
# svn add common/person.class.php
# svn commit -m "ajout de la classe person"
 
Adding         common/person.class.php
svn: E155011: Commit failed (details follow):
svn: E155011: File '.../form-svn-XXX/trunk/common/person.class.php' is out of date
svn: E175005: File 'person.class.php' already exists
  • Une erreur est renvoyée par le dépôt distant, qui refuse la propagation. En effet une révision plus récente est présente sur le dépôt.
  • De plus une autre erreur est retournée, car le fichier 'person.class.php' existe déjà et ne peut être créé une deuxième fois sur le dépôt.
  • Il faut donc mettre à jour notre copie de travail locale
# svn update .
 
Updating '.':
Conflict discovered in '/Volumes/perso/workspace/form-svn/trunk/common/person.class.php'.
Select: (p) postpone, (df) diff-full, (e) edit,
        (mc) mine-conflict, (tc) theirs-conflict,
        (s) show all options: 
  • Nous rencontrons un conflit, générer par le fait qu'un autre utilisateur a déjà commiter le même fichier que nous. Plusieurs choix s'offre à nous :
    • df : Voir toutes les différences entre les deux fichiers
    • e : Lance l'éditeur par défaut afin de modifier le fichier local
    • mc : Informe le client SVN qu'il faut garder la version locale du fichier, et ne pas tenir compte des modifications présente sur la version du dépôt
    • tc : La version locale du fichier est remplacée par le contenu de celle présente sur le dépôt
    • p : La résolution du conflit est reportée, 3 fichiers supplémentaires sont alors créés, nous avons donc 4 fichiers 'person.class.php' :
      • person.class.php : fichier contenant la fusion entre les deux versions en conflit
      • person.class.php.mine : version locale du fichier avant conflit
      • person.class.php.r0 : fichier lors de la révision précédente
      • person.class.php.r3 : version du fichier du dépôt
  • Choisir le choix 'p'
C    common/person.class.php
Updated to revision 3.
Summary of conflicts:
  Text conflicts: 1
  • Le client Subversion nous indique qu'il y a un conflit présent sur notre copie de travail.
  • Résolvons ce conflit, pour cela éditons le fichier 'person.class.php'
common/person.class.php
**<<<<<<< .mine**
<?php
 
/**
* Class Person
*
*/
class Person {
 
        var $id;
        var $firstname;
        var $lastname;
        var $address;
        var $city;
 
        function __construct() {
 
        }
}
?>**=======**
<?php
 
/**
* Class Person
*
*/
class Person {
 
        var $id;
        var $prenom;
        var $nomfamille;
        var $adresse;
        var $ville;
 
        function __construct() {
        }
}
?**>>>>>>>> .r3**
Ce fichier est le résultat de la fusion entre les deux versions du fichier.
  • La partie débutant par '«««< .mine' présente la portion de code de la version locale. Elle se termine avec '======='.
  • La partie suivant '=======' et se terminant avec '»»»» .r3' correspond à la version du dépôt.
  • Pour résoudre notre conflit, nous allons supprimer la partie commençant par '«««< .mine' et finissant par '======='. Il faut aussi supprimer le bout de code '»»»» .r3' à la fin du fichier. Le résultat du fichier est donc :
common/person.class.php
<?php
 
/**
* Class Person
*
*/
class Person {
        var $id;
        var $firstname;
        var $lastname;
        var $address;
        var $city;
 
        function __construct() {
        }
}
  • Il reste à informer le client SVN que nous avons résolu le conflit :
# svn resolved common/person.class.php
Resolved conflicted state of 'common/person.class.php'
  • A la suite de cette commande, les fichiers 'person.class.php.r3', 'person.class.php.r0' et 'person.class.php.mine' sont supprimés par le client SVN car ils ne sont plus utiles, le conflit a été résolu.
  • Nous pouvons maintenant propager notre fichier vers le dépôt
# svn ci -m "ajout de la nouvelle classe person" 
Sending        common/person.class.php
Transmitting file data .
Committed revision 4.

2.11 Déplacement de fichier

Le déplacement d'un fichier est un peu particulier. Pour déplacer un fichier qui a déjà été commiter sur le dépôt distant, il ne faut pas utiliser la commande de base du système (mv ancien_fichier nouveau_fichier par exemple). En effet le dépôt a lui connaissance d'un fichier se situant à un chemin particulier, et si le fichier est déplacé en local sans que le client Subversion en soit averti, le dépôt ne va pas s'y retrouver lors du prochain commit.

  • Il faut donc utiliser la commande “svn mv” qui va déplacer le fichier.
# svn mv www/index.php www/index.php.old
 
A         www/index.php.old
D         www/index.php
  • La modification sera propager lors du prochain commit.
Nous aurions aussi pu utiliser une suppression du fichier www/index.php, puis la création et l'ajout du fichier www/index.php.old. Toutefois cette méthode comporte un gros inconvénient, car comme nous avons deux fichiers distinct dans ce cas, nous allons perdre l'historique du fichier www/index.php. La commande 'svn mv' est donc préférable.
# svn ci -m "propagation des déplacements de fichiers" 
 
Deleting       www/index.php
Adding         www/index.php.old
 
Committed revision 5.

2.12 Suppression de Fichier

Nous venons de voir qu'il ne faut pas déplacer un fichier qui a déjà été commité sans en informer subversion. Il en va de même pour une suppression de fichier. Un fichier déjà versionner ne doit pas être supprimer avec les commandes de base du système, car lors d'un prochain commit, le dépôt subversion se rendra compte qu'il manque un fichier.

  • Supprimons le fichier www/index.php
# rm www/index.php.old
# svn status
 
!       www/index.php.old

Nous pouvons voir que le fichier a un status '!' indiquant que le client subversion ne retrouve pas se fichier. L'exécution d'un commit pourrait créer un conflit. Nous pouvons heureusement récupérer le fichier dans la dernière version hébergée par le dépôt.

# svn revert www/index.php.old
 
Reverted 'www/index.php.old'

Nous avons récupéré notre fichier.

  • Copions maintenant notre fichier www/index.php.old en www/index.php
# svn cp www/index.php.old www/index.php
 
A         www/index.php
  • Supprimons proprement le fichier www/index.php.old
# svn del www/index.php.old
  • Le status de notre fichier supprimé est bien 'D' et sera pris en compte par SVN
# svn status
 
A  +    www/index.php
D       www/index.php.old
# svn ci -m "suppression d'un fichier"
 
Adding         www/index.php
Deleting       www/index.php.old
 
Committed revision 6.

2.13 Branche de développement

Notre application nécessite encore des développements, notamment l'ajout d'une fonctionnalité de création de personnes. Etant une fonctionnalité à part entière, nous allons la développer sur une branche de développement.

  • Créer tout d'abord une nouvelle branche nommée 'create-person'
# svn copy https://subversion.renater.fr/form-svn-XXX/trunk https://subversion.renater.fr/form-svn-XXX/branches/create-person -m "Creation de la branche create-person"
 
Committed revision 7.
  • La nouvelle branche est créée sur le dépôt, nous allons pouvoir la récupérer
# cd /opt/form-svn-XXX/branches
# svn update .
 
Updating '.':
A    create-person
A    create-person/apache-conf
A    create-person/www
A    create-person/www/index.php
A    create-person/db
A    create-person/common
A    create-person/common/person.class.php
Updated to revision 7.
  • Créons maintenant le formulaire permettant de créer une personne
# touch create-person/www/create.php
# emacs create-person/www/create.php
create-person/www/create.php
<html>
<head></head>
<body>
 
<?php
require_once '../common/person.class.php';
 
if (isset($_REQUEST) && $_REQUEST['submit']) {
 
        $person = new Person();
        $person->address = $_REQUEST['address'];
        $person->city = $_REQUEST['city'];
        $person->lastname = $_REQUEST['lastname'];
        $person->firstname = $_REQUEST['firstname'];
        $person->create();
 
        echo "Nouvelle personne cr&eacute;&eacute;e";
 
}
?>
 
<H3>Nouvelle personne</H3>
 
<form action="create.php" method="post">
<p>
Nom de famille :<br/>
<input size="40" maxlength="40" type="text" name="lastname" value=""/>
</p>
<p>
Pr&eacute;nom :<br/>
<input size="40" maxlength="40" type="text" name="firstname" value=""/>
</p>
<p>
Adresse :<br/>
<input size="40" maxlength="40" type="text" name="address" value=""/>
</p>
<p>
Ville :<br/>
<input size="40" maxlength="40" type="text" name="city" value=""/>
</p>
<p>
<input type="submit" name="submit" value="Valider" />
</p>
</form>
 
<a href="index.php">Accueil</a>
</body>
  • Notre formulaire est prêt, éditons le fichier (toujours au niveau de la branche create-peron) de la page d'accueil afin de créer un lien vers notre nouvelle page :
create-person/www/index.php
<html>
<head></head>
<body>
<H1>Application gestion de personnes</H1>
<ul>
<li><a href="create.php">Ajouter une nouvelle personne</a></li>
</ul>
</body>
  • Modifions le fichier gérant les objets “person” afin d'ajouter une fonction de création :
# emacs create-person/common/person.class.php
create-person/common/person.class.php
<?php
require_once '../db/database.php';
 
/**
* Class Person
*
*/
class Person {
 
        var $id;
        var $firstname;
        var $lastname;
        var $address;
        var $city;
 
        function __construct() {
        }
 
        /**
        * create a new person
        *
        */
        function create() {
                $res = query_update_execute("INSERT INTO person (lastname, firstname, address, city) VALUES ('$this->lastname', '$this->firstname', '$this->address', '$this->city')");
        }
 }  
?>        
  • Créons le fichier pour interagir avec la base de données
# touch create-person/db/database.php
# emacs create-person/db/database.php
create-person/db/database.php
<?php
 
global $connection;
 
/**
 *  db_connect() -  Connect to the database 
 */
function db_connect() {
 
        global $connection;
 
        if ($connection != null) {
                return $connection;
        }
 
        try {
                $connection = mysql_connect('localhost', 'user', 'password');
                return $connection;
        } catch ( Exception $e ) {
                error_log($e->getMessage());
                return null;
        }
}
 
 
function query_select_execute($query) {
        $connection = db_connect();
        $results = array();
        mysql_select_db('db', $connection);
        $rs = mysql_query($query, $connection);
 
        while ($row = mysql_fetch_assoc($rs)) {
                $results[] = $row; 
        }
 
        close_connection();
        return $results;
}
 
function query_update_execute($query) {
        $connection = db_connect();
        $results = array();
        mysql_select_db('db', $connection);
        $rs = mysql_query($query, $connection);
        error_log("result :".$rs);
        error_log("erreur :".mysql_error($connection));
 
        close_connection();
}
 
 
function close_connection() {
        global $connection;
        return mysql_close($connection);
}
 
?>
  • Créons pour finir la base de données qui stockera nos personnes
# service mysqld start
# mysql -u root
  • Une fois connecté à MySQL, créons le schéma de la base :
CREATE SCHEMA db CHARACTER SET 'UTF8';
  • créons maintenant un utilisateur afin que notre application puisse s'y connecter :
CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';
GRANT ALL ON db.* TO 'user'@'localhost';
USE db;
  • Créons maintenant la table pour stocker les personnes :
CREATE TABLE person
(
        id int NOT NULL AUTO_INCREMENT,
        lastname varchar(255),
        firstname varchar(255),
        address varchar(255),
        city varchar(255),
        PRIMARY KEY (id)
);
  • Sortir de MySQL
exit
  • Créons la configuration pour le serveur Apache
# touch /etc/httpd/conf.d/form-svn-XXX.conf
# emacs /etc/httpd/conf.d/form-svn-XXX.conf
/etc/httpd/conf.d/form-svn-XXX.conf
<VirtualHost *:80>
DocumentRoot /opt/form-svn-XXX/trunk/www
 
<Directory "/opt/form-svn-XXX/trunk/www">
       Options Indexes FollowSymLinks IncludesNOEXEC
       AllowOverride All
       Order allow,deny
       Allow from all
       ErrorDocument 404 /404.php
</Directory>
 
Alias /create-person /opt/form-svn-XXX/branches/create-person/www
<Directory "/opt/form-svn-XXX/branches/create-person/www">
       Options Indexes FollowSymLinks IncludesNOEXEC
       AllowOverride All
       Order allow,deny
       Allow from all
       ErrorDocument 404 /404.php
</Directory>
 
Alias /version-1.0 /opt/form-svn-XXX/branches/version-1.0/www
<Directory "/opt/form-svn-XXX/branches/version-1.0/www">
       Options Indexes FollowSymLinks IncludesNOEXEC
       AllowOverride All
       Order allow,deny
       Allow from all
       ErrorDocument 404 /404.php
</Directory>
</VirtualHost>
  • Informons le serveur Apache de redémarrer
# service httpd restart

2.14 Voir le détail des modifications effectuées sur la copie de travail

  • Notre évolution est terminée, voyons l'ensemble des modifications que nous avons effectuées grâce à la commande “svn diff”. Cette commande affichera les différences entre le dossier/fichier de la copie locale avec le même dossier/fichier du dépôt distant.
# cd /opt/form-svn-XXX/branches/create-person
# svn diff www/index.php
 
Index: www/index.php
===================================================================
--- www/index.php	(revision 7)
+++ www/index.php	(working copy)
@@ -0,0 +1,8 @@
+<html>
+<head></head>
+<body>
+<H1>Application gestion de personnes</H1>
+<ul>
+<li><a href="create.php">Ajouter une nouvelle personne</a></li>
+</ul>
+</body>
\ No newline at end of file
  • Marquons nos nouveaux fichiers “pour ajout” afin de pouvoir transférer lors du prochain commit
# svn add www/create.php db/database.php
 
A         www/create.php
A         db/database.php
Il est possible aussi d'afficher toutes les modifications effectuées sur la branches :
# svn diff .

Mais dans le cas où un grand nombre de fichiers sont modifiés, le résultat est peu lisible, il est préférable de restreindre la portée de cette commande.

  • Nous pouvons propager les modifications sur notre branche
# svn commit -m "ajout de creation de personnes"
 
Sending        create-person/common/person.class.php
Adding         create-person/db/database.php
Adding         create-person/www/create.php
Sending        create-person/www/index.php
Transmitting file data ....
Committed revision 8.

2.15 Fusionner une branche vers le trunk

Notre évolution est fonctionnelle et terminée, nous pouvons donc reverser les modifications vers le dépôt central. Pour cela, nous allons fusionner la branche create-person avec le trunk. Le trunk contiendra par la suite toutes les modifications présentes sur la branche. A la suite de cette commande, une nouvelle révision sera créée, et la branche sera détruite car plus utile.

  • Tout d'abord plaçons nous dans le dossier trunk, puis vérifions qu'il est bien à jour
# cd /opt/form-svn-XXX/trunk
# svn update .
 
At revision 8.
  • Regardons dans un premier temps où en sont nos branches :
# svn mergeinfo ../branches/create-person .
    youngest common ancestor
    |         last full merge
    |         |        tip of branch
    |         |        |         repository path
 
    6                  8       
    |                  |       
       --| |------------         branches/create-person
      /                        
     /                         
  -------| |------------         trunk
                       |       
                       8       

Cette commande nous donne des informations sur l'état des branches et du trunk. On peut voir que la création de la branche create-person à partir du trunk à la révision 6, et que ces deux branches sont maintenant à la dernière révision.

  • Lançons la fusion des branches
# svn merge --reintegrate https://subversion.renater.fr/form-svn-XXX/branches/create-person
 
--- Merging differences between repository URLs into '.':
A    www/create.php
U    www/index.php
A    db/database.php
U    common/person.class.php
--- Recording mergeinfo for merge between repository URLs into '.':
 U   .
  • Les modifications ont été récupérées sur la copie de travail locale, dans le dossier trunk. Un “svn status” nous informe que les modifications sont présentes et peuvent être commitées.
# svn status
 
 M      .
A  +    www/create.php
M       www/index.php
A  +    db/database.php
M       common/person.class.php
  • Propageons ces modifications sur le dépôt central
# svn ci -m "integration de la branche create-person au trunk"
 
Sending        trunk
Sending        trunk/common/person.class.php
Adding         trunk/db/database.php
Adding         trunk/www/create.php
Sending        trunk/www/index.php
Transmitting file data ..
Committed revision 9.
La fusion d'une branche avec le trunk peut provoquer des conflits, qui peuvent être résolus de la même manière qu'un conflit lors d'un commit.

2.16 Rechargement d'une ancienne révision

Après la modification d'un fichier, si on se rend compte que la modification doit être annulée, il est possible de voir la différence entre le contenu du fichier local et celui du dépôt, mais il est plus simple de récupérer la version présente sur le dépôt.

  • Editons le fichier de la page d'accueil afin d'apporter une modification
# cd /opt/form-svn-XXX/trunk
# emacs www/index.php
www/index.php
<html>
<head></head>
<body>
<H1>Application gestion de personnes</H1>
<ul>
<li><a href="create.php">Ajouter une nouvelle personne</a></li>
**<li><a href="del.php">Supprimer une personne</a></li>**
</ul>
</body>
  • Le status du fichier passe à 'M'
# svn status
 
M       www/index.php
  • La modification est propagée
# svn ci -m "modification du fichier index.php"
 
Sending        www/index.php
Transmitting file data .
Committed revision 10.
  • Au final, la fonctionnalité de suppression n'est plus jugée utile pour l'instant, il faudrait faire revenir le fichier à la révision précédente, car les modifications qui ont été commitées ne sont plus nécessaires. Pour cela, nous allons utiliser la commande “svn merge” en lui précisant les révisions cibles et actuelles :
# svn merge -r 10:9 www/index.php
 
--- Reverse-merging r10 into 'www/index.php':
U    www/index.php
--- Recording mergeinfo for reverse merge of r10 into 'www/index.php':
 G   www/index.php
--- Eliding mergeinfo from 'www/index.php':
 U   www/index.php
  • Le fichier www/index.php est revenu à son état avant changement, son status est à 'non modifié'.
# svn status
Si le fichier www/index.php avait été modifié mais non commité, la commande 'svn revert' aurait suffit pour le remettre dans son état avant modifications locales.

2.17 Utilisation des Alias de révision

Comme nous avons pu le voir au long de ce TP, chaque révision est identifiée par un numéro (identifiant) qui est incrémenté à chaque commit effectué (ou création de branche/tags). Toutefois il n'est pas toujours évident d'utiliser des numéros pour manipuler les révisions. C'est pourquoi il existe des alias permettant d'exécuter les cas courants des commandes svn :

  • HEAD: identifie la révision la plus récente du dépôt
  • BASE: révision locale avant modification, telle qu’elle a été extraite du dépôt
  • COMMITTED: dernière révision pour laquelle une modification a été commitée
  • PREV: correspond à la révision précédente de COMMITTED
  • Utilisons ces Alias dans quelques commandes exemple
  • Tout d'abord nous allons nous placer dans la 2e copie de travail locale
# cd /opt/form-svn-XXX_copie2/trunk
  • Une commande pratique permet de voir les différences entre la révision locale et la dernière révision du dépôt
# svn diff -r BASE:HEAD 
 
Index: common/person.class.php
===================================================================
--- common/person.class.php	(working copy)
+++ common/person.class.php	(revision 10)
@@ -1,16 +1,29 @@
 
...
...
 
Index: www/create.php
===================================================================
--- www/create.php	(revision 0)
+++ www/create.php	(revision 10)
@@ -0,0 +1,46 @@
 
...
...
 
Index: www/index.php
===================================================================
--- www/index.php	(working copy)
+++ www/index.php	(revision 10)
@@ -0,0 +1 @@
 
...
 
Index: db/database.php
===================================================================
--- db/database.php	(revision 0)
+++ db/database.php	(revision 10)
@@ -0,0 +1,57 @@
 
...
...
 
Index: .
===================================================================
--- .	(working copy)
+++ .	(revision 10)
 
Property changes on: .
___________________________________________________________________
Added: svn:mergeinfo
   Merged /branches/create-person:r7-8
A la fin du diff est notifié que la propriété SVN mergeinfo a été modifié sur le trunk, et indique qu'il y a eu un merge de la branche create-person en révision 7, qui a engendré la création de la révision 8.

2.18 Branches de version

Maintenant que notre application est prête, nous allons créer une branche de version, permettant de garder le code à déployer. Une branche de version a une durée de vie longue, elle durera le temps que la version est maintenue par l'équipe de développement. Ce genre de branche sert à recevoir des correctifs pour la version à livrer.

# svn copy https://subversion.renater.fr/form-svn-XXX/trunk https://subversion.renater.fr/form-svn-XXX/branches/version-1.0 -m "Creation de la branche version-1.0"
 
Committed revision 11.

Maintenant que notre branche a été créée, la version 1.0 de notre application existera indépendamment de la version la plus récente (trunk). Des commits pourront être faits sur cette branche afin de corriger des bogues, reporter des légères fonctionnalités.

# cd /opt/form-svn-XXX/branches
# svn update .
 
Updating '.':
A    version-1.0
A    version-1.0/apache-conf
A    version-1.0/www
A    version-1.0/www/create.php
A    version-1.0/www/index.php
A    version-1.0/db
A    version-1.0/db/database.php
A    version-1.0/common
A    version-1.0/common/person.class.php
Updated to revision 11.

2.19 Création d'un Tag

Lorsque le code de la branche est prêt à être livré, il faut créer un tag. Un tag se crée de la même manière qu'une branche, c'est une étiquette qui pointe vers une certaine révision. La différence principale entre une branche de version et un tag, est qu'aucun commit ne doit être effectué sur un tag. Le tag est seulement un pointeur, utilisé pour faire et refaire facilement un déploiement.

# svn copy https://subversion.renater.fr/form-svn-XXX/branches/version-1.0 https://subversion.renater.fr/form-svn-XXX/tags/version-1.0 -m "Creation du tag version-1.0"
 
Committed revision 12.

Vous pouvez tester de récupérer cette version du dépôt, en faisant un checkout de son URL.

2.20 Propriétés

Les propriétés sont des attributs attachés à un ou plusieurs fichiers du dépôt et qui permettent des comportements particuliers.

2.20.1 Keywords

Subversion propose une fonctionnalité intéressante, le remplacement de certains mots clés dans un fichier par des informations issue du versionnement de celui-ci. Subversion défini plusieurs mots clés :

  • Date (= LastChangedDate): décris la dernière date à laquelle le fichier a été commité sur le dépôt. La date insérée dans le fichier est au format : 2016-02-24 11:12:52 +0100 (Wed, 24 Feb 2016), utilisant la timezone locale
  • Revision (= LastChangedRevision) : Affiche la dernière révision du fichier sur le dépôt
  • Author (=LastChangedBy): Décris le dernier utilisateur ayant commité le fichier sur le dépôt
  • HeadURL (=URL): affiche l'URL complète de la dernière version du fichier sur le dépôt
  • Id : synthèse de certains mots clés au format : Nom du fichier révision date auteur. Exemple : $Id: fichier 6 2016-02-24 09:47:19Z sebastien
  • Header : identique au mot clé Id mais affiche l'URL complète du fichier sur le dépôt à la place du nom fichier.

Un exemple, mettre à jour la date de dernière modification d'un document requiert aux utilisateurs de rigoureusement changer la date dans le document à chaque édition du fichier. Déléguer cette tâche à Subversion permet de s'assurer que celle-ci ne sera pas oubliée.

  • Editons le fichier www/index.php pour y placer des mots clés :
# cd /opt/form-svn-XXX/trunk
# emacs www/index.php
www/index.php
<?php
/*
* Auteur de la dernière modification : $Author$
*
* Date de dernière modification : $Date$
*/
?>
 
<html>
<head></head>
<body>
<H1>Application gestion de personnes</H1>
<ul>
<li><a href="create.php">Ajouter une nouvelle personne</a></li>
</ul>
</body>
  • Placer la propriété sur le fichier
# svn propset svn:keywords "Date" www/index.php 
  • Propager la modification sur le dépôt
# svn ci -m "ajout de la propriété 'date'" www/index.php
 
Sending        www/index.php
Transmitting file data .
Committed revision 13.
  • Voir le résultat sur le fichier
# more www/index.php
 
<?php
/*
* Auteur de la dernière modification : $Author$
*
* Date de dernière modification : $Date: 2016-02-24 11:42:00 +0100 (Wed, 24 Feb 2016) $
*/
?>
 
<html>
<head></head>
<body>
<H1>Application gestion de personnes</H1>
<ul>
<li><a href="create.php">Ajouter une nouvelle personne</a></li>
</ul>
</body>

Il est important de noter que si on souhaite placer plusieurs mots clés sur un fichier, il faut tous les préciser dans une seule commande svn propset, et ne pas lancer plusieurs commandes à la suite, car chaque commande écrasera la propriété précédente.

# svn propset svn:keywords "Date Author" www/index.php
 
property 'svn:keywords' set on 'www/index.php'
# svn ci -m "ajout de la propriété 'date'" www/index.php
Sending        www/index.php
 
Committed revision 14.
  • regardons le résultat
# more www/index.php
www/index.php
<?php
/*
 * Auteur de la dernière modification : $Author: smedard $
 *
 * Date de dernière modification : $Date: 2016-03-11 14:47:43 +0000 (Fri, 11 Mar 2016) $
 */
?>
 
<html>
<head></head>
<body>
<H1>Application gestion de personnes</H1>
<ul>
<li><a href="create.php">Ajouter une nouvelle personne</a></li>
</ul>
</body>

2.20.2 Ignore

La propriété svn:ignore permet de retirer explicitement certains fichiers du contrôle de version. Par exemple, si votre IDE génère des fichiers de configurer pour son propre fonctionnement (le .classpath de Eclipse), il peut être intéressant de systématiquement mettre de côté ces fichiers en positionnant la propriété correspondante.

  • Créons un fichier que nous allons demander à SVN d'ignorer :
# cd /opt/form-svn-XXX/trunk/
# touch .tmp
  • Positionnons la propriété “ignore” sur tout le dossier 'trunk' :
# svn propset svn:ignore *.tmp .
 
property 'svn:ignore' set on '.'
  • Tous les fichiers .tmp seront maintenant ignorés par SVN lors de l'exécution des commandes “svn add”.
  • L'exécution de la commande “svn status” sur un fichier ignoré retourne 'I' prouvant bien qu'il n'est pas pris en compte par Subversion
# svn status .tmp
 
I
  • Nous désirons maintenant aussi ignorer tous les fichiers '.bak' que certains éditeurs peuvent générer. Nous allons modifier la propriété en précisant les deux patrons à ignorer.
Cette commande est un peu particulière car elle requiert d'ouvrir des guillemets, entrer le premier patron, entrer un retour chariot, indiquer le deuxième patron, puis fermer les guillemets
# touch .bak
# svn propset svn:ignore "*.bak
*.tmp" .
 
property 'svn:ignore' set on '.'
# svn status .bak .tmp
 
I       .tmp
I       .bak
Il est aussi possible si beaucoup de patron sont nécessaires, de créer un fichier les regroupant, un patron par ligne, et d'indiquer ce fichier en paramètre de la commande propset svn:ignore :
# svn propset svn:ignore -F fichier_contenant_les_patrons

2.21 Hook

Le gestionnaire de source Subversion offre une fonctionnalité intéressante appelée « Hooks ».

Un hook (crochet) permet de lancer un programme personnalisé au moment où le programme principal à la tâche de l’exécuter. Pour SVN les hooks sont applicables sur les évènements de contrôle de version (commit, lock).

Deux types de 'Hooks' sont possibles :

  • pre hooks exécutés en amont de l’opération choisie. Ils permettent de prévenir d’un évènement (exemple : on souhaite rejeter les commits qui ont un commentaire vide).
  • post hooks qui vont être lancés en aval d’une opération. Le 'post hooks' est souvent utilisé à des fins statistiques ou pour envoyer un mail.

Par défaut, un dépôt n'a pas de hook positionné. La forge propose toutefois quelques hooks à ajouter. Pour cela :

  • Accéder à la page de votre projet SourceSup
  • Cliquer sur l'onglet “Sources”
  • Cliquer sur le sous onglet “Administration”
  • Dans la partie “Activer des hooks” puis sous partie “Pre-commit hook”, cocher la case “Check log”
  • La mise en place du hook peut prendre jusqu'à 30 minutes
  • Modifier le fichier “common.person.class.php” et ajouter juste un retour chariot en fin de fichier.
# cd /opt/form-svn-XXX/trunk
# emacs common/person.class.php
  • tenter de propager cette modification sans mettre de commentaire :
# svn ci -m '' common/person.php.class
 
Transmitting file data .svn: E165001: Commit failed (details follow):
svn: E165001: Commit blocked by pre-commit hook (exit code 1) with output:
---------PRE COMMIT HOOK---------
 
Every commit must have a log.
 
---------------------------------

La propagation a été refusée car le message du log est vide. Au vue de l'importance des commentaires associés aux commits, il est intéressant d'activer ce hook.

  • annuler la dernière modification
# svn revert common/person.class.php

2.22 Vue web des dépôts : ViewVC

Depuis la forge, il est possible d'avoir une vue de son dépôt SVN, d'accéder aux différentes révisions, de voir qui a effectué un commit, etc.

  • Pour accéder à cette vue web, rendez-vous sur la page de votre projet SourceSup
  • Cliquer sur l'onglet “Sources”
  • Cliquer sur le lien “Parcourir le dépôt Subversion”

Cette vue affiche l'arborescence de la dernière version du dépôt. Il est possible de naviguer entre les anciennes révisions du dépôt. Pour cela il faut indiquer quelle révision afficher dans le champ “Sticky Revision” et valider avec le bouton “Set”. Il est alors possible de descendre dans les différents dossiers du dépôt dans la révision paramétrée.

Un clic sur le bouton “Clear” permet de revenir à la dernière révision du dépôt directement.

ViewVC offre aussi la possibilité d'afficher l'historique d'un fichier avec les informations principales, comme les dernières révisions avec leur auteur, leur date de création, le commentaire. Pour cela, naviguer dans l'arborescence jusqu'au fichier voulu, puis cliquer dessus.

  • Exemple pour le fichier common/person.class.php
Revision 4 - (view) (download) (annotate) - [select for diffs] 
Modified Wed Mar 9 21:37:32 2016 UTC (38 minutes, 7 seconds ago) by smedard 
File length: 169 byte(s) 
Diff to previous 3
ajout de la nouvelle classe person

Sur cet écran, ViewVC offre la possibilité de faire le différentiel d'un fichier entre deux révisions, pas forcément successives. Sélectionner la version que vous souhaitez comparer avec par défaut la dernière, il est aussi possible de comparer deux anciennes versions.

  • Sélectionner la révision antérieure pour faire un différentiel
  • Valider le formulaire
  • Les différences entre les fichiers des deux versions apparaissent en couleur :

2.23 Statistiques du dépôt

Tous les dépôts hébergés sur la forge SourceSup sont analysés chaque soir afin de publier sur l'interface web du projet des statistiques concernant le code et les développeurs.

  • Pour voir ces informations, rendez-vous sur la page de votre projet SourceSup
  • Cliquer sur l'onglet “Sources”
  • Le widget “historique” sur la gauche de l'écran rappelle le nombre de commits et d'ajouts fait par chaque développeurs sur le dépôt associé au projet.
  • Cliquer sur le sous onglet “Rapport”
  • L'écran affiché propose plusieurs diagrammes montrant le nombre de commits par développeurs, qui a effectué les commits des 3 derniers mois, etc.

Ces informations peuvent être intéressantes pour les utilisateurs s'occupant de la gestion du projet. Les statistiques étant générées dans la nuit pour chaque dépôt, il sera possible de voir celles du projet form-svn-XXX le lendemain.

Exemple de statistiques avec le projet “Sympa” :

2.24 Annexe

Status d'un fichier

Liste des status le plus souvent vus pour un fichier dans l'environnement de Subversion :

  • ' ' Pas de modification.
  • 'A' Sera propagé lors du prochain commit.
  • 'D' Sera supprimé lors du prochain commit.
  • 'M' Élément modifié.
  • 'C' Élément en conflit.
  • '?' Élément non encore géré dans le suivi de versions.
  • '!' Élément manquant (par exemple si vous l'avez déplacé ou effacé sans utiliser la commande svn). Cela indique également qu'un répertoire n'est pas complet (une extraction ou une mise à jour a été interrompue).

Alias de commandes

  • svn co = svn checkout
  • svn ci = svn commit