Verrouillage

Le modèle copier-modifier-fusionner de gestion de versions de Subversion repose sur ses algorithmes de fusion, notamment sur la manière dont ils gèrent les conflits quand de multiples collaborateurs modifient le même fichier simultanément. Subversion lui-même ne propose qu'un seul algorithme de ce type, un algorithme qui détecte les modifications par trois méthodes et qui est suffisamment intelligent pour gérer les données à la ligne près. Subversion vous permet également d'utiliser en plus des outils externes lors du processus de fusion (comme indiqué dans la section intitulée « Programme externe de comparaison de trois fichiers »), parfois encore meilleurs que ceux inclus dans Subversion, proposant par exemple une granularité plus fine allant jusqu'au mot, voire au caractère, au lieu de s'arrêter à la ligne. Mais, en règle générale, ces algorithmes ne fonctionnent que sur des fichiers texte. Le paysage est beaucoup plus sombre lorsque l'on recherche des outils de fusion pour des formats de fichiers non-texte. Et quand vous ne trouvez pas d'outil capable de fusionner de tels fichiers, les limites du modèle copier-modifier-fusionner se font vite sentir.

Prenons un exemple de la vie réelle où ce type de problème apparaît. Harry et Sally sont deux graphistes travaillant sur le même projet (du marketing pour le patron d'un garage). Au cœur d'une affiche de ce projet se trouve l'image d'une voiture dont la carrosserie a besoin d'être réparée, stockée dans un fichier image au format PNG. L'agencement de l'affiche est pratiquement terminé, et Harry et Sally sont contents de la photo qu'ils ont choisie pour leur voiture endommagée : une Ford Mustang bleue de 1967, avec un gnon sur l'aile avant gauche.

C'est alors, comme c'est souvent le cas dans le domaine du graphisme, que des contraintes extérieures imposent de changer la couleur de la voiture. Sally met donc à jour sa copie de travail à la révision HEAD, lance son outil d'édition de photos et commence à modifier la photo de manière à obtenir une voiture rouge cerise. Pendant ce temps, Harry, particulièrement inspiré ce jour-là, décide que l'image serait plus percutante si la voiture était davantage endommagée. Lui aussi met à jour sa copie de travail à la révision HEAD, puis dessine des fissures sur le pare-brise. Il termine son travail avant que Sally ne termine le sien, admire son chef-d'œuvre et propage les changements. Peu après, Sally en termine avec la nouvelle couleur de la voiture et essaie de propager ses modifications. Mais, comme prévu, Subversion ne parvient pas à valider la propagation et informe Sally que sa version de l'image n'est pas à jour.

Voilà où résident les difficultés : si Harry et Sally avaient effectué leurs changements sur un fichier texte, Sally aurait simplement mis à jour sa copie de travail, recevant au passage les modifications de Harry. Dans le pire des cas, ils auraient modifié la même portion du fichier et Sally aurait eu à résoudre les conflits manuellement. Mais, ici, nous avons affaire à des images binaires, pas des fichiers texte. Et s'il est relativement facile de décrire ce que devrait être l'image finale, il y a très peu de chances qu'un logiciel soit suffisamment intelligent pour détecter les parties communes de l'image sur laquelle les artistes ont travaillé, les changements effectués par Harry et les changements effectués par Sally et pour en tirer une image d'une Mustang rouge avec un pare-brise fissuré !

Clairement, les choses se seraient mieux passées si Harry et Sally avaient sérialisé leurs modifications : par exemple, si Harry avait attendu et dessiné ses fissures sur la voiture nouvellement rouge de Sally, ou si Sally avait changé la couleur d'une voiture avec un pare-brise déjà fissuré. Comme indiqué dans la section intitulée « Modèle copier-modifier-fusionner », la plupart de ces problèmes disparaissent complètement quand une communication parfaite existe entre Harry et Sally [15]. Mais comme un système de gestion de versions est en fait un mode de communication, il s'ensuit que si ce type de logiciel facilite la sérialisation de tâches d'édition non parallélisables, c'est plutôt une bonne chose. C'est ici que l'implémentation du concept verrouiller-modifier-libérer dans Subversion prend tout son sens. Il est temps de parler de la fonctionnalité de verrouillage de Subversion, qui est similaire aux mécanismes permettant de « réserver pour modifications » des fichiers dans d'autres systèmes de gestion de versions.

En fin de compte, la fonctionnalité de verrouillage existe afin de minimiser les pertes de temps et les efforts. En autorisant un utilisateur à s'arroger logiciellement le droit exclusif de modifier un fichier dans le dépôt, cet utilisateur peut être suffisamment confiant dans le fait que son travail ne sera pas vain — la propagation de ses changements réussira. Aussi, en signifiant aux autres utilisateurs qu'une sérialisation a lieu pour un objet suivi en versions, ces utilisateurs peuvent raisonnablement s'attendre à ce que cet objet soit modifié par quelqu'un d'autre. Eux aussi peuvent alors éviter de perdre leur temps et leur énergie sur des modifications qui ne pourront pas être fusionnées en raison d'un problème de mise à jour du fichier correspondant.

La fonctionnalité de verrouillage de Subversion comporte en fait plusieurs facettes, qui permettent entre autres de verrouiller un fichier suivi en versions [16] (demander le droit exclusif de modification sur le fichier), de le déverrouiller (abandonner le droit exclusif de modification), de voir la liste des fichiers qui sont verrouillés et par qui, d'annoter des fichiers pour lesquels le verrouillage est fortement recommandé avant édition, etc. Dans cette section, nous abordons toutes les facettes de cette fonctionnalité de verrouillage.

Création d'un verrou

Dans un dépôt Subversion, un verrou est une méta-donnée qui alloue à un utilisateur un accès exclusif en écriture sur un fichier. Cet utilisateur est appelé détenteur du verrou. Chaque verrou possède également un identifiant unique, en général une longue chaîne de caractères, appelé jeton de verrouillage. Le dépôt gère les verrous en assurant in fine leur création, leur application et leur suppression. Si une propagation tente de modifier ou effacer un fichier verrouillé (ou effacer un répertoire parent dudit fichier), le dépôt demande deux informations : que le client effectuant la propagation s'authentifie en tant que détenteur du verrou et que le jeton de verrouillage soit fourni lors de la procédure de propagation afin de montrer que le client sait bien quel verrou il utilise.

Pour illustrer la création d'un verrou, reprenons notre exemple de graphistes travaillant sur les même fichiers image binaires. Harry a décidé de changer cette image JPEG. Pour interdire aux autres collaborateurs d'effectuer des changements sur le fichier pendant qu'il le modifie (et pour les avertir qu'il va modifier ce fichier), il verrouille le fichier dans le dépôt en utilisant la commande svn lock :

