aragost Trifork: Mercurial Kick Start Exercises


Premiers pas avec Mercurial

Voici les premiers pas avec Mercurial. Nous pensons que la meilleure façon d’apprendre des choses est de les faire vous-même, nous vous encourageons donc à suivre et à exécuter vous-même les commandes d‘“Alice” et “Bob”. Les couleurs des terminaux vous permettent de savoir qui exécute les commandes : Alice a un terminal de couleur pêche clair, celui de Bob est turquoise clair et Carla (que vous découvrirez plus tard) a un terminal de couleur lavande.

Vous trouverez à la fin des exercices pratiques avec des commandes pour Alice et Bob.

Sommaire

Installation & Configuration

Mercurial est disponible pour la plupart des systèmes d’exploitation notamment Windows, Mac OS X et GNU/Linux :

Windows :

Installez seulement TortoiseHg, cela inclut Mercurial et des outils graphiques pour examiner et modifier les dépôts.

Après l’installation, vous aurez dans le menu contextuel de Windows Explorer (bouton droit) des options pour accéder aux outils graphiques. Après avoir ouvert une nouvelle session, vous pourrez utiliser en ligne de commande les programmes hg et thg. Utilisez thg pour démarrer les outils graphiques de TortoiseHg.

Astuce

Bien que les exemples utilisent des commandes Unix, vous pouvez facilement les traduire pour les exécuter sous Windows :

ls          -> dir
cat         -> type
echo "abc"  -> echo abc
Linux :

Installez Mercurial en utilisant le gestionnaire de paquet de votre distribution. Si vous ne le trouvez pas, vous pouvez l’installer à partir des sources : téléchargez la dernière version stable, décompressez-la et exécutez make local. Ensuite créez un lien symbolique du script hg dans un répertoire de votre PATH. C’est fait !

Vous aurez besoin des PyQt bindings pour installer TortoiseHg. Ils sont disponibles dans la plupart des distributions. Ensuite suivez ces instructions. L’application thg sera aussi installée.

Mac OS X :
Nous vous recommandons d’installer MacHg pour avoir un client rapide et natif pour Mercurial. MacHg est distribué avec sa propre version de Mercurial, donc vous n’avez pas à vous en soucier. Si vous avez déjà utilisé TortoiseHg sous Windows, vous serez content de savoir que vous pouvez l’installer sur Mac OS X aussi.

Nous utiliserons la ligne de commande dans nos exemples, mais certaines fois nous vous montrerons l’état du dépôt dans le Workbench de TortoiseHg.

Après l’installation de Mercurial, essayez d’exécuter la commande hg version :

alice$ hg version
Mercurial Distributed SCM (version 2.2)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2012 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

C’est parfait si votre version est supérieure à 2.2. Pour ces tutoriels, vous pouvez aussi utiliser la version 1.4 de Mercurial, mais nous vous recommandons d’utiliser toujours la dernière version disponible pour votre système d’exploitation. Vous pouvez vérifier votre installation avec :

alice$ hg debuginstall 
Checking encoding (UTF-8)...
Checking installed modules (/usr/share/pyshared/mercurial)...
Checking templates (/usr/lib/python2.7/dist-packages/mercurial)...
Checking commit editor...
Checking username...
 no username supplied (see "hg help config")
 (specify a username in your configuration file)
1 problems detected, please check your install!

Normalement après l’installation, le nom de l’utilisateur est manquant. Indiquez-le en l’écrivant dans un fichier de configuration. Avec les systèmes Unix ou équivalents, il s’agit du fichier $HOME/.hgrc, avec Windows le fichier est appelé %HOME%\Mercurial.ini (exécutez hg help config pour voir tous les chemins possibles ou vous pouvez aussi utiliser thg userconfig). Créer un fichier avec le contenu suivant :

[ui]
username = Prénom Nom <example@example.net>

mais avec votre propre nom. Nous utiliserons d’abord Alice <alice@example.net> et son terminal ressemblera aux deux ci-dessus. La commande hg debuginstall ne devrait plus afficher aucune erreur.

Commandes de base

Nous allons maintenant passer rapidement en revue les commandes de base de Mercurial. Ces commandes sont similaires à celles des anciens systèmes tels que Subversion ou même CVS. Si vous utilisez TortoiseHg sous Windows, vous pouvez aussi lire le guide de démarrage rapide (en anglais).

Création d’un dépôt

Création d’un dépôt avec la commande hg init :

alice$ ls
alice$ hg init example
alice$ ls
example

