David Demelier

J'écris du code, de la musique et je contribue à mes projets opensource préférés.

Pourquoi je préfère Mercurial

À l’heure de l’externalisation massive, du “social coding“, du cloud computing ou encore d’autres joyeusetés “mainstream” beaucoup d’entre nous sommes passés à GitHub, bitbucket ou d’autres services d’hébergement.

Il est vrai que Git domine largement le marché de nos jours et ce, sans doute grâce à Github. Néanmoins, dans cet article je vais expliquer pourquoi j’utilise Mercurial et que je ne suis pas prêt de changer.

Avant de vous donner mes raisons, je tiens à préciser que Mercurial est le premier SCM que j’ai utilisé massivement depuis environ 2008. Je n’ai jamais changé depuis bien que j’ai déjà essayé quelques autres comme git, fossil.

Cet article risque d’être très subjectif, je ne dénigre en aucun cas Git, je vais le comparer le plus objectivement possible. Tout ce que je dirai sur Git ne sont pas des reproches mais des jugements personnels.

Simple

Quand vous recherchez “git vs mercurial” sur votre voteur de recherche préféré, vous tomberez sans doute sur cet argument principal. Mercurial est plus simple que Git. C’est vrai, Mercurial possède moins d’options et moins de commandes qui sont aussi plus concises. Cela ne veut pas dire que Mercurial est moins puissant. Vous verrez pourquoi plus loin.

Prenons par exemple la commande commit, voici la différence entre Git et Mercurial :

$ hg help commit | wc -l
59
$ git help commit | wc -l
320

Certes la documentation est détaillée, mais Git possède un nombre élevé d’options ce qui permet à git commit de faire beaucoup trop de choses à mon sens.

KISS

Mercurial et Git sont différents dans leurs commandes. Mercurial respecte le principe KISS bien plus fortement que Git.

Prenons par exemple la commande git checkout. Cette dernière permet de :

  1. Créer une branche avec git checkout -b,
  2. Réinitialiser des fichiers git checkout -- files...,
  3. Changer de branche git checkout mabranche.

Dans Mercurial, il s’agit de 3 commandes différentes, hg branch, hg revert et hg update.

Homogène

Mercurial est presque entièrement écrit en Python. Certaines parties sont écrites en C pour des raisons de performances, mais il est possible d’installer Mercurial en python-pur. Notez que c’est peu recommandé toutefois. Git, en contrepartie est codé en C, perl, shell et dans certains cas awk et ruby.

J’apprécie aussi le fait que chaque commande est implémentée de la même manière, avec un système de décorateur en python. Ce qui signifie que chaque documentation ou erreur de commande renvoie le même message.

Exemple avec des options qui n’existent pas entre Git et Mercurial :

$ git commit -x
error: unknown switch `x'
usage: git commit [<options>] [--] <pathspec>...

$ git send-email -x

No patch files specified!

git send-email [options] <file | directory | rev-list options >

$ git log -x
fatal: unrecognized argument: -x

En contrepartie, Mercurial :

$ hg commit -x
hg commit: option -x not recognized
hg commit [OPTION]... [FILE]...

$ hg branch -x
hg branch: option -x not recognized
hg branch [-fC] [NAME]

Note : une partie des commande a été tronqué pour des raisons esthétiques.

Intégration

Dans Mercurial, toutes les commandes sont intégrées avec aucune autre dépendance. Mon exemple préféré est hg serve et git instaweb.

La commande hg serve créé un petit serveur web directement dans le dépôt permettant une lecture seule par défaut du dépôt depuis un navigateur. Ceci utilise python uniquement sans aucune dépendance. En contrepartie, la commande git instaweb utilise divers outils externe et est d’une complexité hors norme.

Phases

Un système de phases est présent dans Mercurial, ce qui vous empêche par défaut de faire n’importe quoi dans votre projet.

Il y a 3 phases dans Mercurial : secret < draft < public.

Par défaut, quand vous faites un commit, votre révision est en draft. Cela signifie que vous pouvez encore la modifier ou la déplacer. Celle ci deviendra publique si vous la transmettez sur un autre dépôt. Une fois publique, il est interdit de modifier ces révisions et la plupart des commandes vous le diront. Il est néanmoins possible de forcer un changement de phase quand on sait ce qu’on fait. La dernière phase secret permet de ne pas transmettre par erreur une révision qu’on voudrait garder localement encore. Celle ci s’obtient en utilisant hg commit -s.

Utilisable en script

Rejoignant un peu la section KISS au dessus, les sorties de commandes de Mercurial sont toujours simples. Cela permet aux développeurs de scripts de pouvoir s’intercaler convenablement sur les sorties de commandes.

Exemple entre hg status et git status :

Pour les deux cas, je pars d’un dépôt vide et je rajoute juste un README.md.

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   README.md

Avec Mercurial :

$ hg status
A README.md

Les commandes hg in et hg out

Deux commandes de Mercurial qui sont vraiment pratique, elles permettent de voir si des révisions vont être soumises ou récupérées.

Portable

Mercurial est 100% portable. C’est un des objectifs principaux du projet. Il fonctionne exactement de la même manière sur Mac, Linux et Windows. Par ailleurs, les développeurs de TortoiseHg ont aussi fait le bon choix d’utiliser Qt pour l’interface graphique. Ainsi, d’un système à l’autre, vous avez exactement la même interface, solide intégrée et native.

En contrepartie, Git sur Windows est particulièrement de moins bonne qualité et certaines des commandes ne fonctionnent pas très bien sur cmd.exe. Bon je vous l’accorde, quelle idée d’utiliser la ligne de commande Windows.

Templates

Les templates forment un moyen de personnaliser la sortie des commandes. Cela se passe avec le paramètre -T lorsque la commande est supportée.

Exemple avec hg log, je souhaite afficher uniquement les révisions :

$ hg log -T '{node}\n'
6dcaa3331044d0b52280b58ae32fac138b9fdbc8
2e1ea792ddfd0b8cbea856f2550deb9cc56ca038
72034ab78d2aae8a1007b85169d7ae298f8be2d4

Je souhaite afficher les révisions courtes et la première ligne de leur description :

hg log -T '{rev}: {desc|firstline}\n'
65: Marker: style & fix
64: Docs: add custom definition list syntax
63: Marker: add custom definition list, closes #899 @1h
62: Marker: new style
61: Marker: update usage

Les templates permettent aussi de rajouter des filtres sur le contenu comme vous pouvez le voir sur le format {desc|firstline} où firstline est une fonction qui s’applique sur la description.

Je vous laisse le soin de regarder hg help templates pour plus amples informations.

Revsets

De la même manière des templates, il est possible de sélectionner des révisions parmi un ensemble de requêtes. Cela permet entre autre d’exporter, supprimer, déplacer un certain type de révisions.

Pour ce faire, certaines commandes permettent de sélectionner des ensembles de révisions avec la plupart du temps un paramètre -r.

Par exemple, il est tout à fait possible de lister toutes les révisions encore considérées draft.

$ hg log -r 'draft()'
changeset:   66:2b7ecb2b2152
user:        David Demelier <markand@malikania.fr>
date:        Tue Aug 21 21:40:34 2018 +0200
summary:     ajout de foo.md

changeset:   67:f97f50da2a23
bookmark:    @
tag:         tip
user:        David Demelier <markand@malikania.fr>
date:        Tue Aug 21 21:40:40 2018 +0200
summary:     ajout de bar.md