$ svn lock banane.jpg -m "Édition du fichier pour la livraison de demain."
'banane.jpg' verrouillé par l'utilisateur 'harry'.
$

Il y a plusieurs points intéressants dans l'exemple ci-dessus : d'abord, notez que Harry utilise l'option --message (-m) de svn lock. Comme svn commit, la commande svn lock accepte des commentaires (soit via --message (-m), soit via --file (-F)) pour indiquer la raison du verrouillage du fichier. En revanche, contrairement à svn commit, svn lock n'exige pas automatiquement un message en lançant votre éditeur de texte préféré. Les commentaires de verrouillage sont optionnels, mais néanmoins recommandés pour faciliter la communication entre collaborateurs.

Ensuite, la tentative de verrouillage a réussi. Cela signifie que le fichier n'était pas préalablement verrouillé et que Harry disposait de la dernière version du fichier. Si la copie de travail de Harry avait été obsolète, le dépôt aurait refusé la demande, forçant Harry à effectuer une mise à jour (svn update) et à relancer ensuite la commande de verrouillage. La commande de verrouillage aurait également échoué si le fichier avait déjà été verrouillé par quelqu'un d'autre.

Comme vous pouvez le constater, la commande svn lock affiche la confirmation que le verrouillage a réussi. Dès lors, le verrouillage du fichier apparaît dans le résultat des commandes svn status et svn info :

$ svn status
     K banane.jpg