La commande hg init a créé un nouveau répertoire qui est le nouveau dépôt. Nous aurions aussi pu créer le répertoire example nous-mêmes et exécuter, dans ce répertoire, la commande hg init sans autre paramètre.

En rentrant dans le répertoire example nous pouvons observer :

alice$ cd example
alice$ ls -a
.
..
.hg

Comme vous pouvez le voir le nouveau dépôt est vide excepté un sous-répertoire .hg. Mercurial enregistre l’historique et d’autres fichiers de gestion dans ce répertoire.

Création d’une révision

Créons un fichier et demandons à Mercurial l’état du dépôt :

alice$ echo "Hello World" > hello.txt
alice$ hg status
? hello.txt

La réponse nous indique que Mercurial ne connaît pas le fichier hello.txt, c’est-à-dire qu’il ne suit pas ses modifications. Nous pouvons ajouter ce fichier :

alice$ hg add hello.txt
alice$ hg status
A hello.txt

ce qui change l’état à “A” pour “ajouté”. Le fichier n’est pas encore enregistré dans l’historique du dépôt — nous allons le faire en créant un commit :

alice$ hg commit -m "First version of hello"

La réponse n’indique rien — Mercurial ne vous préviendra que lorsque cela est nécessaire.

Analyse de l’historique

Vous pouvez voir la nouvelle révision (changeset dans Mercurial) avec la commande hg log :

alice$ hg log
changeset:   0:d312da7770f4
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Bravo ! Vous avez fait un nouveau commit avec Mercurial. Modifions maintenant le fichier et demandons à Mercurial de nous montrer les fichiers de la copie de travail qui diffèrent de la dernière révision :

alice$ echo "Goodbye!" > hello.txt
alice$ hg diff
diff -r d312da7770f4 hello.txt
--- a/hello.txt Wed Mar 10 20:10:05 2010 +0000
+++ b/hello.txt Mon Mar 10 20:10:10 2010 +0000
@@ -1,1 +1,1 @@
-Hello World
+Goodbye!

Nous pouvons changer d’avis et revenir au fichier tel qu’il était précédemment :

alice$ hg revert hello.txt
alice$ cat hello.txt
Hello World

Faisons un autre changement à notre fichier :

alice$ echo >> hello.txt
alice$ echo "by Alice." >> hello.txt
alice$ hg commit -m "Added author." 

Nous pouvons maintenant demander à Mercurial d’annoter le fichier, c’est-à-dire de nous montrer quand chaque ligne a été modifiée :

alice$ hg annotate hello.txt
0: Hello World
1:
1: by Alice.

Mercurial nous indique ici que la première ligne provient de la révision 0 et que les deux lignes suivantes proviennent de la révision 1. TortoiseHg offre un très bel outil d’annotation interactif, thg annotate.

Annoter les fichiers est très utile pour vous aider à résoudre des bugs : après avoir trouvé le bug, l’utilisation de hg annotate vous permettra immédiatement de trouver la révision dans laquelle la ligne a été modifiée. Vous pourrez alors utiliser hg log pour récupérer le message associé à la révision, ce qui vous expliquera sans doute la raison pour laquelle le développeur a fait cette modification.

alice$ hg log -r 1
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

Les deux révisions de notre dépôt sont séquentielles. Cela est plus visible grâce à l’extension standard graphlog. Pour limiter la taille du noyau de Mercurial, beaucoup de fonctionnalités se trouvent dans des extensions. La plupart des meilleures extensions sont livrées avec Mercurial et il est très facile de les activer. Par exemple, pour activer graphlog, Alice a juste ajouté :

[extensions]
graphlog =

à son fichier de configuration. Elle peut exécuter la commande hg help graphlog pour obtenir de l’aide sur l’extension. Cette extension est particulièrement simple, elle donne juste à Alice une nouvelle commande :

alice$ hg glog
@  changeset:   1:2e982bdc137f
|  tag:         tip
|  user:        Alice <alice@example.net>
|  date:        Wed Mar 10 20:15:00 2010 +0000
|  summary:     Added author.
|
o  changeset:   0:d312da7770f4
   user:        Alice <alice@example.net>
   date:        Wed Mar 10 20:10:05 2010 +0000
   summary:     First version of hello

Le résultat de la commande nous montre que la révision 2e982bdc137f est un enfant de d312da7770f4. La même relation peut être vue avec thg log :

alice-log.png

Les fichiers dans la copie de travail sont toujours synchronisés avec une révision particulière. Dans thg log, cela est visible, dans le graphe des révisions, avec une petite balle entourée d’un cercle. Dans le résultat de la commande hg glog, la révision parente du répertoire de travail est mise en évidence avec le signe @.

Voyage dans le temps

