Auteur: | Guillaume Bouchard |
---|---|
Création: | 4 Novembre 2009 |
Sommaire
Introduction
Lors de tout travail sur du code, il arrive souvent que l'on se retrouve face à différentes problématiques :
Pour cela il existe de nombreux outils appelés gestionnaires de versions. Vous avez peut être déjà entendu parler de Subversion, mais celui-ci n'est pas très pratique pour vous car il impose d'avoir un serveur pour stocker les données, ce qui impose une connexion internet et la disponibilité d'un serveur (ou l'inscription à une offre en ligne comme assembla par exemple)
Dans ce document je vais vous apprendre à vous servir de façon basique de Mercurial, ou hg, et vous verrez que quelques minutes d'apprentissage pourrons vous faire gagner beaucoup de temps.
Mercurial est un gestionnaire de version décentralisé, cela signifie qu'il n'a pas besoin de serveur pour fonctionner.
Mercurial n'initialise sur un répertoire, dit répertoire de travail. Une fois cette initialisation effectuée, l'outil prend note de toutes les modifications faites dans le répertoire (et ses sous-répertoire) et vous fournit des outils très performants d'analyse du répertoire ainsi qu'un moyen simple de transférer vos modification à un collaborateur.
Lors de vos sessions de travail, il faudra dire à Mercurial quel sont les nouveaux fichiers que vous voulez suivre. Puis de façons régulières, il faudra demander à Mercurial de réaliser un point de sauvegarde, ou commit.
Mercurial est disponible sur windows, macos ou unix, Je vous renvoie au site de Mercurial. Pour linux il est disponible très facilement dans votre arbre de paquets (apt-get/aptitude/emerge install mercurial).
Il est bien évidement disponible sur les machines de la fac, et si ce n'est pas le cas, allez le demander aux administrateurs.
Il existe de nombreuses interfaces graphiques pour mercurial, la plus connue pour Windows étant TortoiseHG. La suite de ce document ne les traites pas et se focalise sur l'utilisation ligne de commande, mais si vous comprenez les concepts, l'utilisation d'une autre interface ne posera pas de problème.
Supposons que nous travaillons sur un petit projet de programmation python. Ce projet consiste à demander à l'utilisateur des informations personnels et a afficher quelques bêtises basées sur ses informations.
Note
L'exemple se fait sur un programme python car cela m'amuse à vous sensibiliser à l'existence de ce langage, ne vous formaliser pas sur le code, comprenez l'outil !
Commençons par crée notre répertoire de travail et ajoutons un peu de code à l'intérieur :
~ $ mkdir my_project ~ $ cd my_project/ ~/my_project $ vim test.py
Le contenu du fichier test.py étant le suivant, celui-ci demande le nom de l'utilisateur et l'affiche :
name = raw_input("Quel est votre nom ? ") print "Bonjour",name
L'exécution du programme nous donne :
~/my_project $ python test.py Quel est votre nom ? Guillaume Bonjour Guillaume
Nous voulons maintenant demander à Mercurial de gérer notre code grâce à la commande hg init :
~/my_project $ hg init ~/my_project $ hg status ? test.py
La commande hg status nous donne la liste des fichiers du répertoire et leur état. Ici le "?" nous annonce que Mercurial ne connaît pas le fichier test.py, c'est à dire qu'il ne le gère pas encore. Il faut lui dire de le gérer grâce à hg add :
~/my_project $ hg add test.py ~/my_project $ hg status A test.py
Ici le A signifie que le fichier a été ajouté.
Note
Par pitié, ne mettez pas de fichier inutile dans vos dépôts (résultats de compilation par exemple). Ceux-ci sont volumineux et inutile.
Note
Votre répertoire de travail, ou dépôt contient maintenant un répertoire .hg. Ne modifier, déplacer, supprimer jamais ce répertoire. Il contient toutes les informations de votre dépôt.
Les modifications ne sont pas encore sauvegardées, hg commit nous crée le premier point de sauvegarde :
~/my_project $ hg commit
Note
Lors du commit, vous serez amenés à entrer un message de commit dans un éditeur de texte. Essayer d'entrer le message le plus précis possible décrivant les modification réalisées depuis le dernier commit.
Note
Quand faut il réaliser un commit ? Le plus souvent possible, chaque fois qu'une tache atomique est réalisée, comme l'ajout d'une fonction ou la correction d'un bug. Plus vos commit seront petits et bien dissociables, plus la recherche dans l'historique des commits sera facile à réaliser.
Si vous demander à nouveau le statut du dépôts, celui-ci ne renverra rien puisque depuis le dernier commit, aucune modification n'a été effectuée. Vous pouvez aussi observer la liste des commit grâce à la commande hg log :
~/my_project $ hg log changeset: 0:b1502e612f2d tag: tip user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde
Note
Les commits sont accompagnées d'un nom d'utilisateur. Pour bien vous identifier et ainsi permettre d'attribuer facilement les commits à son auteur, il faut modifier le fichier ~/.hgrc de manière a entrer les informations nécessaires :
[ui] username=Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr>
Nous allons maintenant modifier un peu notre programme, le fichier test.py devient :
#coding: utf8 name = raw_input("Quel est votre nom ? ") birthyear = int(raw_input("En quelle année etes vous né ?")) print "Bonjour",name print "Vous avez",(2009 - birthyear),"ans, félicitations !"
et l'exécution donne :
~/my_project $ python test.py Quel est votre nom ? Guillaume En quelle année etes vous né ?1986 Bonjour Guillaume Vous avez 23 ans, félicitations !
Que donne hg status, et hg diff qui permet de voir les modifications depuis le dernier commit ? :
~/my_project $ hg status M test.py ~/my_project $ hg diff diff -r b1502e612f2d test.py --- a/test.py Wed Nov 04 12:59:34 2009 +0100 +++ b/test.py Wed Nov 04 13:10:51 2009 +0100 @@ -1,3 +1,7 @@ +#coding: utf8 name = raw_input("Quel est votre nom ? ") +birthyear = int(raw_input("En quelle année etes vous né ?")) + print "Bonjour",name +print "Vous avez",(2009 - birthyear),"ans, félicitations !"
Le M de status indique que le fichier à été modifié. Le diff nous montre les 4 lignes ajoutés en les préfixant d'un +.
Il est temps de sauvegarder cette modification, vous pouvez spécifier directement le message dans la commande :
~/my_project $ hg commit -m "Ajout de la demande de la date et du calcul de l'age" ~/my_project $ hg log changeset: 1:da5b3591d96c tag: tip user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:13:31 2009 +0100 summary: Ajout de la demande de la date et du calcul de l'age changeset: 0:b1502e612f2d user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde
Vous pouvez facilement réaliser une archive du dépôt et la transférer par mail grâce à la commande hg bundle:
~/my_project $ hg bundle -a mondepot.bz2 2 changesets found ~/my_project $ ls mondepot.bz2 test.py
Il vous suffit ensuite d'envoyer le fichier mondepot.bz2 à votre collegue par mail ou tout autre moyen (la clé usb fonctionne très bien).
De son coté, celui-ci crée un répertoire de travail avec hg init et importe vos commits :
~ $ mkdir tp_trop_cool ~ $ cd tp_trop_cool/ ~/tp_trop_cool $ hg init ~/tp_trop_cool $ hg unbundle ../mondepot.bz2 adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 1 files (run 'hg update' to get a working copy)
Mercurial vous conseil d'exécuter hg update. En effet, lors de l'importation des modification, votre dépôt reste dans son état précédant, les commits importé ne sont pas appliqués au dépôt. Effectuez l'update, nous verrons par la suite ce que cela signifie :
~/tp_trop_cool $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Les modifications de votre collègue ont étés importées :
~/tp_trop_cool $ hg log changeset: 1:da5b3591d96c tag: tip user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:13:31 2009 +0100 summary: Ajout de la demande de la date et du calcul de l'age changeset: 0:b1502e612f2d user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde test.py
Supposons pour les besoins que mon collègue s'appelle Jon Snow, il travail dans le répertoire ~/tp_trop_cool sur son ordinateur alors que moi je travail dans le repertoire ~/my_project/.
Jon Snow va effectuer plusieurs modifications du fichier, d'abord une erreur de frappe :
~/tp_trop_cool $ hg diff diff -r da5b3591d96c test.py --- a/test.py Wed Nov 04 13:13:31 2009 +0100 +++ b/test.py Wed Nov 04 13:31:50 2009 +0100 @@ -1,6 +1,6 @@ #coding: utf8 name = raw_input("Quel est votre nom ? ") -birthyear = int(raw_input("En quelle année etes vous né ?")) +birthyear = int(raw_input("En quelle année etes vous né ? ")) print "Bonjour",name print "Vous avez",(2009 - birthyear),"ans, félicitations !" ~/tp_trop_cool $ hg commit -m "Ajout d'un espace à la fin de la question" ~/tp_trop_cool $ vim test.py
Puis il va supprimer la date 2009 en dur au profit de la fonction renvoyant la date du jour :
~/tp_trop_cool $ hg diff diff -r d55f343ae46f test.py --- a/test.py Wed Nov 04 13:32:04 2009 +0100 +++ b/test.py Wed Nov 04 13:32:26 2009 +0100 @@ -1,7 +1,9 @@ #coding: utf8 +import datetime + name = raw_input("Quel est votre nom ? ") birthyear = int(raw_input("En quelle année etes vous né ? ")) print "Bonjour",name -print "Vous avez",(2009 - birthyear),"ans, félicitations !" +print "Vous avez",(datetime.datetime.now().year - birthyear),"ans, félicitations !" ~/tp_trop_cool $ hg commit -m "Suppression de 2009 en dur et remplacement par la valeur de l'année en cours"
Au final, son log est le suivant :
~/tp_trop_cool $ hg log changeset: 3:004cb2580811 tag: tip user: Jon Snow <jon.snow@thewall.7kd.invalid> date: Wed Nov 04 13:32:35 2009 +0100 summary: Suppression de 2009 en dur et remplacement par la valeur de l'année en cours changeset: 2:d55f343ae46f user: Jon Snow <jon.snow@thewall.7kd.invalid> date: Wed Nov 04 13:32:04 2009 +0100 summary: Ajout d'un espace à la fin de la question changeset: 1:da5b3591d96c user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:13:31 2009 +0100 summary: Ajout de la demande de la date et du calcul de l'age changeset: 0:b1502e612f2d user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde
De mon coté, je fais une modification très simple du dépôt :
~/my_project $ hg diff diff -r da5b3591d96c test.py --- a/test.py Wed Nov 04 13:13:31 2009 +0100 +++ b/test.py Wed Nov 04 13:36:58 2009 +0100 @@ -1,6 +1,6 @@ #coding: utf8 name = raw_input("Quel est votre nom ? ") -birthyear = int(raw_input("En quelle année etes vous né ?")) +birthyear = int(raw_input("En quelle année êtes vous né(e) ?")) print "Bonjour",name print "Vous avez",(2009 - birthyear),"ans, félicitations !" ~/my_project $ hg ci -m "Correction d'une faute d'orthographe + accord feminin"
Plus une autre où j'ajoute un fichier TODO :
~/my_project $ hg add TODO ~/my_project $ hg diff diff -r d781be5c36b1 TODO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO Wed Nov 04 13:39:20 2009 +0100 @@ -0,0 +1,2 @@ +- pleins de trucs à faire +- penser a utiliser mercurial, c'est sympa ~/my_project $ hg status A TODO ~/my_project $ hg commit -m "Ajout du TODO"
J'ai donc deux nouveaux commits dans mon dépôt :
~/my_project $ hg log changeset: 3:06023582b078 tag: tip user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:39:25 2009 +0100 summary: Ajout du TODO changeset: 2:d781be5c36b1 user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:37:28 2009 +0100 summary: Correction d'une faute d'orthographe + accord feminin changeset: 1:da5b3591d96c user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:13:31 2009 +0100 summary: Ajout de la demande de la date et du calcul de l'age changeset: 0:b1502e612f2d user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde
Je décide d'envoyer mes commits par mail à Jon :
~/my_project $ hg bundle -a mes_nouveaux_commits.bz2 4 changesets found
Jon les reçoit et les charges dans son dépôt :
~/tp_trop_cool $ hg unbundle ../mes_nouveaux_commits.bz2 adding changesets adding manifests adding file changes added 2 changesets with 2 changes to 2 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge)
Voila peut être la pire des situation, Jon a travaillé pendant que je travaillais aussi, résultat nos codes sont différentes. Mercurial ne tolère pas cela et ainsi crée une nouvelle tête, c'est ce qu'il nous informe dans son message.
Note
Nous voyons ici tout de suite le cas particulier où les deux participants ont travaillés chacun de leur coté. Dans le cas ou cela ne serait pas le cas, hg unbundle ne créerait pas les nouvelles têtes et les commits de Guillaume serait directement intégrés aux commits de Jon.
Mercurial est plutôt sympathique et nous donne la solution, il faut exécuter hg merge. Cette commande va regrouper ensemble les deux têtes de commits :
~/tp_trop_cool $ hg merge merging test.py warning: conflicts during merge. merging test.py failed! 1 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
Drame, une des modification de Guillaume dans "test.py" n'est pas compatible avec celles de Jon, il va donc falloir intervenir manuellement. En ouvrant le fichier test.py on peut trouver :
<<<<<<< local birthyear = int(raw_input("En quelle année etes vous né ? ")) ======= birthyear = int(raw_input("En quelle année êtes vous né(e) ?")) >>>>>>> other
Ces marques sont la preuve d'un conflit dans la zone en question. La zone du haut correspond à votre code, celle du bas au code que l'on vient d'importer. Il est de VOTRE responsabilité de remplacer cette zone par du code prenant en compte les modifications des deux personnes, ici la faute d'orthographe, la correction de genre et l'espace en fin de ligne :
birthyear = int(raw_input("En quelle année êtes vous né(e) ? "))
Vous devez ensuite exécuter un commit pour sauvegarder les modifications du merge, mais avant il faut prévenir Mercurial que vous avez résolu le conflit dans le fichier test.py :
~/tp_trop_cool $ hg resolve -m test.py ~/tp_trop_cool $ hg commit -m "merge avec Guillaume"
Le log commence à devenir conséquent :
~/tp_trop_cool $ hg log changeset: 6:b9f31a715b66 tag: tip parent: 3:004cb2580811 parent: 5:06023582b078 user: Jon Snow <jon.snow@thewall.7kd.invalid> date: Wed Nov 04 13:53:58 2009 +0100 summary: merge avec Guillaume changeset: 5:06023582b078 user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:39:25 2009 +0100 summary: Ajout du TODO changeset: 4:d781be5c36b1 parent: 1:da5b3591d96c user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:37:28 2009 +0100 summary: Correction d'une faute d'orthographe + accord feminin changeset: 3:004cb2580811 user: Jon Snow <jon.snow@thewall.7kd.invalid> date: Wed Nov 04 13:32:35 2009 +0100 summary: Suppression de 2009 en dur et remplacement par la valeur de l'année en cours changeset: 2:d55f343ae46f user: Jon Snow <jon.snow@thewall.7kd.invalid> date: Wed Nov 04 13:32:04 2009 +0100 summary: Ajout d'un espace à la fin de la question changeset: 1:da5b3591d96c user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 13:13:31 2009 +0100 summary: Ajout de la demande de la date et du calcul de l'age changeset: 0:b1502e612f2d user: Guillaume Bouchard <guillaume.bouchard@insa-lyon.fr> date: Wed Nov 04 12:59:34 2009 +0100 summary: Premier point de sauvegarde
Jon peut ainsi renvoyer ses modification à Guillaume :
~/tp_trop_cool $ hg bundle -a bundle_apres_merge.bz2 7 changesets found
Qui peut les intégrer :
~/my_project $ hg unbundle ../bundle_apres_merge.bz2 adding changesets adding manifests adding file changes added 3 changesets with 3 changes to 2 files (run 'hg update' to get a working copy) ~/my_project $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Guillaume vient de recevoir 3 changement de la part de Jon, les deux effectués par Jon ainsi que le merge effectué par Jon de son travail. Celui-ci peut effectuer différentes opérations sur son dépôt pour consulter ces changements.
hg update N lui permettra de modifier son dépôt pour qu'il reflète l'état de la révision N (N étant le premier nombre dans la ligne changeset du hg log). hg update revient à la toute dernière version. :
~/my_project $ hg update 0 1 files updated, 0 files merged, 1 files removed, 0 files unresolved ~/my_project $ python test.py Quel est votre nom ? Guillaume Bonjour Guillaume ~/my_project $ hg update 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved ~/my_project $ python test.py Quel est votre nom ? Guillaume En quelle année etes vous né ?1986 Bonjour Guillaume Vous avez 23 ans, félicitations ! ~/my_project $ hg update 3 2 files updated, 0 files merged, 0 files removed, 0 files unresolved ~/my_project $ python test.py Quel est votre nom ? Guillaume En quelle année êtes vous né(e) ?1986 Bonjour Guillaume Vous avez 23 ans, félicitations ! ~/my_project $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
Vous pouvez consulter les différences entre deux versions avec hg diff -rN:M :
~/my_project $ hg diff -r2:4 diff -r d781be5c36b1 -r d55f343ae46f test.py --- a/test.py Wed Nov 04 13:37:28 2009 +0100 +++ b/test.py Wed Nov 04 13:32:04 2009 +0100 @@ -1,6 +1,6 @@ #coding: utf8 name = raw_input("Quel est votre nom ? ") -birthyear = int(raw_input("En quelle année êtes vous né(e) ?")) +birthyear = int(raw_input("En quelle année etes vous né ? ")) print "Bonjour",name print "Vous avez",(2009 - birthyear),"ans, félicitations !"
Les commandes de mercurial commencent par hg et sont suivie d'une action, qui peut etre raccourci :
Affiche l'état du dépôt depuis le dernier commit, plusieurs possibilités :
A | Fichier Ajouté | |
M | Fichier Modifié | |
? | Fichier non pris en compte |
Note
hg bundle -a stocke TOUT le dépôt dans l'archive, cela peut être lourd pour le transfert par mail, mais à l'avantage que tout le dépôt peut être recré à partir d'un unique bundle en cas ou vous perdiez les données de votre ordinateur.
Conclusion
Mercurial peut vous faire gagner beaucoup de temps pour un temps d'apprentissage très cours. Bien évidement cette introduction ne dispense en rien de lire le site de Mercurial où vous pourrez en apprendre plus sur son fonctionnement et sur tous les petits service qu'il peut rendre.
Note
Une des première chose à faire que je vous conseil est de vous renseignez sur l'utilisation du .hgignore.
Note
Dernière remarque, Mercurial est utile pour tout ce qui est texte, vous pouvez donc aussi y stocker vos liste de course, vos sites web ou vos rapport si ceux-ci sont fait dans un langage texte (comme LaTeX ou RestructuredText). Ce document est d'ailleurs actuellement géré avec Mercurial.
Note
La note précédente suppose que le document est sous gestion de version, c'est à dire qu'il peut évoluer. N'hésitez pas à me mailer tous ce qui vous semble pertinent.