$ svn info banane.jpg
Chemin : banane.jpg
Nom : banane.jpg
URL : http://svn.exemple.com/depot/projet/banane.jpg
Racine du dépôt : http://svn.exemple.com/depot
UUID du dépôt : edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Revision: 2198
Type de nœud : file
Tâche programmée : normale
Auteur de la dernière modification : frank
Révision de la dernière modification : 1950
Date de la dernière modification : 2006-03-15 12:43:04 -0600 (mer. 15 mars 2006)
Texte mis à jour : 2006-06-08 19:23:07 -0500 (jeu. 08 juin 2006)
Propriétés mis à jour : 2006-06-08 19:23:07 -0500 (jeu. 08 juin 2006)
Somme de contrôle: 3b110d3b10638f5d1f4fe0f436a5a2a5
Nom de verrou : opaquelocktoken:0c0f600b-88f9-0310-9e48-355b44d4a58e
Propriétaire du verrou : harry
Verrou créé : 2006-06-14 17:20:31 -0500 (mer. 14 juin 2006)
Commentaire du verrou (1 ligne):
Édition du fichier pour la livraison de demain.

$

Le fait que la commande svn info, qui ne contacte pas le dépôt quand elle porte sur un chemin d'une copie de travail, affiche bien le jeton de verrouillage, révèle une caractéristique importante des jetons de verrouillage : ils sont intégrés dans la copie de travail. La présence du jeton de verrouillage est primordiale. Il authorise la copie de travail à utiliser le verrou ultérieurement. Par ailleurs, la commande svn status affiche un K (raccourci pour « locKed »« verrouillé » en anglais) avant le nom du fichier, indiquant que le jeton de verrouillage est présent.

Maintenant que Harry a verrouillé banane.jpg, Sally ne peut ni modifier ni effacer ce fichier :