Avec la commande hg update vous pouvez changer la révision parente de votre copie de travail. Cela peut être utile pour revenir en arrière et examiner d’anciennes révisions de votre code, par exemple si vous avez besoin de tester une ancienne révision de votre logiciel avec un nouveau système d’exploitation. Ici Alice va revenir à la première révision pour regarder son ancien fichier. Elle utilise d’abord la commande hg parents pour savoir où elle se situe dans le graphe :

alice$ hg parents
changeset:   1:2e982bdc137f
tag:         tip
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:15:00 2010 +0000
summary:     Added author.

et ensuite elle effectue le saut !

alice$ hg update 0
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ hg parents
changeset:   0:d312da7770f4
user:        Alice <alice@example.net>
date:        Wed Mar 10 20:10:05 2010 +0000
summary:     First version of hello

Son fichier provient maintenant d’une ancienne révision :

alice$ cat hello.txt
Hello World

mais la nouvelle révision n’est pas perdue. Mercurial peut facilement la retrouver pour elle.

alice$ hg cat -r 1 hello.txt
Hello World

by Alice.

La commande hg cat est pratique pour voir rapidement d’anciennes révisions de fichiers à la place de l’utilisation de hg update qui oblige à changer tout le répertoire de travail avec l’ancienne révision. Pour revenir à la révision tip, il suffit d’utiliser la commande hg update sans argument :

alice$ hg update
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
alice$ cat hello.txt
Hello World

by Alice.

Exercices

  1. Utilisez hg init pour créer un dépôt appelé kick-start.

  2. Allez dans le répertoire kick-start et créer quelques fichiers. Utilisez la commande hg add pour ajouter les fichiers au dépôt. Vous pouvez utiliser la commande hg revert pour annuler l’ajout des fichiers si vous changez d’avis.

  3. Utilisez la commande hg commit pour commiter les fichiers. Un éditeur sera automatiquement lancé si vous ne précisez aucun commentaire lors de votre commit.

  4. Faites quelques commits supplémentaires.

  5. Utilisez la commande hg update pour mettre à jour votre répertoire de travail avec une ancienne révision. Remarquez que le résultat de la commande hg parents évolue et comparez avec le résultat de la commande hg tip qui ne change pas. Exécutez ensuite hg update pour revenir au tip .

  6. Essayez d’effectuer un commit quand votre répertoire de travail est une ancienne révision. Mercurial vous indiquera que vous avez créer une nouvelle tête (“head”) — une tête est une révision qui ne possède aucun enfant. Utilisez la commande hg heads pour voir les têtes de votre dépôt et regardez le graph dans thg log.

    Remarquez comme la nouvelle révision créée a comme parent le parent indiqué précédemment avec la commande hg parents. C’est une constante avec Mercurial : le parent de la révision de la copie de travail devient le parent de la révision de la prochaine révision.

  7. De multiples têtes représentent de multiples lignes de développement parallèles. Vous fusionnerez sans doute ces lignes avec la commande hg merge. Si vos modifications ne se chevauchent pas, alors la fusion sera simple. Sinon vous devrez résoudre les conflits — plus d’informations à ce sujet ultérieurement.

    Remarquez que la commande hg parents affiche deux révisions après avoir exécuté la commande hg merge. Cela signifie que le prochain commit aura deux parents. Après votre commit, vous pouvez voir les parents avec la commande hg glog ou dans thg log.

Travail en équipe

Le dépôt d’Alice est dans son répertoire personnel. Nous allons vous montrer, avec un fond turquoise clair, les commandes de Bob, son collègue qui doit aussi travailler sur ce projet.

Création d’un clone

Dans ces exemples, Alice et Bob partageront leurs fichiers en local. Cela pourrait aussi très bien être un partage de fichiers à travers un réseau d’entreprise. Les changements pourraient aussi être partagés avec les protocoles HTTP ou SSH — certains utilisent même des courriels ou des clés USB pour partager des révisions. Ils utilisent alors habituellement le format compressé binaire produit par la commande hg bundle. Ce n’est pas nécessaire ici, car Bob utilise la commande hg clone avec des chemins relatifs vers le dépôt d’Alice :

bob$ hg clone ../alice/example
destination directory: example
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved

Bob a maintenant sa propre copie complètement indépendante du dépôt d’Alice. Il peut faire cette copie (ou clone) dès qu’il a accès en lecture au dépôt d’Alice. Bob peut faire ses propres révisions sans perturber le travail d’Alice. Il effectue quelques changements :