$ svn delete banane.jpg
D         banane.jpg
$ svn commit -m "Suppression des fichiers inutiles."
Suppression    banane.jpg
svn: Échec de la propagation (commit), (détails):
svn: Suppression de
'/depot/projet/!svn/wrk/64bad3a9-96f9-0310-818a-df4224ddc35d/banane.jpg':
423 Verrouillé (http://svn.exemple.com)
$

Mais Harry, après avoir fait ses retouches sur sa belle banane jaune, peut propager ses changements sur le fichier. C'est parce qu'il s'authentifie en tant que détenteur du verrou et aussi parce que sa copie de travail possède le bon jeton de verrouillage :

$ svn status
M    K banane.jpg
$ svn commit -m "Rendu la banane plus jaune."
Envoi        banane.jpg
Transmission des données .
Révision 2201 propagée.
$ svn status
$

Notez qu'après que la propagation est terminée, svn status permet de voir que le jeton de verrouillage n'est plus présent dans la copie de travail. C'est le comportement normal de svn commit : elle recherche dans la copie de travail (ou dans une liste de cibles, si vous fournissez une telle liste) les modifications effectuées localement et elle envoie les jetons de verrouillage qu'elle trouve durant sa recherche au serveur, en tant que partie intégrante du processus de propagation. Après que la propagation a réussi, tous les verrous du dépôt qui ont été mentionnés sont libérés, même ceux pour lesquels les fichiers n'ont pas été propagés. Ce comportement a pour but de dissuader les utilisateurs d'être négligents avec leurs verrous ou de garder des verrous trop longtemps. Si Harry verrouille au hasard trente fichiers dans un répertoire nommé Images parce qu'il n'est pas sûr de savoir quels fichiers il doit modifier et qu'il ne modifie finalement que quatre fichiers, alors quand il lance la commande svn commit Images, la procédure libère les trente verrous.

Ce mode de fonctionnement (libérer automatiquement les verrous) peut être modifié avec l'option --no-unlock de svn commit. C'est utile quand vous voulez propager des changements mais que vous prévoyez d'effectuer des changements supplémentaires et que donc vous avez toujours besoin des verrous. Vous pouvez également en faire le fonctionnement par défaut en réglant l'option no-unlock dans la zone de configuration (voir la section intitulée « Zone de configuration des exécutables »).

Bien sûr, verrouiller un fichier n'oblige pas l'utilisateur à le modifier. Le verrou peut être libéré n'importe quand avec la commande svn unlock :

$ svn unlock banane.c
'banane.c' déverrouillé.

Identification d'un verrou

Quand une propagation échoue parce que quelqu'un d'autre a posé un verrou, il est facile de savoir pourquoi. La commande la plus simple est svn status --show-updates:

$ svn status -u
M              23   truc.c
M    O         32   raisin.jpg
       *       72   machin.h
État par rapport à la révision      105
$

Dans cet exemple, Sally peut voir que non seulement sa copie de travail de machin.h n'est plus à jour, mais aussi qu'un des deux fichiers qu'elle prévoie de propager est verrouillé dans le dépôt. La lettre O (« Others »« autres » en anglais) indique qu'un verrou existe sur ce fichier et qu'il a été créé par quelqu'un d'autre. Si elle essayait de lancer svn commit, le verrou sur raisin.jpg l'en empêcherait. Sally est laissée dans l'expectative de savoir qui a posé le verrou, quand et pourquoi. Là encore, svn info trouve la réponse :

$ svn info http://svn.exemple.com/depot/projet/raisin.jpg
Chemin : raisin.jpg
Nom : raisin.jpg
URL: http://svn.exemple.com/depot/projet/raisin.jpg
UUID du dépôt : edb2f264-5ef2-0310-a47a-87b0ce17a8ec
Révision: 105
Type de nœud : file
Auteur de la dernière modification : sally
Révision de la dernière modification : 32
Texte mis à jour : 2006-01-25 12:43:04 -0600 (Sun, 25 Jan 2006)
Nom de verrou : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire du verrou : harry
Verrou créé : 2006-02-16 13:29:18 -0500 (jeu. 16 févr. 2006)
Commentaire du verrou (1 ligne):
Besoin de faire une retouche rapide sur cette image.
$

De la même manière que svn info peut être utilisée pour examiner les objets de la copie de travail, elle peut être utilisée pour examiner les objets du dépôt. Si l'argument principal de svn info est un chemin de la copie de travail, alors toutes les informations stockées localement sont affichées ; toute mention d'un verrou signifie que la copie de travail détient un jeton de verrouillage (si le fichier est verrouillé par un autre utilisateur ou depuis une autre copie de travail, alors lancer svn info sur la copie de travail ne renvoit aucune information relative au verrou). Si l'argument principal de svn info est une URL, alors les informations affichées se rapportent à la dernière version de l'objet dans le dépôt et toute mention d'un verrou concerne le verrou en cours sur l'objet.

Ainsi, dans notre exemple, Sally peut voir que Harry a verrouillé le fichier le 16 février pour effectuer une « retouche rapide ». Comme nous sommes en juin, elle suspecte qu'il a probablement oublié le verrou. Elle pourrait téléphoner à Harry pour le lui signaler et lui demander de libérer le verrou. S'il n'est pas joignable, elle peut toujours essayer de forcer le verrou elle-même, ou demander à un administrateur de le faire.

Cassage et vol d'un verrou

Un verrou n'est pas quelque chose de sacré : dans la configuration par défaut de Subversion, les verrous peuvent être libérés non seulement par leur détenteur, mais aussi par n'importe qui d'autre. Quand quelqu'un d'autre que le détenteur d'un verrou le libère, nous appelons ça casser le verrou.

Avec un statut d'administrateur, il est facile de casser un verrou. Les programmes svnlook et svnadmin peuvent afficher et casser les verrous directement dans le dépôt (pour plus d'informations sur ces outils, reportez-vous à la section intitulée « Boîte à outils de l'administrateur »).

$ svnadmin lslocks /usr/local/svn/depot
Chemin : /projet2/images/banane.jpg
Chaîne UUID : opaquelocktoken:c32b4d88-e8fb-2310-abb3-153ff1236923
Propriétaire : frank
Créé : 2006-06-15 13:29:18 -0500 (jeu. 15 juin 2006)
Expire :
Commentaire (1 ligne):
J'améliore encore la couleur jaune.

Chemin : /projet/raisin.jpg
Chaîne UUID : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire : harry
Créé : 2006-02-16 13:29:18 -0500 (jeu. 16 fév. 2006)
Expire :
Commentaire (1 ligne):
Besoin de faire une retouche rapide sur cette image.

$ svnadmin rmlocks /usr/local/svn/depot/projet/raisin.jpg
'/projet/raisin.jpg' déverrouillé
$

L'option la plus intéressante est celle qui permet aux utilisateurs de casser les verrous détenus par d'autres personnes à travers le réseau. Pour ce faire, Sally doit simplement ajouter l'option --force à la commande svn unlock :

$ svn status -u
M              23   truc.c
M    O         32   raisin.jpg
       *       72   machin.h
État par rapport à la révision     105
$ svn unlock raisin.jpg
svn: 'raisin.jpg' n'est pas verrouillé dans cette copie de travail
$ svn info raisin.jpg | grep URL
URL: http://svn.exemple.com/depot/projet/raisin.jpg
$ svn unlock http://svn.exemple.com/depot/projet/raisin.jpg
svn: Unlock request failed: 403 Forbidden (http://svn.exemple.com)
$ svn unlock --force http://svn.exemple.com/depot/projet/raisin.jpg
'raisin.jpg' déverrouillé.
$

Ainsi, la tentative initiale de Sally pour libérer le verrou a échoué parce qu'elle a lancé svn unlock directement sur le fichier de sa copie de travail, où aucun jeton de verrouillage n'était présent. Pour casser le verrou directement dans le dépôt, elle doit passer une URL à svn unlock. Son premier essai pour casser le verrou avec l'URL échoue car elle ne peut pas s'authentifier comme détentrice du verrou (et elle n'a pas non plus le jeton de verrouillage). Mais quand elle passe l'option --force, les pré-requis d'authentification et d'autorisation sont ignorés et le verrou est cassé.

Casser le verrou peut ne pas être suffisant. Dans l'exemple, Sally ne veut pas seulement casser le verrou oublié par Harry, mais également re-verrouiller le fichier pour son propre usage. Elle peut le faire en lançant svn unlock avec l'option --force puis svn lock à la suite, mais il existe une petite chance que quelqu'un d'autre verrouille le fichier entre les deux commandes. La meilleure solution est donc de voler le verrou, ce qui implique de casser et re-verrouiller le fichier en une seule opération atomique. Pour ce faire, Sally passe l'option --force à la commande svn lock :

$ svn lock raisin.jpg
svn: avertissement : Échec de la demande de verrou : 423 verrouillé (http://svn.exemple.com)
$ svn lock --force raisin.jpg
'raisin.jpg' verrouillé par l'utilisateur 'sally'.
$

Dans tous les cas, que le verrou soit cassé ou volé, Harry est bon pour une bonne surprise. La copie de travail de Harry contient toujours le jeton de verrouillage original, mais le verrou n'existe plus. Le jeton de verrouillage est dit défunt. Le verrou associé au jeton de verrouillage a été soit cassé (il n'existe plus dans le dépôt) soit volé (remplacé par un autre verrou). Quoi qu'il en soit, Harry peut voir ce qu'il en est en demandant à svn status de contacter le dépôt :

$ svn status
     K raisin.jpg
$ svn status -u
     B         32   raisin.jpg
$ svn update
  B  raisin.jpg
$ svn status
$

Si le verrou dans le dépôt a été cassé, alors svn status --show-updates affiche un B (pour « Broken »« cassé » en anglais) à côté du fichier. Si un nouveau verrou existe en lieu et place de l'ancien, alors un T (pour « sTolen »« volé » en anglais) est affiché. Finalement, svn update détecte les jetons de verrouillage défunts et les supprime de la copie de travail.

Communication par l'intermédiaire des verrous

Nous avons vu comment svn lock et svn unlock peuvent être utilisés pour poser, libérer, casser ou voler des verrous. Cela résout le problème de la sérialisation des accès à un fichier. Mais qu'en est-il du problème plus vaste d'éviter les pertes de temps ?

Par exemple, supposons que Harry verrouille un fichier image et commence à l'éditer. Pendant ce temps, loin de là, Sally veut faire la même chose. Elle ne pense pas à faire un svn status --show-updates et n'a donc pas la moindre idée que Harry a déjà verrouillé le fichier. Elle passe des heures à modifier le fichier et quand elle tente de propager ses changements, elle découvre soit que le fichier est verrouillé, soit que son propre fichier n'était pas à jour. Quoi qu'il en soit, ses modifications ne peuvent pas être fusionnées avec celles de Harry. L'un des deux doit passer ses modifications par pertes et profits, un temps conséquent a été gaspillé.

La solution proposée par Subversion à ce problème est de fournir un mécanisme pour rappeler aux utilisateurs qu'un fichier devrait être verrouillé avant de faire des modifications. Ce mécanisme est mis en œuvre par une propriété spéciale : svn:needs-lock. Si cette propriété est associée à un fichier (quelle que soit sa valeur, qui n'est pas prise en compte), alors Subversion essaie d'utiliser les permissions du système de fichiers pour le placer en lecture seule — à moins, bien sûr, que l'utilisateur ait explicitement verrouillé le fichier. Quand un jeton de verrouillage est présent (indiquant que svn lock a été lancée), le fichier est placé en lecture-écriture. Quand le verrou est libéré, le fichier passe de nouveau en lecture seule.

La théorie est donc que si le fichier image a cette propriété définie, alors Sally remarquera tout de suite quelque chose d'étrange à l'ouverture du fichier : beaucoup d'applications avertissent l'utilisateur immédiatement quand un fichier en lecture seule est ouvert pour édition et pratiquement toutes l'empêcheront de sauvegarder ses modifications dans le fichier. Cela lui rappellera de verrouiller le fichier avant de l'éditer, découvrant ainsi le verrou pré-existant :

$ /usr/local/bin/gimp raisin.jpg
gimp: erreur: le fichier est en lecture seule !
$ ls -l raisin.jpg
-r--r--r--   1 sally   sally   215589 juin  8 19:23 raisin.jpg
$ svn lock raisin.jpg
svn: avertissement : Échec de la demande de verrou : 423 verrouillé (http://svn.exemple.com)
$ svn info http://svn.exemple.com/depot/projet/raisin.jpg | grep errou
Nom de verrou : opaquelocktoken:fc2b4dee-98f9-0310-abf3-653ff3226e6b
Propriétaire du verrou : harry
Verrou créé : 2006-06-08 07:29:18 -0500 (jeu. 08 juin 2006)
Commentaire de verrouillage (1 ligne):
J'effectue quelques retouches.  Je le verrouille pour deux heures.
$
[Astuce] Astuce

Les utilisateurs et les administrateurs sont tous encouragés à positionner la propriété svn:needs-lock sur les fichiers qui ne peuvent pas être contextuellement fusionnés. C'est la technique de base pour favoriser les bonnes habitudes de verrouillage et éviter les pertes de temps.

Notez que cette propriété est un outil de communication qui fonctionne indépendamment de la politique de verrouillage. Autrement dit, n'importe quel fichier peut être verrouillé, que cette propriété existe ou pas. Et réciproquement, l'existence de cette propriété ne rend pas obligatoire le verrouillage pour pouvoir propager des modifications.

Malheureusement, le système n'est pas parfait. Il est possible que, même si le fichier possède la propriété, l'avertissement de lecture seule ne marche pas. Quelquefois, les applications ne suivent pas les normes et « piratent » le fichier en lecture seule, autorisant sans rien dire l'utilisateur à modifier et sauvegarder le fichier. Subversion ne peut pas faire grand chose dans ce genre de cas : au final, rien ne remplace une bonne communication entre les membres d'une équipe [17].



[15] À ce propos, un peu de communication n'aurait pas non plus été un mauvais remède pour leurs homonymes hollywoodiens.

[16] Pour l'instant, Subversion ne permet pas de poser de verrou sur un répertoire.

[17] À part, peut-être, la fusion mentale vulcaine.