bob$ cd example
bob$ echo "Goodbye!" > goodbye.txt
bob$ echo >> goodbye.txt
bob$ echo "by Bob." >> goodbye.txt
bob$ hg add
adding goodbye.txt
bob$ hg commit -m "My goodbye file." 
bob$ echo >> hello.txt
bob$ echo "not by Bob." >> hello.txt
bob$ hg commit -m "Not my file." 

Le dépôt possède maintenant quatre révisions :

bob-log.png

Comparaison des clones

Le dépôt de Bob se souvient du dépôt original (origine du clone) et Bob peut ainsi demander si de nouvelles révisions sont disponibles dans le dépôt d’Alice :

bob$ hg incoming
comparing with /home/alice/example
searching for changes
no changes found

Il peut aussi demander les révisions de son dépôt qui sont absentes dans celui d’Alice :

bob$ hg outgoing
comparing with /home/alice/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

C’est normal : Bob a créé deux nouvelles révisions dans son dépôt et elles ne sont donc pas encore présentes dans le dépôt d’Alice.

Déplacement de révisions

Bob n’a pas les droits nécessaires pour modifier les fichiers dans le répertoire personnel d’Alice, il ne peut donc pas y écrire ses révisions. Il lui fait savoir qu’il a de nouvelles révisions dans son dépôt et qu’elle peut venir les chercher. Pour cela elle va utiliser la commande hg pull, mais avant elle va saisir le chemin du dépôt de Bob dans le fichier .hg/hgrc. C’est un fichier local de configuration du dépôt. Elle écrit:

[paths]
default = /home/bob/example

pour faire du dépôt de Bob le dépôt par défaut pour la commande hg pull et pour les autres commandes qui interagissent avec des dépôts distants. Elle peut voir ce qui pourrait être ajouté à son dépôt avec la commande hg incoming :

alice$ hg incoming
comparing with /home/bob/example
searching for changes
changeset:   2:659410363fe0
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:00:00 2010 +0000
summary:     My goodbye file.

changeset:   3:51b2976b01e8
tag:         tip
user:        Bob <bob@example.net>
date:        Thu Mar 11 10:03:00 2010 +0000
summary:     Not my file.

Remarquez que c’est la commande symétrique à hg outgoing que Bob a exécuté dans son dépôt. Elle décide de continuer et de tirer les changements de Bob :

alice$ hg pull
pulling from /home/bob/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files
(run 'hg update' to get a working copy)

Le dernier message est un indice qui indique à Alice que son répertoire de travail n’a pas été modifié :

alice-pull.png

Le répertoire de travail n’est pas modifié car Alice a peut-être fait ses propres modifications et elle ne veut peut-être pas mettre à jour son répertoire de travail maintenant. Dans notre cas elle le fait :

alice$ hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Vous exécuterez sans doute toujours une commande hg update après la commande hg pull, c’est pour cette raison qu’il existe un raccourci : hg pull -u qui fera automatiquement une mise à jour du répertoire de travail après la commande pull.

Lignes de développement parallèles

Alice a retravaillé un peu le fichier hello.txt :

alice$ hg diff
diff -r 51b2976b01e8 hello.txt
--- a/hello.txt Thu Mar 11 10:03:00 2010 +0000
+++ b/hello.txt Thu Mar 11 11:00:00 2010 +0000
@@ -1,4 +1,4 @@
-Hello World
+Hello, Wonderful World!

 by Alice.
alice$ hg commit -m "Happy, happy!" 

Au même moment, Bob travaille. Il crée un nouveau fichier :

bob$ echo "Welcome!" > welcome.txt
bob$ hg add
adding welcome.txt
bob$ hg commit -m "Welcome file." 

Alice et Bob ont tous les deux créé une révision qui est un enfant de 51b2976b01e8, mais aucun des deux ne sait encore ce que l’autre a modifié car ils n’ont pas encore exécuté de pull ou de push. Alice décide de tirer depuis Bob :

alice$ hg pull
pulling from /home/bob/example
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)

Que se passe-t-il ? Comme indiqué ci-dessus, une révision de “tête” est une révision sans enfant. Comme Alice et Bob ont fait des modifications différentes à partir de la révision 51b2976b01e8, il y a maintenant 2 révisions sans enfant dans le dépôt. Ces têtes sont des lignes de développement parallèles, comme on peut le voir avec thg log :

alice-heads.png

Les deux lignes de développement doivent être réconciliées, et c’est ce que fait la commande hg merge :

alice$ hg merge
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)

Comme Alice et Bob ont modifié des fichiers différents, il n’y a pas de conflit. Après hg merge, la copie de travail a deux parents — c’est ce qui vous permet de savoir qu’une opération de fusion est en cours si vous l’avez oubliée. La fusion n’est pas encore dans le dépôt (commande commit) car vous pouvez avoir besoin de modifier certains fichiers pour que votre code fonctionne (compilation par exemple). Ce n’est pas parce qu’aucune modification ne se chevauche qu’il ne reste pas des conflits de sémantique que Mercurial ne peut évidemment pas détecter. C’est en général une bonne idée de lancer l’ensemble des tests après une fusion. Dans notre cas, Alice continue et exécute la commande commit :

alice$ hg commit -m "Merge with Bob." 

Les deux têtes sont maintenant liées ensemble dans le graphe des révisions :

alice-merged.png

La révision de la fusion contient les changements des deux révisions parentes. Vous pouvez imaginez une révision de fusion comme votre manière de dire au monde : c’est de cette manière que vous devez combiner la révision X avec la révision Y. Quand d’autres tireront la révision de fusion à partir de votre dépôt, elle s’insérera automatiquement à la bonne place dans leur graphe des révisions. Ils n’auront ainsi pas besoin de faire la même fusion. Nous pouvons le constater si Bob tire depuis Alice :

bob$ hg pull
pulling from /home/alice/example
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
bob-pull.png

Vous pouvez constater que les révisions apparaissent dans un ordre différent entre le dépôt de Bob et le dépôt d’Alice — la révision “Happy, happy!” a changé d’ordre avec la révision “Welcome file.”. Les révisions conservent toujours leur ID, le long nombre hexadécimal, mais le numéro de révision peut changer. Le numéro de révision dépend seulement de l’ordre dans lequel la révision a été ajoutée au dépôt, et quand deux personnes tirent depuis le dépôt de l’autre, la plupart du temps les numéros de révision ne seront pas identiques. Vous devez donc toujours utiliser l’ID de la révision quand vous parlez avec une autre personne. Mais vous pouvez continuer à utiliser le numéro de révision avec des commandes telles que hg update qui fonctionnent à l’intérieur de votre dépôt local.

Exercices

  1. Créer un dépôt et faites quelques commits.

  2. Faites deux copies (clone) de votre dépôt. Vous pouvez constater que le fichier de configuration .hg/hgrc contient automatiquement l’emplacement du dépôt original. La commande hg paths vous permet de voir les chemins définis.

  3. Ajoutez un autre chemin dans .hg/hgrc, tel que:

    bob = /home/bob/test
    

    Vous pouvez maintenant exécuter les commandes hg pull bob, hg outgoing bob, etc… Il est conseillé d’ajouter des raccourcis vers les personnes avec qui vous collaborez souvent.

  4. Essayez de tirer et de pousser des changements. Vérifiez que le répertoire de travail n’est pas modifié même si l’historique est ajouté au dépôt. Le répertoire de travail est seulement modifié avec la commande hg update.

Mercurial permet une collaboration déconnectée. Cela est possible grâce à l’échange de révisions dans ce qu’on appelle des bundles. Un bundle est une représentation compressée binaire d’un certain nombre de révisions.

  1. Créez une nouvelle révision dans un de vos dépôts. Utilisez la commande hg bundle --base X out.bundle pour créer un bundle dans le fichier out.bundle qui contiendra ainsi toutes les révisions depuis la révision X. L’idée est que vous enregistriez quelque part la dernière révision commune entre vous et la destination, et utilisiez cette révision comme base.

  2. Allez dans le dépôt de destination, et utilisez la commande hg unbundle pour importer les révisions du bundle. Dans cet exemple, vous avez probablement les deux dépôts sur votre ordinateur, mais en général les deux dépôts se trouvent sur des ordinateurs qui ne sont jamais connectés au même moment. Cela permet d’échanger des révisions par email par exemple.

Vous pouvez aussi publier votre dépôt avec HTTP :

  1. Exécutez hg serve dans un dépôt. Vous pouvez maintenant accéder au dépôt à partir d’un navigateur avec l’url http://localhost:8000. Essayez de démarrer un serveur (hg serve) en même temps pour le dépôt d’Alice et pour le dépôt de Bob.

  2. Essayez de tirer depuis un dépôt publié avec hg serve.

Notez que hg serve n’est prévu que pour publier un dépôt de manière temporaire. Pour la publication permanente d’un dépôt, vous pouvez utiliser, par exemple, Apache avec le script hgweb.cgi qui est fourni avec Mercurial.

Conclusion

Vous avez vu la plupart des commandes de base de Mercurial et vous avez aussi vu comment fonctionnait le graphe de l’historique. Les commandes importantes sont :

Et la commande la plus importante :