# HG changeset patch # User Romain PELISSE # Date 1252781616 -7200 # Node ID a6b81cd31cfd5da20e0dc629ee65cc4f3b08eb58 # Parent 9d32b10fdb1ed71ebea7b04597cd4bea74b6045b adding complete.xml - which generated but Bryan did add it also - as I have absolutly no personnality I do as he does diff -r 9d32b10fdb1e -r a6b81cd31cfd fr/complete.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/fr/complete.xml Sat Sep 12 20:53:36 2009 +0200 @@ -0,0 +1,16711 @@ + + + + + + Mercurial: The Definitive Guide + + + Compiled from $rev_id$ + + 1 + 9780596800673 + + + Bryan + O'Sullivan + + + + + Mike + Loukides + + + + 2006 + 2007 + 2008 + 2009 + Bryan O'Sullivan + + + + + + + + + Preface + + + Un conte technique + + Il y a quelques années, quand j'ai voulu expliqué + pourquoi je pensais que le gestion de révision distribuée est importante, + le domaine était encore si nouveau qu'il n'y avait presque aucune + littérature publiée pour servir de référence aux personnes intéressées. + + Bien qu'à cette époque je passais beaucoup de temps + à travailler sur les entrailles de Mercurial, je me suis mis à la + rédaction de ce livre parce qu'il me semblait la manière la plus efficace + d'aider notre logiciel à atteindre un vaste auditoire, toujours avec + l'idée que la gestion de révision devrait être distribuée par nature. J'ai + publié ce libre en ligne sous une licence libre pour la même raison : pour + diffuser la parole auprès du monde. + + Il y a un rythme familier à un bon livre sur un logiciel + qui ressemble de près au fait de conter une histoire : Pourquoi ceci est ? + Pourquoi ceci est important ? Comment peut il m'aider ? Comment m'en + servir ? Dans ce livre, j'essaye de répondre à toutes ces questions pour + la gestion de révision distribuée en général, et pour Mercurial en + particulier. + + + + Merci de votre soutien à Mercurial + + En achetant une copie de ce livre, vous soutenez le + développement et la liberté de Mercurial en particulier, et dans + l'Open Source, au logiciel libre en général. O'Reilly Media et + moi-même donnons les revenus issus des ventes de ce livre à la + Software Freedom Conservancy (http://www.softwarefreedom.org/) + qui fournit un support juridique à Mercurial et à de + nombreux autres projets Open Source proéminents et de qualité. + + + + Remerciements + + Ce livre n'aurait pas vu le jour sans les + efforts de Matt Mackal, l'auteur et le chef du projet Mercurial. + Il est assisté très efficacement par des centaines de contributeurs + volontaires à travers le monde. + + Les enfants, Cian et Ruairi, ont toujours été prêt + à m'aider à me reposer avec de merveilleux et impulsif jeux d'enfants. + Je tiens aussi à remercier mon ex-femme, Shannon, pour son soutien. + + + Mes collègues et amis m'ont aidé et assisté de + de nombreuses manières. Cette liste de personne est nécessaire mais très + incomplète : Stephen Hahn, Karyn Ritter, Bonnie Corwin, James Vasile, + Matt Norwood, Eben Moglen, Bradley Kuhn, Robert Walsh, Jeremy + Fitzhardinge, Rachel Chalmers. + + J'ai conçu ce livre de manière ouverte, en publiant + des brouillons des chapitres du livre sur des site web, au fur et à + mesure que je les réalisais. Leurs lecteurs m'ont fait des retours + utilisant l'application web que j'avais développée. A la fin de sa + conception, plus de 100 personnes m'avaient fait des commentaires, + un chiffre incroyable quand l'on considère que ce système de + commentaire n'a tourné que dans les deux derniers mois de la + rédaction du livre. + + J'aimerais particulièrement remercier les + personnes suivantes, dont les commentaires représentent plus + d'un tiers de l'ensemble de ces derniers. Je voudrais les + remercier pour leur attention et effort à me faire des retours + très détaillés. + + Martin Geisler, Damien Cassou, Alexey Bakhirkin, Till Plewe, + Dan Himes, Paul Sargent, Gokberk Hamurcu, Matthijs van der + Vleuten, Michael Chermside, John Mulligan, Jordi Fita, Jon + Parise. + + Je souhaite aussi remercier l'aide des personnes + qui ont découvert des erreurs et fournit des suggestions avisées + à travers tout le livre. + + Jeremy W. Sherman, Brian Mearns, Vincent Furia, Iwan + Luijks, Billy Edwards, Andreas Sliwka, Paweł Sołyga, Eric + Hanchrow, Steve Nicolai, Michał Masłowski, Kevin Fitch, Johan + Holmberg, Hal Wine, Volker Simonis, Thomas P Jakobsen, Ted + Stresen-Reuter, Stephen Rasku, Raphael Das Gupta, Ned + Batchelder, Lou Keeble, Li Linxiao, Kao Cardoso Félix, Joseph + Wecker, Jon Prescot, Jon Maken, John Yeary, Jason Harris, + Geoffrey Zheng, Fredrik Jonson, Ed Davies, David Zumbrunnen, + David Mercer, David Cabana, Ben Karel, Alan Franzoni, Yousry + Abdallah, Whitney Young, Vinay Sajip, Tom Towle, Tim Ottinger, + Thomas Schraitle, Tero Saarni, Ted Mielczarek, Svetoslav + Agafonkin, Shaun Rowland, Rocco Rutte, Polo-Francois Poli, + Philip Jenvey, Petr Tesałék, Peter R. Annema, Paul Bonser, + Olivier Scherler, Olivier Fournier, Nick Parker, Nick Fabry, + Nicholas Guarracino, Mike Driscoll, Mike Coleman, Mietek Bák, + Michael Maloney, László Nagy, Kent Johnson, Julio Nobrega, Jord + Fita, Jonathan March, Jonas Nockert, Jim Tittsler, Jeduan + Cornejo Legorreta, Jan Larres, James Murphy, Henri Wiechers, + Hagen Möbius, Gábor Farkas, Fabien Engels, Evert Rol, Evan + Willms, Eduardo Felipe Castegnaro, Dennis Decker Jensen, Deniz + Dogan, David Smith, Daed Lee, Christine Slotty, Charles Merriam, + Guillaume Catto, Brian Dorsey, Bob Nystrom, Benoit Boissinot, + Avi Rosenschein, Andrew Watts, Andrew Donkin, Alexey Rodriguez, + Ahmed Chaudhary. + + + + Conventions utilisées dans ce livre + + Les conventions typographiques suivantes sont utilisées dans ce livre : + + + + Italique + + + Indique les termes nouveaux, les URLs, les + adresses mail, les noms de fichiers et les extensions de + fichier. + + + + + Taille constante + + + Utilisé pour les extraits de code, comme + dans les paragraphes pour référer aux éléments du programme, + tels que les variables ou les noms de fonctions, de bases + de données, de types de données, de variables d'environnement, + d'instructions, et de mots clés. + + + + + Taille constante avec gras + + + Afficher les commandes ou autres textes qui + devraient être saisis par l'utilisateur. + + + + + Constante avec italique + + + Affiche les textes qui devraient être remplacés + par une valeur définie par l'utilisateur ou des valeurs définies + selon le contexte. + + + + + + Cette icône indique une astuce, une suggestion ou + une note d'ordre général. + + + + Cette icône est un message d'alerte ou de prudence. + + + + + Utiliser les exemples de code + + Ce livre est ici pour vous aider dans votre + travail. De manière générale, vous pouvez donc utiliser le code + de ce livre dans vos programmes et votre documentation. Vous + n'avez pas à nous contacter pour nous demander la permission + de le faire, à moins que vous ne reproduisiez une partie significative + du code. Par exemple, écrire un programme qui utilise plusieurs + extraits de code du livre ne demande aucune autorisation particulière. + Vendre ou distribuer un CD-ROM provenant des livres O'Reilly demande + à l'inverse une autorisation. Répondre à une question en citant ce + livre ou ses exemples de code ne demande aucune autorisation préalable. + Intégrer une grande quantité des codes d'exemples de ce livre dans + votre propre ouvrage demande une autorisation de notre part. + + Nous apprécions, sans l'exiger, que vous citiez + l'ouvrage dans vos écrits l'utilisant, en indiquant le titre, + l'auteur, l'éditeur et son ISBN. Par exemple: “Titre du + livre par Son Auteur. Copyright 2008 O’Reilly Media, Inc., + 978-0-596-xxxx-x.” + + Si vous estimez que votre usage des exemples de code + dépasse le cadre défini ci dessus, n'hésitez pas à nous contacter : + permissions@oreilly.com. + + + + Safari® Books Online + + + Quand vous voyez l'icône de Safari® Books Online + sur la couverture d'un de vos livres techniques préférés, cela signifie + que le livre est disponible, en ligne, à travers le O’Reilly Network Safari + Bookshelf. + + + Safari offre une solution qui est meilleure que + les e-books. C'est une bibliothèque virtuelle qui vous laisse + aisément rechercher dans des milliers de livres, mais aussi + copier-coller leurs exemples, télécharger des chapitres, et + trouver des réponses rapides quand vous avez besoin d'une + information précise et à jour. Essayez le gratuitement : + http://my.safaribooksonline.com. + + + + Comment nous contacter + + Merci d'adresser vos commentaires et vos questions + sur ce livre à son éditeur: + + + O’Reilly Media, Inc. + + 1005 Gravenstein Highway North + + Sebastopol, CA 95472 + + 800-998-9938 (in the United States or Canada) + + 707-829-0515 (international or local) + + 707 829-0104 (fax) + + + Nous avons une page web pour cet ouvrage, où nous + publions des errata, des exemples, et encore d'autres informations + additionnelles. Vous pouvez accéder à cette page par l'URL suivante: + + + + + + + N'oubliez pas de mettre à jour l'attribut <url> aussi. + + Pour commenter ou poser des questions techniques + sur cet ouvrage, envoyez un email à : + + + bookquestions@oreilly.com + + + Pour plus d'informations sur nos livres, nos + conférences, nos centres d'informations, et le réseau O’Reilly, + voyez notre site web : + + + + + + + + + + + + + + + Comment en est on arrivé là ? + + +À propos de la gestion source + + La gestion de sources est un processus permettant de gérer différentes +versions de la même information. Dans sa forme la plus simple, c'est +ce que tout le monde fait manuellement : quand vous modifiez +un fichier, vous le sauvegardez sous un nouveau nom contenant un numéro, +à chaque fois plus grand que celui de la version précédente. + + Ce genre de gestion de version manuelle est cependant facilement sujette +aux erreurs, ainsi, depuis longtemps, des logiciels existent pour +résoudre cette problématique. Les premiers outils de gestion de sources +étaient destinés à aider un seul utilisateur, à automatiser la gestion +des versions d'un seul fichier. Dans les dernières décades, cette cible +s'est largement agrandie, ils gèrent désormais de multiples fichiers, et +aident un grand nombre de personnes à travailler ensemble. Les outils les +plus modernes n'ont aucune difficulté à gérer plusieurs milliers de +personnes travaillant ensemble sur des projets regroupant plusieurs +centaines de milliers de fichiers. + + L'arrivée de la gestion de révision distribuée est + relativement récente, et, pour le moment, ce nouveau domaine a grandi + grâce à la volonté des gens d'explorer ces territoires encore inconnus. + + + J'écris un livre sur la gestion de révision distribuée + parce que je pense qu'il s'agit d'un sujet important qui mérite un guide + du terrain. J'ai choisi d'écrire un livre sur Mercurial car il est + l'outil le plus facile pour découvrir ce nouveau domaine, tout en étant + un outil efficace qui répond aux demandes d'environnements réels et + difficiles, là où d'autres outils de gestions de versions s'effondrent. + + + Pourquoi utiliser un gestionnaire de source ? + + Il y a de nombreuses raisons pour que vous ou votre équipe souhaitiez +utiliser un outil automatisant la gestion de version pour votre projet. + + + L'outil se chargera de suivre l'évolution de votre projet, sans +que vous ayez à le faire. Pour chaque modification, vous aurez à votre +disposition un journal indiquant qui a fait quoi, pourquoi +il l'a fait, quand il l'a fait, et +ce qu'il a modifié. + +Quand vous travaillez avec d'autres personnes, les logiciels de +gestion de source facilitent le travail collaboratif. Par exemple, quand +plusieurs personnes font, plus ou moins simultanément, des modifications +incompatibles, le logiciel vous aidera à identifier et à résoudre les conflits. + +L'outil vous aidera à réparer vos erreurs. Si vous effectuez un changement +qui se révèle être une erreur, vous pourrez revenir à une version +antérieure d'un fichier ou même d'un ensemble de fichiers. En fait, un outil de +gestion de source vraiment efficace vous permettra d'identifier à quel +moment le problème est apparu (voir la section pour plus +de détails). + +L'outil vous permettra aussi de travailler sur plusieurs versions différentes +de votre projet et de gérer l'écart entre chacune. + +La plupart de ces raisons ont autant d'importances —du + moins en théorie— que vous travailliez sur un projet pour vous, ou + avec une centaine d'autres personnes. + + +Une question fondamentale à propos des outils de gestion de + source, qu'il s'agisse du projet d'une personne ou d'une grande équipe, est + quels sont ses avantages par rapport à ses + coûts. Un outil qui est difficile à utiliser ou à + comprendre exigera un lourd effort d'adaptation. + + +)Un projet de cinq milles personnes s'effondrera très + certainement de lui même sans aucun processus et outil de gestion de + source. Dans ce cas, le coût d'utilisation d'un logiciel de gestion de + source est dérisoire puisque sans, l'échec est presque + garanti. + + +D'un autre coté, un rapide hack d'une personne + peut sembler un contexte bien pauvre pour utiliser un outil de gestion de + source, car, bien évidement le coût d'utilisation dépasse le coût total du + projet. N'est ce pas ? + + + Mercurial supporte ces deux + échelles de travail. Vous pouvez apprendre les bases en quelques + minutes seulement, et, grâce à sa performance, vous pouvez l'utiliser + avec facilité sur le plus petit des projets. Cette simplicité + signifie que vous n'avez pas de concept obscurs ou de séquence de + commandes défiant l'imagination, sans aucune corrélation avec + ce que vous êtes entrain de faire. En même + temps, ces mêmes performances et sa nature + peer-to-peer vous permettent d'adapter, sans + difficulté, son utilisation à de très grands projets. + + + Aucun outil de gestion de source ne peut sauver un + projet mal mené, mais un bon outil peut rendre beaucoup plus fluide + votre travail. + + + + + + Les multiples noms de la gestion de source + + La gestion de source + + est un domaine tellement large qu'il n'existe pas qu'un seul nom ou + acronyme pour le désigner. Voici quelques noms ou acronymes que vous + rencontrerez le plus souvent. + + + +: + + + + Revision control (RCS) + Software configuration management (SCM), ou + configuration management + Source code management + Source code control, ou source control + Version control (VCS) + + Certaines personnes prétendent que ces termes ont en fait + des sens différents mais en pratique ils se recouvrent tellement qu'il n'y + a pas réellement de manière pertinente de les distinguer. + + + + + + +A propos des exemples dans ce livre + + Ce livre prend une approche non usuel pour les exemples + de code. Tous les exemples sont en live — Chacun + est actuellement le résultat d'un script shell qui exécute les + commandes Mercurial que vous voyez. A chaque fois qu'une image du livre + est construite à partir des sources, tous les scripts d'exemple sont + lancés automatiquement, et leurs résultats effectifs sont comparés aux + résultats attendus. + + L'avantage de dette approche est que les exemples sont + toujours précis ; ils décrivent exactement la + conduite de la version de Mercurial qui est mentionnée en entête du + livre. Si je met à jour la version de Mercurial que je suis en train de + documenter, et que la sortie de certaines commandes change, la + construction du livre échoue. + + + Il existe un petit désavantage à cette approche qui est que les dates et + heures que vous verrez dans les exemples tendent à être + écrasés ensemble, dans le sens où elles ne sont pas + celles qu'elles auraient été si un humain avait tapé les commandes. En + effet, humain ne peut pas taper plus d'une commande toutes les quelques + secondes, avec le temps qui s'écoule, mes scripts d'exemples exécutent + plusieurs commandes en une seconde. + + + Une circonstance de ceci est que plusieurs commits + consécutifs dans un exemple peuvent apparaître comme ayant eu lieu + durant la même seconde. + Vous pouvez observer le phénomène dans l'exemple bisect dans + + + Donc, lorsque vous lisez ces exemples, ne prêtez pas trop + d'importance aux dates et heures que vous voyez dans la sortie des + commandes. Cependant, soyez confiants que le + comportement que vous voyez est consistent et reproductible + + + + + + + + Tendances de la gestion de source + + Il y a eu une tendance évidente dans le développement et + l'utilisation d'outils de gestion de source depuis les quatre dernières + décades, au fur et à mesure que les utilisateurs se sont habitués à + leur outils et se sont sentis contraints par leurs limitations. + + + La première génération commença simplement par gérer un + fichier unique sur un ordinateur individuel. Cependant, même si ces + outils présentaient une grande avancée par rapport à la gestion + manuelle des versions, leur modèle de verrouillage et leur utilisation + limitée à un seul ordinateur rendaient leur utilisation possible + uniquement dans une très petite équipe. + + + La seconde génération a assoupli ces contraintes en + adoptant une architecture réseau et centralisée, permettant de gérer + plusieurs projets entiers en même temps. Alors que les projets + grandirent en taille, ils rencontrèrent de nouveaux problèmes. Avec les + clients discutant régulièrement avec le serveurs, la montée en charge + devint un réel problème sur les gros projets. Une connexion réseau peu + fiable pouvait complètement empêcher les utilisateurs distants de + dialoguer avec le serveur. Alors que les projets Open Source commencèrent à mettre en place des + accès en lecture seule disponible anonymement, les utilisateurs sans + les privilèges de commit réalisèrent qu'ils ne pouvaient + pas utiliser les outils pour collaborer naturellement avec le projet, + comme ils ne pouvaient pas non plus enregistrer leurs modifications. + + + La génération actuelle des outils de gestion de source + est peer-to-peer par nature. Tous ces systèmes ont + abandonné la dépendance à un serveur central, et ont permis à leur + utilisateur de distribuer les données de leur gestion de source à qui + en a besoin. La collaboration à travers Internet a transformé la + contrainte technologique en une simple question de choix et de + consensus. Les outils modernes peuvent maintenant fonctionner en mode + déconnecté sans limite et de manière autonome, la connexion au réseau + n'étant nécessaire que pour synchroniser les modifications avec les + autres dépôts. + + + + + Quelques avantages des gestionnaires de source distribués + + Même si les gestionnaire de source distribués sont depuis + plusieurs années assez robustes et aussi utilisables que leurs + prédécesseurs, les utilisateurs d'autres outils n'y ont pas encore été + sensibilisés. Les gestionnaires de source distribués se distinguent + particulièrement de leurs équivalents centralisés de nombreuses + manières. + + + Pour un développeur individuel, ils restent beaucoup plus + rapides que les outils centralisés. Cela pour une raison simple : un + outil centralisé doit toujours dialoguer à travers le réseau pour la + plupart des opérations, car presque toutes les métadonnées sont + stockées sur la seule copie du serveur central. Un outil distribué + stocke toute ses métadonnées localement. À tâche égale, effectuer un + échange avec le réseau ajoute un délai aux outils centralisés. Ne + sous-estimez pas la valeur d'un outil rapide : vous allez passer + beaucoup de temps à interagir avec un logiciel de gestion de source. + + + Les outils distribués sont complètement indépendants des + aléas de votre serveur, d'autant plus qu'ils répliquent les métadonnées + à beaucoup d'endroits. Si votre serveur central prend feu, vous avez + intérêt à ce que les médias de sauvegardes soient fiables, et que votre + dernier backup soit récent et fonctionne sans problème. + Avec un outil distribué, vous avez autant de backup que + de contributeurs. + + + En outre, la fiabilité de votre réseau affectera beaucoup + moins les outils distribués. Vous ne pouvez même pas utiliser un outil + centralisé sans connexion réseau, à l'exception de quelques commandes, + très limitées. Avec un outil distribué, si votre connexion réseau tombe + pendant que vous travaillez, vous pouvez ne même pas vous en rendre + compte. La seule chose que vous ne serez pas capable de faire sera de + communiquer avec des dépôts distants, opération somme toute assez rare + en comparaison aux opérations locales. Si vous avez une équipe de + collaborateurs très dispersée ceci peut être significatif. + + + + Avantages pour les projets Open Source + + Si vous prenez goût à un projet Open Source et que vous décidez de commencer + à toucher à son code, et que le projet utilise un gestionnaire de + source distribué, vous êtes immédiatement un "pair" avec les + personnes formant le cœur du projet. S'ils publient + leurs dépôts, vous pouvez immédiatement copier leurs historiques de + projet, faire des modifications, enregistrer votre travail en + utilisant les mêmes outils qu'eux. Par comparaison avec un outil + centralisé, vous devez utiliser un logiciel en mode lecture + seule à moins que quelqu'un ne vous donne les privilèges de + commit sur le serveur central. Avant ça, vous ne serez + pas capable d'enregistrer vos modifications, et vos propres + modifications risqueront de se corrompre chaque fois que vous + essayerez de mettre à jour à votre espace de travail avec le serveur + central. + + + + Le non-problème du "fork" + + Il a été souvent suggéré que les gestionnaires de + source distribués posent un risque pour les projets Open Source car ils facilitent grandement la + création de fork. + + Un fork apparait quand il y des divergences d'opinion + ou d'attitude au sein d'un groupe de développeurs qui aboutissent à + la décision de ne plus travailler ensemble. Chaque parti s'empare + d'une copie plus ou moins complète du code source du projet et + continue dans sa propre direction. + + + + Parfois ces différents partis décident de se + réconcilier. Avec un serveur central, l'aspect + technique de cette réconciliation est un + processus douloureux, et essentiellement manuel. Vous devez décider + quelle modification est la gagnante, et replacer, par + un moyen ou un autre, les modifications de l'autre équipe dans + l'arborescence du projet. Ceci implique généralement la perte d'une + partie de l'historique d'un des partis, ou même des deux. + + + Ce que les outils distribués permettent à ce sujet est + probablement la meilleure façon de développer un + projet. Chaque modification que vous effectuez est potentiellement un + fork. La grande force de cette approche est que les + gestionnaires de source distribués doivent être vraiment très + efficaces pour fusionner (merge) + + des forks, car les forks, dans ce + contexte, arrivent tout le temps. + + + Si chaque altération que n'importe qui effectue, à tout + moment, est vue comme un fork à fusionner, alors ce + que le monde de l'Open Source voit + comme un fork devient uniquement + une problématique sociale. En fait, les outils de gestions de source + distribués réduisent les chances de + fork : + + + + + Ils éliminent la distinction sociale qu'imposent les outils + centralisés entre les membres du projets (ceux qui ont accès au + commit) et ceux de l'extérieur (ce qui ne l'ont + pas). + + Ils rendent plus facile la réconciliation après un + fork social, car tout ce qu'elle implique est une + simple fusion. + + + + + Certaines personnes font de la résistance envers les + gestionnaires de source distribués parce qu'ils veulent garder un + contrôle ferme sur leur projet, et ils pensent que les outils + centralisés leur fournissent ce contrôle. Néanmoins, si c'est votre + cas, sachez que si vous publiez votre dépôt CVS ou Subversion de + manière publique, il existe une quantité d'outils disponibles pour + récupérer entièrement votre projet et son historique (quoique + lentement) et le récréer ailleurs, sans votre contrôle. En fait, + votre contrôle sur votre projet est illusoire, vous ne faites + qu'interdire à vos collaborateurs de travailler de manière fluide, en + disposant d'un miroir ou d'un fork de votre + historique. + + + + + + Avantages pour les projets commerciaux + + Beaucoup de projets commerciaux sont réalisés par des + équipes éparpillées à travers le globe. Les contributeurs qui sont + loin du serveur central devront subir des commandes lentes et même + parfois peu fiables. Les solutions propriétaires de gestion de source + tentent de palier ce problème avec des réplications de sites distants + qui sont à la fois coûteuses à mettre en place et lourdes à + administrer. Un système distribué ne souffre pas de ce genre de + problèmes. En outre, il est très aisé de mettre en place plusieurs + serveurs de références, disons un par site, de manière à ce qu'il n'y + ait pas de communication redondante entre les dépôts, sur une + connexion longue distance souvent onéreuse. + + + Les systèmes de gestion de source supportent + généralement assez mal la monté en charge. Il n'est pas rare pour un + gestionnaire de source centralisé pourtant onéreux de s'effondrer + sous la charge combinée d'une douzaine d'utilisateurs concurrents + seulement. Une fois encore, la réponse à cette problématique est + généralement encore la mise en place d'un ensemble complexe de + serveurs synchronisés par un mécanisme de réplication. Dans le cas + d'un gestionnaire de source distribué, la charge du serveur central + — si vous avez un— est plusieurs fois inférieure (car + toutes les données sont déjà répliquées ailleurs), un simple serveur, + pas très cher, peut gérer les besoins d'une plus grande équipe, et la + réplication pour balancer la charge devient le travail d'un simple + script. + + + Si vous avez des employés sur le terrain, en train de + chercher à résoudre un souci sur le site d'un client, ils + bénéficieront aussi d'un gestionnaire de source distribué. Cet outil + leur permettra de générer des versions personnalisées, d'essayer + différentes solutions, en les isolant aisément les unes des autres, + et de rechercher efficacement à travers l'historique des sources, la + cause des bugs ou des régressions, tout ceci sans avoir besoin de la + moindre connexion au réseau de votre compagnie. + + + + + + Pourquoi choisir Mercurial? + + Mercurial a plusieurs caractéristiques qui en font un + choix particulièrement pertinent pour la gestion de source : + + + Il est simple à apprendre et à utiliser. + Il est léger. + Il s'adapte très bien à la charge. + Il se personnalise facilement. + + + Si vous êtes déjà familier d'un outil de gestion de + source, vous serez capable de l'utiliser en moins de 5 minutes. Sinon, + ça ne sera pas beaucoup plus long. Les commandes utilisées par + Mercurial, comme ses fonctionnalités, sont généralement uniformes et + cohérentes, et vous pouvez ainsi garder en tête simplement quelques + règles générales, plutôt qu'un lot complexe d'exceptions. + + + Sur un petit projet, vous pouvez commencer à travailler + avec Mercurial en quelques instants. Ajouter des modifications ou des + branches, transférer ces modifications (localement ou via le réseau), + et les opérations d'historique ou de statut sont aussi très rapides. + Mercurial reste hors de votre chemin grâce à sa simplicité + d'utilisation et sa rapidité d'exécution. + + + L'utilité de Mercurial ne se limite pas à de petits + projets: il est aussi utilisé par des projets ayant des centaines ou + même des milliers de contributeurs, avec plusieurs dizaines de milliers + de fichiers, et des centaines de méga octets de code source. + + + Si les fonctionnalités au cœur de Mercurial ne sont pas + suffisantes pour vous, il est très aisé d'en construire d'autres. + Mercurial est adapté à l'utilisation de scripts, et son implémentation + interne en Python, propre et claire, rend encore plus facile l'ajout de + fonctionnalités sous forme d'extensions. Il en existe déjà un certain + nombre de très populaires et très utiles, dont le périmètre va de la + recherche de bugs à l'amélioration des performances. + + + + + Mercurial comparé aux autres outils + + Avant que vous n'alliez plus loin, comprenez bien que + cette section reflète mes propres expériences, et elle est donc (j'ose + le dire) peu objective. Néanmoins, j'ai utilisé les outils de gestion + de source listés ci dessous, dans la plupart des cas, pendant plusieurs + années. + + + + Subversion + + Subversion est un des outils de gestion de source les + plus populaire, il fût développé pour remplacer CVS. Il a une + architecture client/server centralisée. + + + Subversion et Mercurial ont des noms de commandes très + similaires pour les mêmes opérations, ainsi si vous êtes familier + avec l'un, c'est facile d'apprendre l'autre. Ces deux outils sont + portables sur les systèmes d'exploitation les plus populaires. + + + Avant la version 1.5, Subversion n'offrait aucune forme + de support pour les fusions. Lors de l'écriture de ce livre, ses + capacités de fusion étaient nouvelles, et réputées pour être + complexes et buguées. + + + Mercurial dispose d'un avantage substantiel en terme de + performance par rapport à Subversion sur la plupart des opérations + que j'ai pu tester. J'ai mesuré une différence de performance allant + de deux à six fois plus rapide avec le système de stockage de fichier + local de Subversion 1.4.3 (ra_local), qui est la + méthode d'accès la plus rapide disponible. Dans un déploiement plus + réaliste, impliquant un stockage réseau, Subversion serait encore + plus désavantagé. Parce que la plupart des commandes Subversion + doivent communiquer avec le serveur et que Subversion n'a pas de + mécanisme de réplication, la capacité du serveur et la bande passante + sont devenues des goulots d'étranglement pour les projets de taille + moyenne ou grande. + + + En outre, Subversion implique une surcharge + substantielle dans le stockage local de certaines données, pour + éviter des transactions avec le serveur, pour certaines opérations + communes, telles que la recherche des fichiers modifiés + (status) et l'affichage des modifications par + rapport à la révision courante (diff). En + conséquence, un répertoire de travail Subversion a souvent la même + taille, ou est plus grand, qu'un dépôt Mercurial et son espace de + travail, et ceci bien que le dépôt Mercurial contienne l'intégralité + de l'historique. + + + Subversion est largement supporté par les outils + tierces. Mercurial est actuellement encore en retrait de ce point de + vue. L'écart se réduit néanmoins, en effet, certains des outils + graphiques sont maintenant supérieurs à leurs équivalents Subversion. + Comme Mercurial, Subversion dispose d'un excellent manuel + utilisateur. + + + Parce que Subversion ne stocke pas l'historique chez + ses clients, il est parfaitement adapté à la gestion de projets qui + doivent suivre un ensemble de larges fichiers binaires et opaques. Si + vous suivez une cinquantaine de versions d'un fichier incompressible + de 10MB, l'occupation disque coté client d'un projet sous Subversion + restera à peu près constante. A l'inverse, l'occupation disque du + même projet sous n'importe lequel des gestionnaires de source + distribués grandira rapidement, proportionnellement aux nombres de + versions, car les différences entre chaque révisions seront très + grandes. + + + En outre, c'est souvent difficile ou, généralement, + impossible de fusionner des différences dans un fichier binaire. La + capacité de Subversion de verrouiller des fichiers, pour permettre à + l'utilisateur d'être le seul à le mettre à jour + (commit) temporairement, est un avantage significatif + dans un projet doté de beaucoup de fichiers binaires. + + + Mercurial peut importer l'historique depuis un dépôt + Subversion. Il peut aussi exporter l'ensemble des révisions d'un + projet vers un dépôt Subversion. Ceci rend très facile de + prendre la température et d'utiliser Mercurial et + Subversion en parallèle, avant de décider de migrer vers Mercurial. + La conversion de l'historique est incrémentale, donc vous pouvez + effectuer une conversion initiale, puis de petites additions par la + suite pour ajouter les nouvelle modifications. + + + + + + Git + + Git est un outil de gestion de source distribué qui fût + développé pour gérer le code source de noyau de Linux. Comme + Mercurial, sa conception initiale a été inspirée par Monotone. + + + Git dispose d'un ensemble conséquent de commandes, avec + plus de 139 commandes individuelles pour la version 1.5.0. Il a aussi + la réputation d'être difficile à apprendre. Comparé à Git, le point + fort de Mercurial est clairement sa simplicité. + + + En terme de performance, Git est extrêmement rapide. + Dans la plupart des cas, il est plus rapide que Mercurial, tout du + moins sur Linux, alors que Mercurial peut être plus performant sur + d'autres opérations. Néanmoins, sur Windows, les performances et le + niveau de support général fourni par Git, au moment de l'écriture de + cet ouvrage, est bien derrière celui de Mercurial. + + + Alors que le dépôt Mercurial ne demande aucune + maintenance, un dépôt Git exige d'exécuter manuellement et + régulièrement la commande repacks sur ses métadonnées. + Sans ceci, les performances de git se dégradent et la consommation de + l'espace disque augmente rapidement. Un serveur qui contient + plusieurs dépôts Git qui ne sont pas régulièrement et fréquemment + repacked deviendra un vrai problème lors des + backups du disque, et il y eu des cas, où un + backup journalier pouvait durer plus de 24 heures. Un + dépôt fraichement repacked sera légèrement plus petit + qu'un dépôt Mercurial, mais un dépôt non repacked est + beaucoup plus grand. + + + Le cœur de Git est écrit en C. La plupart des commandes + Git sont implémentées sous forme de scripts Shell ou Perl, et la + qualité de ces scripts varie grandement. J'ai plusieurs fois constaté + que certains de ces scripts étaient chargés en mémoire aveuglément et + que la présence d'erreurs pouvait s'avérer fatal. + + + Mercurial peut importer l'historique d'un dépôt Git. + + + + CVS + + CVS est probablement l'outil de gestion de source le + plus utilisé aujourd'hui dans le monde. À cause de son manque de + clarté interne, il n'est plus maintenu depuis plusieurs années. + + + Il a une architecture client/serveur centralisée. Il ne + regroupe pas les modifications de fichiers dans une opération de + commit atomique, ce qui permet à ses utilisateurs de + casser le build assez facilement + : une personne peut effectuer une opération de commit + sans problème puis être bloquée par besoin de fusion, avec comme + conséquence néfaste, que les autres utilisateurs ne récupèreront + qu'une partie de ses modifications. Ce problème affecte aussi la + manière de travailler avec l'historique du projet. Si vous voulez + voir toutes les modifications d'une personne du projet, vous devrez + injecter manuellement les descriptions et les timestamps des modifications de chacun des + fichiers impliqués (si vous savez au moins quels sont ces fichiers). + + + CVS a une notion étrange des tags et des branches que je n'essayerai même + pas de décrire ici. Il ne supporte pas bien les opérations de + renommage d'un fichier ou d'un répertoire, ce qui facilite la + corruption de son dépôt. Il n'a presque pas pour ainsi dire de + contrôle de cohérence interne, il est donc pratiquement impossible de + dire si un dépôt est corrompu ni à quel point. Je ne recommanderai + pas CVS pour un projet existant ou nouveau. + + + Mercurial peut importer l'historique d'un projet CVS. + Néanmoins, il y a quelques principes à respecter; ce qui est vrai + aussi pour les autres outils d'import de projet CVS. À cause de + l'absence de commit atomique et gestion de version de + l'arborescence, il n'est pas possible de reconstruire de manière + précise l'ensemble de l'historique. Un travail de + devinette est donc nécessaire, et les fichiers + renommés ne sont pas détectés. Parce qu'une bonne part de + l'administration d'un dépôt CVS est effectuée manuellement, et est + donc, sujette à erreur, il est courant que les imports CVS + rencontrent de nombreux problèmes avec les dépôt corrompus (des + timestamps de révision complètement + buggés et des fichiers verrouillés depuis des années sont deux des + problèmes les moins intéressants dont je me souvienne). + + + Mercurial peut importer l'historique depuis un dépôt CVS. + + + + + + Outils propriétaires + + Perforce a une architecture client/serveur centralisée, + sans aucun mécanisme de mise en cache de données coté client. + Contrairement à la plupart des outils modernes de gestion de source, + Perforce exige de ses utilisateurs d'exécuter une commande pour + informer le serveur central de tout fichier qu'ils souhaitent + modifier. + + + Les performances de Perforce sont plutôt bonnes pour + des petites équipes, mais elles s'effondrent rapidement lorsque le + nombre d'utilisateurs augmente au delà de la douzaine. Des + installations de Perforce assez larges nécessitent le déploiement de + proxies pour supporter la montée en charge associée. + + + + + Choisir un outil de gestion de source + + A l'exception de CVS, tous les outils listés ci-dessus + ont des forces qui leur sont propres et qui correspondent à certaines + formes de projet. Il n'y a pas un seul meilleur outil de gestion de + source qui correspondrait le mieux à toutes les situations. + + + En guise exemple, Subversion est un très bon choix + lorsqu'on travaille avec beaucoup de fichiers binaires, qui évoluent + régulièrement, grâce à sa nature centralisée et sa capacité à + verrouiller des fichiers. + + + Personnellement, je préfère Mercurial pour sa + simplicité, ses performances et sa bonne capacité de fusion, et il + m'a très bien rendu service de plusieurs années maintenant. + + + + + + Migrer depuis un outil à Mercurial + + Mercurial est livré avec une extension nommée convert, qui peut, de manière incrémentale + importer des révisions depuis différents autres outils de gestion de + source. Par incrémental, j'entends que vous pouvez + convertir l'historique entier du projet en une seule fois, puis + relancer l'outil d'import plus tard pour obtenir les modifications + effectuées depuis votre import initial. + + + Les outils de gestion de source supportés par convert sont : + + + Subversion + CVS + Git + Darcs + + + En outre, convert peut + exporter les modifications depuis Mercurial vers Subversion. Ceci rend + possible d'essayer Subversion en parallèle avant de choisir une + solution définitive, sans aucun risque de perte de données. + + + La commande convert est très simple à utiliser. + Simplement, indiquez le chemin ou l'URL du dépôt de source, en lui + indiquant éventuellement le nom du chemin de destination, et la + conversion se met en route. Après cet import initial, il suffit de + relancer la commande encore une fois pour importer les modifications + effectuées depuis. + + + + + Une courte histoire de la gestion de source + + Le plus célèbre des anciens outils de gestion de source + est SCCS (Source Code Control System)}, + que Marc Rochkind conçu dans les laboratoires de recherche de Bell + (Bell Labs), dans le début des années + 70. SCCS ne fonctionnait que sur des + fichiers individuels, et obligeait chaque personne travaillant sur le + projet d'avoir un accès à un répertoire de travail commun, sur le même + système. Seulement une seule personne pouvait modifier un fichier au + même moment, ce fonctionnement était assuré par l'utilisation de verrou + (lock). Il était courant que des personnes verrouillent + des fichiers, et plus tard, oublient de le déverrouiller ; empêchant + n'importe qui d'autre de travailler sur ces fichiers sans l'aide de + l'administrateur... + + + Walter Tichy a développé une alternative libre à + SCCS au début des années 80, qu'il + nomma RCS (Revision Control System). + Comme SCCS, RCS demandait aux développeurs de travailler + sur le même répertoire partagé, et de verrouiller les fichiers pour se + prémunir de tout conflit issu de modifications concurrentes. + + + Un peu plus tard dans les années 1980, Dick Grune utilisa + RCS comme une brique de base pour un + ensemble de scripts shell qu'il + intitula cmt, avant de la renommer en CVS + (Concurrent Versions System). La grande innovation de CVS + était que les développeurs pouvaient travailler simultanément et + indépendamment dans leur propre espace de travail. Ces espaces de + travail privés assuraient que les développeurs ne se marchent pas + mutuellement sur les pieds, comme c'était souvent le cas avec RCS et + SCCS. Tous les développeurs disposaient donc de leur copie de tous les + fichiers du projet, et ils pouvaient donc librement les modifier. Ils + devaient néanmoins effectuer la fusion (merge) de leurs fichiers, avant + d'effectuer le commit de leurs modifications sur le dépôt + central. + + + Brian Berliner reprit les scripts de Grune's et les réécrit en C, + qu'il publia en 1989. Depuis, ce code a été modifié jusqu'à devenir la + version moderne de CVS. CVS a acquis ainsi la capacité de fonctionner + en réseau, transformant son architecture en client/serveur. + L'architecture de CVS est centralisée, seul le serveur a une copie de + l'historique du projet. L'espace de travail client ne contient qu'une + copie de la dernière version du projet, et quelques métadonnées pour + indiquer où le serveur se trouve. CVS a été un grand succès, + aujourd'hui il est probablement l'outil de gestion de contrôle le plus + utilisé au monde. + + + Au début des années 1990, Sun Microsystems développa un premier + outil de gestion de source distribué, nommé TeamWare. Un espace de + travail TeamWare contient une copie complète de l'historique du projet. + TeamWare n'a pas de notion de dépôt central. (CVS utilisait RCS pour le + stockage de l'historique, TeamWare utilisait SCCS). + + + Alors que les années 1990 avançaient, les utilisateurs ont pris + conscience d'un certain nombre de problèmes avec CVS. Il enregistrait + simultanément des modifications sur différents fichiers + individuellement, au lieu de les regrouper dans une seule opération + cohérente et atomique. Il ne gère pas bien sa hiérarchie de fichier, il + est donc assez aisé de créer le chaos en renommant les fichiers et les + répertoires. Pire encore, son code source est difficile à lire et à + maintenir, ce qui agrandit largement le niveau de + souffrance associé à la réparation de ces problèmes + d'architecture de manière prohibitive. + + + En 2001, Jim Blandy et Karl Fogel, deux développeurs qui avaient + travaillé sur CVS, initièrent un projet pour le remplacer par un outil + qui aurait une meilleure architecture et un code plus propre. Le + résultat, Subversion, ne quitte pas le modèle centralisé et + client/server de CVS, mais ajoute les opérations de + commit atomique sur de multiples fichiers, une meilleure + gestion des espaces de noms, et d'autres fonctionnalités qui en font un + meilleur outil que CVS. Depuis sa première publication, il est + rapidement devenu très populaire. + + + Plus ou moins simultanément, Graydon Hoare a commencé sur + l'ambitieux système de gestion distribué Monotone. Bien que Monotone + corrige plusieurs défauts de CVS tout en offrant une architecture + peer-to-peer, il va aussi plus loin que la plupart des + outils de révision de manière assez innovante. Il utilise des + hashs cryptographiques comme identifiants, et il a une + notion complète de confiance du code issu des + différentes sources. + + + Mercurial est né en 2005. Bien que très influencé par Monotone, + Mercurial se concentre sur la facilité d'utilisation, les performances + et la capacité à monter en charge pour de très gros projets. + + + + + + + + + + + + + + Une rapide présentation de Mercurial : les bases + + + Installer Mercurial sur votre système + + Des paquetages binaires de Mercurial sont disponibles pour la + plupart des systèmes d'exploitation, ce qui rend facile l'utilisation + immédiate de Mercurial sur votre ordinateur. + + + Windows + + La meilleur version de Mercurial pour Windows est + TortoiseHg, qui peut être téléchargée ici : http://bitbucket.org/tortoisehg/stable/wiki/Home. + Ce logiciel n'a aucune dépendance exterieure; il fonctionne et + c'est tout. Il fournit aussi bien les outils en ligne de + commmande qu'une interface graphique. + + + + + Mac OS X + + Lee Cantey publie un installeur de Mercurial pour Mac OS + X sur http://mercurial.berkwood.com. + + + + Linux + + Parce que chaque distribution de Linux a ses propres + outils de gestion de paquets, politiques et rythmes de + développements, il est difficile de donner un ensemble + d'instructions unique pour installer les binaires de Mercurial. La + version de Mercurial avec laquelle vous vous retrouverez dépendra + grandement de l'activité de la personne en charge du paquetage pour + la distribution. + + Pour rester simple, je me concentrerai sur + l'installation de Mercurial en ligne de commande, sous les + distributions les plus courantes. La plupart des distributions + fournissent des gestionnaires graphiques de paquetage qui vous + permettront d'installer Mercurial en quelques clicks. Le paquetage + devrait se nommer mercurial. + + + Ubuntu et Debian: + apt-get install mercurial + Fedora: + yum install mercurial + Gentoo: + emerge mercurial + OpenSUSE: + zypper install + mercurial + + + + + Solaris + + SunFreeWare, à http://www.sunfreeware.com, + fournit des paquets précompilés pour Mercurial. + + + + + Commencer à utiliser Mercurial + + Pour commencer, nous utiliserons la commande hg version pour vérifier si Mercurial est + installé proprement. Les informations affichées sur la version ne sont + pas réellement importantes en soit, c'est surtout de savoir si elles + s'affichent qui nous intéresse. + + +$ hg version +Mercurial Distributed SCM (version 1.2.1) + +Copyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> 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. + + + + + + L'aide intégrée + + Mercurial fournit un système d'aide intégré, ce qui est + inestimable quand vous vous retrouvez coincé à essayer de vous + rappeler comment lancer une commande. Si vous êtes bloqué, exécutez + simplement hg help; elle affichera + une brève liste des commandes, avec une description pour chacune. Si + vous demandez de l'aide sur une commande spécifique (voir + ci-dessous), elle affichera des informations plus détaillées. + + +$ hg help init +hg init [-e CMD] [--remotecmd CMD] [DEST] + +create a new repository in the given directory + + Initialize a new repository in the given directory. If the given + directory does not exist, it is created. + + If no directory is given, the current directory is used. + + It is possible to specify an ssh:// URL as the destination. + See 'hg help urls' for more information. + +options: + + -e --ssh specify ssh command to use + --remotecmd specify hg command to run on the remote side + +use "hg -v help init" to show global options + + + + + Pour un niveau d'informations encore plus détaillé + (ce dont vous aurez rarement besoin), exécuter hg + help . L'option + est l'abréviation de + , et indique à Mercurial + d'ficher plus d'informations que d'habitude. + + + + + Travailler avec un dépôt + + Avec Mercurial, tout se déroule au sein du + dépôt. Le dépôt d'un projet contient tous + les fichiers qui appartiennent au projet. + + Il n'y a rien de particulièrement magique au sujet de + ce dépôt, c'est simplement une arborescence sur votre système de fichiers + que Mercurial traite de manière spéciale. Vous pouvez renommer ou effacer + ce répertoire à n'impporte quel moment, en utilisant la ligne de commande + ou votre explorateur de fichiers. + + + Faire une copie locale de votre dépôt + + Copier un dépôt est juste un + peu spécial. Bien que vous puissiez utiliser une commande habituelle de + copie pour copier votre dépôt, il vaut mieux utiliser une commande fournie par + Mercurial. Cette commande est appelée hg clone, + car elle crée une copie identique à un dépôt existant. + + +$ hg clone http://hg.serpentine.com/tutorial/hello +destination directory: hello +requesting all changes +adding changesets +adding manifests +adding file changes +added 5 changesets with 5 changes to 2 files +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Un avantage de la commande hg + clone est que, comme nous l'avons vu ci dessus, elle nous + permet de faire de cloner les dépôts à travers le réseau. Un autre + est qu'elle se rappelle d'où a été cloné un dépôt, ce qui est utile + quand on veut mettre à jour le clone. + + Si votre opération de clonage réussit, vous devriez maintenant + avoir un répertoire local appelé hello. + Ce répertoire contiendra quelques fichiers. + + +$ ls -l +total 4 +drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:05 hello +$ ls hello +Makefile hello.c + + + + + Ces fichiers ont le même contenu et historique dans votre dépôt + qu'ils ont dans le dépôt que vous avez cloné. + + Chaque dépôt Mercurial est complet, autonome et + indépendant. Il contient sa propre copie privée des fichiers du + projet et de leur historique. Le clone d'un dépôt se souvient de la + localisation du dépôt à partir duquel il a été clôné, mais il ne + communique pas avec ce dernier, ou un autre, à moins que vous ne lui + demandiez. + + Ce que tout ceci signifie pour le moment est que nous + sommes libres d'expérimenter avec ce dépôt, confiants dans le fait + qu'il s'agit d'un bac à sable qui n'affectera personne + d'autre. + + + + Quel est le contenu d'un dépôt ? + + Prêtons plus attention un instant au contenu d'un dépôt. + Nous voyons qu'il contient un répertoire nommé .hg + . C'est ici que Mercurial conserve toutes ses + métadonnées. + + +$ cd hello +$ ls -a +. .. .hg Makefile hello.c + + + + + Le contenu du répertoire .hg + et ses sous répertoires sont les seuls propres à Mercurial. + Tous les autres fichiers et répertoires dans le dépôt sont à vous, et + vous pouvez en faire ce que vous voulez. + + Pour introduire un peu de terminologie, le répertoire + .hg est un vrai + dépôt, et tous les fichiers et les répertoires qui coexistent avec lui, + sont désignés sous le nom espace de travail. Une + manière facile de se rappeler cette distinction est de retenir que le + dépôt contient l'historique + de votre projet, alors que l'espace de travail + contient un "snapshot" de votre projet à un certain + point de son historique. + + + + + Une promenade dans l'historique + + Une des premières choses que vous aurez envie + de faire avec un nouveau dépôt, sera de comprendre son historique. + La commande hg log vous donne une + vue de l'historique. + + +$ hg log +changeset: 4:2278160e78d4 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:16:53 2008 +0200 +summary: Trim comments. + +changeset: 3:0272e0d5a517 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:08:02 2008 +0200 +summary: Get make to generate the final binary from a .o file. + +changeset: 2:fef857204a0c +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:05:04 2008 +0200 +summary: Introduce a typo into hello.c. + +changeset: 1:82e55d328c8c +user: mpm@selenic.com +date: Fri Aug 26 01:21:28 2005 -0700 +summary: Create a makefile + +changeset: 0:0a04b987be5a +user: mpm@selenic.com +date: Fri Aug 26 01:20:50 2005 -0700 +summary: Create a standard "hello, world" program + + + + + + Par défaut, cette commande affiche à l'écran un bref paragraphe pour chaque + révision enregistrée pour ce projet. Dans la terminologie de Mercurial, nous + appelons chacun de ces évènements enregistrés un changeset, parce + qu'il contient un ensemble de modifications sur plusieurs fichiers. + + La commande hg log affiche + ainsi ces informations : + + + changeset : Ce champ contient + un nombre, séparé par deux points (:), d'une chaine hexadécimale. Il + s'agit en fait d'identifiants d'un changeset. Il y a + deux identifiants car le numéro de la révision est plus court et plus à + facile à saisir qu'une séquence hexadécimale. + + user : L'identité de la personne + qui a créée ce %%% laisser le terme anglais car il sera affiché + changeset. C'est un champ libre de forme, mais la plupart du + temps il contient le nom et l'email de la personne. + + date : La date et l'heure à + laquelle le \textit{changeset} a été créé, ainsi que le fuseau horaire dans + lequelle il a été créé. (La date et l'heure sont locales à ce + \textit{fuseau}, elles indiquent donc quelle date et heure il était + pour la personne qui a créé ce changeset. + + résumé: La première ligne du + message que le créateur a associé à son changeset pour le décrire. + + Certains changesets, comme le premier de la + liste ci-dessus ont un champ tag. Le tag est une autre + façon d'identifier un changeset en lui donnant un nom simple à retenir. + (Le tag nommé tip est spécial : il fait toujours + référence aux derniers changements dans le dépôt.) + + + Par défaut, la commande hg log + n'affiche qu'un résumé, il manque beaucoup de détails. + + La figure fournit une + représentation graphique de l'historique du dépôt hello + , pour rendre plus facile de voir dans quelle direction + l'historique se déroule. Nous reviendrons régulièrement + sur cette représentation dans ce chapitre et ceux qui suivent. + + +
+ Graphical history of the <filename class="directory" moreinfo="none">hello</filename> repository + + + XXX add text + +
+ + + + Changesets, révisions, et collaboration + + Comme l'anglais est réputé pour être un langage maladroit, + et que l'informatique est la source de bien des erreurs de terminologie + (pourquoi utiliser un seul terme quand quatre feront l'affaire ?), la + gestion de version a une variété de mots et de phrases qui veulent dire + la même chose. Si vous discutez d'historique de Mercurial avec d'autres + personnes, vous constaterez que souvent, le mot changeset + est contracté simplement en change ou (à l'écrit) + cset, et même parfois un changeset + révision, abrégé en rev. + + Bien que le mot que vous utilisez pour + désigner le concept de changeset importe peu, l'identifiant + que vous utilisez pour désigner un changeset spécifique + a une grande importance. Rappelez vous que le champ changeset affiché par la + commande hg log identifie un changeset à + la fois avec un numéro de révision et une séquence hexadécimale. + + + Le numéro de révision est seulement + valable dans ce dépôt, + La séquence hexadécimale est un + identifiant permanent, et invariant qui + pourra toujours être associé au changeset exact de chaque + copie de votre dépôt. + + La distinction est importante. Si vous envoyez un email + à quelqu'un en parlant de la révision 33, il est très + probable que sa révision 33 ne sera pas la même + que la votre. La raison de ceci est que le numéro de révision dépend + de l'ordre dans lequel les modifications sont arrivées dans le dépôt, + et il n'y a aucune garantie que les mêmes changements soient arrivés + dans le même ordre dans différents dépôts. Trois modifications + a,b,c peuvent aisément apparaitre dans un dépôt + comme 0,1,2, et dans un autre comme 0,2,1 + . + + Mercurial utilise les numéros de révision uniquement comme des raccourcis + pratiques. Si vous devez discuter d'un \textit{changeset} avec quelqu'un, + ou identifer un \textit{changeset} pour une quelconque raison (par exemple, + un rapport de \textit{bug}), utilisez la séquence hexadécimale. + + + + Afficher une révision spécifique + + Pour réduire la sortie de hg log + à une seule révision, utilisez l'option (ou ). Vous pouvez utiliser + le numéro de révision ou la séquence hexadécimale comme identifiant, et + demander autant de révisions que vous le souhaitez. + + +$ hg log -r 3 +changeset: 3:0272e0d5a517 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:08:02 2008 +0200 +summary: Get make to generate the final binary from a .o file. + +$ hg log -r 0272e0d5a517 +changeset: 3:0272e0d5a517 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:08:02 2008 +0200 +summary: Get make to generate the final binary from a .o file. + +$ hg log -r 1 -r 4 +changeset: 1:82e55d328c8c +user: mpm@selenic.com +date: Fri Aug 26 01:21:28 2005 -0700 +summary: Create a makefile + +changeset: 4:2278160e78d4 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:16:53 2008 +0200 +summary: Trim comments. + + + + + + Si vous voulez voir l'historique de plusieurs révisions + sans avoir à les énumérer, vous pouvez utiliser la intervalle + de numérotation qui vous permet d'exprimer l'idée je + veux toutes les révisions entre $a$ et $b$, inclus + + +$ hg log -r 2:4 +changeset: 2:fef857204a0c +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:05:04 2008 +0200 +summary: Introduce a typo into hello.c. + +changeset: 3:0272e0d5a517 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:08:02 2008 +0200 +summary: Get make to generate the final binary from a .o file. + +changeset: 4:2278160e78d4 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:16:53 2008 +0200 +summary: Trim comments. + + + + + + Mercurial respecte aussi l'ordre dans lequel vous spécifiez + les révisions, ainsi hg log -r 2:4 + affichera 2,3,4 alors que hg + log -r 4:2 affichera 4,3,2. + + + + Informations détaillées + + Le résumé affiché par hg log + est suffisant si vous savez déjà ce que vous cherchez. En + revanche, vous aurez probablement besoin de voir une description + complète du changement, ou une liste des fichiers modifiés si vous + cherchez à déterminer qu'un changeset est bien celui que vous + recherchez. L'option \hgopt{-v} de la commande hg + log (ou ) vous + donne ces informations supplémentaires. + + +$ hg log -v -r 3 +changeset: 3:0272e0d5a517 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:08:02 2008 +0200 +files: Makefile +description: +Get make to generate the final binary from a .o file. + + + + + + + Si vous voulez voir à la fois la description + et le contenu d'une modification, ajouter l'option (ou ). Ceci affiche le contenu d'une modification + comme un diff unifié + + (si vous n'avez jamais vu de diff unifié avant, consultez la + section pour un rapide + survol). + + +$ hg log -v -p -r 2 +changeset: 2:fef857204a0c +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:05:04 2008 +0200 +files: hello.c +description: +Introduce a typo into hello.c. + + +diff -r 82e55d328c8c -r fef857204a0c hello.c +--- a/hello.c Fri Aug 26 01:21:28 2005 -0700 ++++ b/hello.c Sat Aug 16 22:05:04 2008 +0200 +@@ -11,6 +11,6 @@ + + int main(int argc, char **argv) + { +- printf("hello, world!\n"); ++ printf("hello, world!\"); + return 0; + } + + + + + + L'option est + incroyablement utile, il est donc important dans s'en rappeller. + + +
+ + Tout sur les options de commandes + + Avant d'aller plus loin sur le fonctionnement + des commandes de Mercurial, étudions un moment comment elles + fonctionnent de manière générale. Vous trouverez ça probablement + utile pour la suite de notre parcours. + + Mercurial utilise une approche directe et cohérente + pour interpréter les options que vous passez aux commandes. Il suit une + convention commune à la plupart des systèmes Unix et Linux modernes. + + + Chaque option a un nom complet. Par exemple, + comme nous l'avons déjà vu, la commande hg + log accepte l'option . + + La plupart des options disposent de + noms abrégés. Aussi, au lieu d'utiliser , vous pouvez utiliser . + (Les options qui n'ont pas de noms abrégés sont généralement + rarement utilisées). + + Les noms complets commencent par deux + tirets (i.e. ), + alors que les options courtes commencent avec un seul (i.e. + ). + + Les noms des options sont cohérents + entre les commandes. Par exemple, chaque commande qui accepte + un changeset ID ou un numéro de révision accepte aussi et comme arguments. + + + + Dans les exemples de ce livre, j'utilise les noms abrégés + plutôt que les noms complets. Ceci est une préférence personnelle, pas + une recommandation. + + La plupart des commandes qui affichent une quelconque sortie + à l'écran, afficheront davantage avec l'option (ou ), et + moins avec l'option (ou + ). + + + Option naming consistency + + Presque toujours, les commandes de Mercurial utilisent + des noms d'options cohérentes pour référer à des concepts identiques. + Par exemple, si une commande concerne les changesets, vous les + identifierez toujours avec l'option . + Cette utilisation cohérente des noms d'options permet de mémoriser plus + facilement quelles options accepte une commande. + + + + + + Faire et vérifier des modifications + + Maintenant que nous avons une bonne idée des + commandes pour consulter l'historique de Mercurial, regardons + comment faire des modifications et les examiner. + + La première chose que nous allons faire c'est isoler notre + expérience dans un dépôt à part. Nous allons utiliser la commande hg clone, mais nous n'avons pas besoin de faire + une copie de dépôt distant. Comme nous avons déjà une copie locale, nous + pouvons juste faire un clone de celle-ci à la place. C'est beaucoup plus + rapide que de faire une copie à travers le réseau, et un dépôt cloné + localement prend également moins d'espace disque + L'économie d'espace disque apparait clairement quand les + dépôts source et destination sont sur le même système de fichier, où, dans + ce cas, Mercurial utilisera des liens physiques pour effectuer des partages + copie-lors-des-écritures de ses métadonnées internes. Si cette explication + ne signifie rien pour vous, ne vous inquietez pas : tout ceci se passe de + manière transparente et automatiquement. Vous n'avez pas du tout besoin de + comprendre ceci.. + + +$ cd .. +$ hg clone hello my-hello +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd my-hello + + + + + On notera au passage qu'il est souvent considéré comme + une bonne pratique de conserver une copie immaculée + du dépôt distant, à partir de laquelle vous pourrez faire des + copies locales temporaires pour créer des bacs à sable + pour chaque tâche sur laquelle vous souhaitez travailler. Ceci + vous permet de travailler sur plusieurs choses en parallèle, + chacune isolée les unes des autres en attendant que ces tâches + soient finies et que vous soyez prêt à les réintégrer. Parce + que les copies locales sont peu coûteuses, il est très rapide + de créer ou détruire des dépôts dès que vous n'en avez plus + besoin. + + Dans notre dépôt my-hello, nous avons un fichier + hello.c qui contient le classique hello, + world. + + +$ cat hello.c +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("hello, world!\"); + return 0; +} + + + + + Editons ce fichier pour qu'il affiche une autre ligne + sur la sortie standard. + + +# ... edit edit edit ... +$ cat hello.c +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("hello, world!\"); + printf("hello again!\n"); + return 0; +} + + + + + La commande Mercurial hg + status nous dira ce que Mercurial sait des fichiers du + dépôts. + + +$ ls +Makefile hello.c +$ hg status +M hello.c + + + + + La commande hg status + n'affichera pas le contenu des fichiers, mais une ligne commençant par + M pour hello.c. + A moins que vous lui demandiez, la commande hg + status n'affichera aucune information sur les fichiers que + vous n'avez pas modifiés. + + Le M indique que + Mercurial a remarqué que nous avons modifié le fichier + hello.c. Nous n'avons pas besoin + d'informer Mercurial que nous allons modifier un + fichier avant de commencer à le faire, ou que nous avons modifié un + fichier après avoir commencé à le faire, il est capable de découvrir ça + tout seul. + + C'est déjà pratique de savoir que nous avons modifié le + fichier hello.c, mais nous préférerions savoir + exactement ce que nous avons changé. Pour ceci, nous + utilisons la commande hg diff. + + +$ hg diff +diff -r 2278160e78d4 hello.c +--- a/hello.c Sat Aug 16 22:16:53 2008 +0200 ++++ b/hello.c Sun Aug 16 14:05:26 2009 +0000 +@@ -8,5 +8,6 @@ + int main(int argc, char **argv) + { + printf("hello, world!\"); ++ printf("hello again!\n"); + return 0; + } + + + + + + Comprendre les patches + + Penser à jeter un oeil à si vous n'arrivez pas à lire la sortie + ci-dessus. + + + + Enregister vos modifications dans une nouvelle révision + + Nous pouvons modifier des fichiers, compiler et tester + nos modifications, et utiliser les commandes hg + status et hg diff pour + voir les modifications effectuées, jusqu'à ce que nous soyons assez + satisfaits pour décider d'enregistrer notre travail dans un + \textit{changeset}. + + La commande hg commit + vous laisse créer une nouvelle révision, nous désignerons généralement + cette opération par faire un commit ou + committer. + + + Définir le nom d'utilisateur + + Quand vous exécutez la commande hg commit pour la première fois, il n'est + pas garanti qu'elle réussisse du premier coup. En effet, Mercurial + enregistre votre nom et votre adresse avec chaque modification que + vous effectuez, de manière à ce que vous soyez capable (ou d'autres + le soient) de savoir qui a fait quelle modification. Mercurial essaye + automatiquement de découvrir un nom d'utilisateur qui ait un minimum + de sens pour effectuer l'opération de commit avec. Il va essayer + chacune des méthodes suivantes, dans l'ordre : + + + Si vous spécifiez l'option avec la commande hg commit, suivi d'un nom + d'utilisateur, ceci aura toujours la priorité sur les autres + méthodes ci dessous. + Si vous avez défini une variable + d'environnement HGUSER, c'est cette valeur qui est + alors utilisée. + Si vous créez un fichier nommé .hgrc dans votre répertoire + \textit{home}, avec une entrée username, c'est la valeur associée + qui sera utilisée. Pour voir à quoi ressemble le contenu de ce + fichier regardez la section + ci-dessous. + Si vous avez défini une variable + d'environnement EMAIL celle ci sera utilisée + ensuite. + Enfin, Mercurial interrogera votre système + pour trouver votre nom d'utilisateur local ainsi que le nom de la + machine hôte, et il fabriquera un nom d'utilisateur à partir de + ces données. Comme il arrive souvent que ce genre de noms soit + totalement inutile, il vous préviendra en affichant un message + d'avertissement. + + + Si tous ces mécanismes échouent, Mercurial n'exécutera + pas la commande, affichant un message d'erreur. Dans ce cas, il ne + vous laissera pas effectuer de commit tant que vous n'aurez pas + défini un nom d'utilisateur. + + Vous devriez penser à utiliser la variable + d'environement HGUSER et l'option comme moyen pour + changer le nom d'utilisateur par défaut. Pour + une utilisation normale, la manière la plus simple et robuste + d'opérer est de créer un fichier .hgrc, voir ci-dessous pour les détails + à ce sujet. + + + Créer un fichier de configuration pour Mercurial + + Pour définir un nom d'utilisateur, utilisez votre + éditeur de texte favori pour créer un fichier .hgrc dans votre répertoire home. + Mercurial va utiliser ce fichier pour retrouver votre + configuration personnelle. Le contenu initial devrait + ressembler à ceci : + + + <quote>Home directory</quote> sous Windows + + Quand on parle de répertoire home, sur une version + anglaise d'une installation de Windows, il s'agira habituellement + d'un répertoire nommée comme votre nom dans C:\Documents + and Settings. Vous pouvez trouver de quelle répertoire + il s'agit en lançant une fenêtre d'interpréteur de commande et en + exécutant la commande suivante : + + C:\ echo + %UserProfile + + + # This is a Mercurial configuration file. +[ui] +username = Firstname Lastname <email.address@domain.net> + + La ligne avec [ui] commence une + section du fichier de configuration, ainsi la ligne + username = ... signifie + définir la valeur de l'élément username dans la + section ui. Une section continue jusqu'à ce + qu'une nouvelle commence, ou que la fin du fichier soit atteinte. + Mercurial ignore les lignes vides et traite tout texte situé à la suite + d'un # jusqu'à la fin de la ligne + comme un commentaire. + + + + Choisir un nom d'utilisateur + + Vous pouvez utiliser n'importe quelle valeur + pour votre username, car cette information + est destinée à d'autres personnes et non à être interprétée + par Mercurial. La convention que la plupart des personnes + suivent est d'utiliser leurs noms suivies de leurs adresses emails, + comme montré ci-dessus : + + Le mécanisme interne du serveur web intégré à Mercurial, + masque les adresses emails, pour rendre plus difficile leurs + récupérations par les outils utilisés par les spammmers. + Ceci réduit la probabilité que de recevoir encore plus de + spam si vous vous publiez un dépôt sur internet. + + + + + Rédiger un message de \textit{commit} + + Lorsqu'on effectue une opération de commit, Mercurial + lance automatiquement un éditeur de texte pour permettre de saisir + un message qui décrira les modifications effectuées dans cette + révision. Ce message est nommé le message de commit. + Ce sera un enregistrement pour tout lecteur expliquant le pourquoi + et le comment de vos modifications, et il sera affiché par la + commande hg log. + + +$ hg commit + + + + + L'éditeur que la commande hg + commit déclenche ne contiendra qu'une ligne vide suivi + d'un certain nombre de lignes commençant par HG: + . + + + This is where I type my commit comment. + + HG: Enter commit message. Lines beginning with 'HG:' are removed. + HG: -- + HG: user: Bryan O'Sullivan <bos@serpentine.com> + HG: branch 'default' + HG: changed hello.c + + + Mercurial ignore les lignes qui commencent + avec HG:, il ne les + utilise que pour nous indiquer quels fichiers modifiés il se + prépare à \textit{commiter}. Modifier ou effacer ces lignes n'a + aucune conséquence sur l'opération de commit. + + + + + Rédiger un message \textit{approprié} + + Comme hg log n'affiche + que la première ligne du message de commit par défaut, il est souvent + considéré comme une bonne pratique de rédiger des messages de commit + qui tiennent sur une seule ligne. Voilà un exemple concret de message + de commit qui ne suit pas cette directive, et + qui a donc un résumé peu lisible. + + +changeset: 73:584af0e231be +user: Censored Person <censored.person@example.org> +date: Tue Sep 26 21:37:07 2006 -0700 +summary: include buildmeister/commondefs. Add an exports and install + + + A ce sujet, il faut noter qu'il n'existe pas de règle + absolue dans ce domaine. Mercurial lui-même n'interprète pas les + contenus des messages de commit, ainsi votre projet est libre de + concevoir différentes politiques de mise en page des messages. + + Ma préférence personnelle va au message court, mais + informatif, qui offre des précisions supplémentaires par rapport à ce + que pourrait m'apprendre une commande hg log + --patch. + + Si vous exécutez la commande hg + commit sans aucun argument, elle enregistre tous les + changements qui ont été fait, et qui sont indiqué par les commandes + hg status et hg diff. + + + Une surprise pour les utilisateurs habitués à Subversion + + Comme n'importe quel autre commande de Mercurial, si + vous soumettez pas de manière explicite les noms des fichiers à + committer à la commande hg commit, elle + va travailler sur l'ensemble du répertoire de travail. Soyez conscient + de ceci si vous venez du monde Subversion ou CVS, car vous pourriez + attendre qu'elle opère uniquement le répertoire courant et ses sous + répertoires. + + + + Annuler un \textit{commit} + + Si, en rédigeant le message, vous décidez que + finalement vous ne voulez pas effectuer ce commit, il suffit + de quitter simplement l'éditeur sans sauver. Ceci n'aura aucune + conséquence sur le dépôt ou les fichiers du répertoire de + travail. + + + + Admirer votre travail + + Une fois que votre \textit{commit} est terminé, vous + pouvez utiliser la commande hg tip + pour afficher le \textit{changeset} que vous venez de créer. Cette + commande produit une sortie à l'écran qui est identique à celle du + hg log, mais qui n'affiche que la + dernière révision du dépôt. + + +$ hg tip -vp +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +files: hello.c +description: +Added an extra line of output + + +diff -r 2278160e78d4 -r c94f208d1dfb hello.c +--- a/hello.c Sat Aug 16 22:16:53 2008 +0200 ++++ b/hello.c Sun Aug 16 14:05:26 2009 +0000 +@@ -8,5 +8,6 @@ + int main(int argc, char **argv) + { + printf("hello, world!\"); ++ printf("hello again!\n"); + return 0; + } + + + + + + On fait couramment référence à la dernière révision + du dépôt comme étant la révision tip, ou plus + simplement le tip. + + Au passage, la commande hg + tip accepte la plupart des options qu'accepte + hg log. Ainsi ci dessus implique soit + verbeux, + veux dire affiche le patch. L'utilisation de l'option + pour afficher un patch est un + autre exemple de la cohérence des commandes évoquée plus tôt. + + + + + Partager ses modifications + + Nous avons mentionné plus haut que les dépôts + de Mercurial sont autosuffisants. Ce qui signifie que la nouvelle + révision que vous venez de créer existe seulement dans votre + répertoire my-hello. Étudions + comment propager cette modification dans d'autres dépôts. + + + Récupérer les modifications d'autres dépôts + + Pour commencer, construisons un clone de notre dépôt + hello qui ne contiendra pas + le changement que nous venons d'effectuer. Nous l'appellerons notre + dépôt temporaire hello-pull. + + +$ cd .. +$ hg clone hello hello-pull +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Nous allons utiliser la commande hg pull pour envoyer les modifications + depuis my-hello dans hello-pull. Néanmoins, récupérer + aveuglement des modifications depuis un dépôt a quelque chose d'un + peu effrayant. Mercurial propose donc une commande hg incoming qui permet de savoir quelles + modifications la commande hg pull + pourrait entraîner dans notre dépôt, et ceci + sans effectuer réellement de modification dessus. + + +$ cd hello-pull +$ hg incoming ../my-hello +comparing with ../my-hello +searching for changes +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Apporter les modifications rapatriées dans un dépôt se + résume donc à exécuter la commande hg + pull, et préciser depuis quel dépôt effectuer le hg pull. + + +$ hg tip +changeset: 4:2278160e78d4 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:16:53 2008 +0200 +summary: Trim comments. + +$ hg pull ../my-hello +pulling from ../my-hello +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +(run 'hg update' to get a working copy) +$ hg tip +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Comme vous le voyez avec une sortie avant et après de la + commande hg tip, nous avons réussi à + récupérer aisément les modifications dans notre dépôt. Il reste néanmoins + quelque chose à faire avant de placer ces modifications dans l'espace de + travail. + + + Récupérer des changements précis + + Il est possible à cause du délai entre l'exécution de la + commande hg incoming et l'exécution de + la commande hg pull, que vous ne + puissiez pas voir toutes les modifications que vous rapporterez d'un + autre dépôt. Supposons que vous récupériez les modifications d'un dépôt + situé quelque part sur le réseau. Alors que vous regardez le résultat de + la commande hg incoming, et avant que + vous ne décidiez de récupérer ces modifications, quelqu'un peut ajouter + de nouvelles révisions dans le dépôt distant. Ce qui signifie que vous + récupérez plus de révision que ce que vous aviez regardées en utilisant + la commande hg incoming. + + Si vous voulez seulement récupérer ce que vous aviez + vérifier à l'aide de la commande hg + incoming, ou que pour d'autres raisons vous souhaitiez ne + récupérer qu'un sous ensemble des révisions supplémentaires + disponibles, indiquant simplement les modifications que vous souhaitez + récupérer par leurs ID de révision, soit hg pull + -r7e95bb. + + + + + Mise à jour de l'espace de travail + + Nous avons jusqu'à maintenant grossièrement défini la + relation entre un dépôt et un espace de travail. La commande hg pull que nous avons exécutée dans la section + a apporté des modifications, que nous + avons vérifiées, dans notre dépôt, mais il n'y a aucune trace de ces + modifications dans notre espace de travail. En effet, hg pull ne touche pas (par défaut) à l'espace + de travail. C'est la commande hg update + qui s'en charge. + + +$ grep printf hello.c + printf("hello, world!\"); +$ hg update tip +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ grep printf hello.c + printf("hello, world!\"); + printf("hello again!\n"); + + + + + Il peut sembler un peu étrange que la commande hg pull ne mette pas à jour l'espace de travail + automatiquement. Il y a en fait une très bonne raison à cela : vous + pouvez utilisez la commande hg update + pour mettre à jour votre espace de travail à l'état dans lequel il était + à n'importe quelle révision de l'historique du dépôt. + Si vous aviez un espace de travail contenant une ancienne + révision—pour chercher l'origine d'un bug, par exemple—et + que vous effectuiez un hg pull qui + mettrait à jour automatiquement votre espace de travail, vous ne seriez + probablement pas très satisfait. + + Néanmoins, comme les opérations de pull sont très souvent + suivies d'un update, Mercurial vous permet de combiner les + deux aisément en passant l'option + à la commande hg pull. + + Si vous étudiez de nouveau la sortie de la commande hg pull dans la section quand nous l'avons exécutée sans l'option + , vous pouvez constater qu'elle a + affiché un rappel assez utile : vous devez encore effectuer une + opération pour mettre à jour votre espace de travail. + + Pour découvrir sur quelle révision de l'espace de + travail on se trouve, utilisez la commande hg + parents. + + +$ hg parents +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Si vous regardez de nouveau le dessin , vous verrez les flèches reliant + entre elles les révisions. Le nœud d'où la flèche + part est dans chaque cas un parent, + et le nœud où la flèche arrive est un + enfant. + + Pour mettre à jour l'espace de travail d'une révision + particulière, indiquez un numéro de révision ou un \textit{changeset + ID} à la commande hg update. + + +$ hg update 2 +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg parents +changeset: 2:fef857204a0c +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sat Aug 16 22:05:04 2008 +0200 +summary: Introduce a typo into hello.c. + +$ hg update +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg parents +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Si vous ne précisez pas de manière explicite de numéro + de révision la commande hg update + mettra à jour votre espace de travail avec le contenu de la révison + \textit{tip}, comme montré dans l'exemple ci dessus lors du second + appel à hg update. + + + + Transférer les modifications vers un autre dépôt + + Mercurial vous laisse transférer les modifications vers + un autre dépôt, depuis votre dépôt actuel. Comme dans l'exemple du + hg pull ci-dessus, nous allons créer + un dépôt temporaire vers lequel transférer nos modifications. + + +$ cd .. +$ hg clone hello hello-push +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + La commande hg outgoing + nous indique quels changements nous allons transférer vers l'autre + serveur. + + +$ cd my-hello +$ hg outgoing ../hello-push +comparing with ../hello-push +searching for changes +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Et la commande hg push + effectue réellement le transfert. + + +$ hg push ../hello-push +pushing to ../hello-push +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files + + + + + Comme avec hg pull, la + commande hg push ne met pas à jour + le répertoire de travail du dépôt dans lequel il transfère les + modifications. À l'inverse de hg + pull, hg push ne fournit + pas d'option -u pour forcer la mise à jour de + l'espace de travail cible. Cette asymétrie est délibéré : le dépot + vers lequel nous transférons peut très bien être un serveur distant + et partagé par plusieurs personnes. Si nous devions mettre à jour son + répertoire de travail alors que quelqu'un d'autre travaille dessus, + nous risquerions de perturber son travail. + + Qu'est ce qui se passe lorsque vous essayez de récupérer + ou de transférer vos modifications et que le dépôt cible a déjà reçu + ces modifications ? Rien de bien excitant. + + +$ hg push ../hello-push +pushing to ../hello-push +searching for changes +no changes found + + + + + + + + Emplacements par défaut + + Quand nous faisons un clone d'un dépôt, Mercurial + enregistre l'emplacement du dépôt d'origine dans le fichier + .hg/hgrc de notre nouveau dépôt. Si nous ne + fournissons pas d'emplacement à la commande hg + pull ou à la commande hg push, ces + commandes utiliseront alors cet emplacement comme valeur par défaut. + Les commandes hg incoming et hg + outgoing feront de même. + + Si vous regardez le fichier + .hg/hgrc, vous constaterez que son contenu + ressemble à ce qui suit. + + [paths] +default = http://www.selenic.com/repo/hg + + Il est possible—et souvent + pratique—d'avoir un emplacement par défaut pour les commandes + hg push et hg outgoing + différent de celui des commandes hg pull et + hg incoming. C'est faisable en ajoutant une entrée + default-push à la section + [paths] du .hg/hgrc, comme + suit. + + [paths] +default = http://www.selenic.com/repo/hg +default-push = http://hg.example.com/hg + + + + Partager ses modifications à travers le réseau + + Les commandes que nous avons étudiées dans les sections + précédentes ne sont pas limitées aux dépôts locaux. Chacune fonctionne + de la même manière à travers une connexion réseau, il suffit de lui + passer une URL à la place d'un chemin de fichier local. + + +$ hg outgoing http://hg.serpentine.com/tutorial/hello +comparing with http://hg.serpentine.com/tutorial/hello +searching for changes +changeset: 5:c94f208d1dfb +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + + + + + + Dans cet exemple, nous allons voir quels changements + nous pourrions transférer vers le dépôt distant, mais le dépôt est, + de manière tout à fait compréhensible, pas configuré pour accepter + des modifications d'utilisateurs anonymes. + + +$ hg push http://hg.serpentine.com/tutorial/hello +pushing to http://hg.serpentine.com/tutorial/hello +searching for changes +ssl required + + + + + + + + + + Commencer un nouveau projet + + Il est tout aussi aisé de commencer un nouveau projet + que de travailler sur un qui existe déjà. La commande hg + init crée un nouveau dépôt Mercurial vide. + + +$ hg init myproject + + + + + Ceci crée simplement un répertoire nommé + myproject dans le répertoire courant. + + +$ ls -l +total 12 +-rw-r--r-- 1 rpelisse rpelisse 47 Aug 16 14:04 goodbye.c +-rw-r--r-- 1 rpelisse rpelisse 45 Aug 16 14:04 hello.c +drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 myproject + + + + + Nous pouvons dire que myproject est + un dépôt Mercurial car il contient un répertoire + .hg. + + +$ ls -al myproject +total 12 +drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 . +drwx------ 3 rpelisse rpelisse 4096 Aug 16 14:04 .. +drwxr-xr-x 3 rpelisse rpelisse 4096 Aug 16 14:04 .hg + + + + + Si vous voulons ajouter quelques fichiers préexistants + dans ce dépôt, il suffit de les recopier dans le répertoire de travail, + et demander à Mercurial de commencer à les suivre en utilisant la + commande hg add. + + +$ cd myproject +$ cp ../hello.c . +$ cp ../goodbye.c . +$ hg add +adding goodbye.c +adding hello.c +$ hg status +A goodbye.c +A hello.c + + + + + Une fois que nous sommes satisfaits de notre projet, + nous pouvons commencer à ajouter nos révisions. + + +$ hg commit -m 'Initial commit' + + + + + Il ne prend que quelques instants pour commencer à + utiliser Mercurial sur un nouveau projet, ce qui fait aussi de ses + points forts. Travailler avec une gestion de révision devient très + facile, nous pouvons même l'utiliser pour les plus petits projets où + nous aurions probablement jamais penser utiliser un outils aussi + complexe. + +
+ + + + + + + + + Un rapide tour de Mercurial: fusionner les travaux + + Nous avons maintenant étudié comment cloner un dépôt, effectuer + des changements dedans, et récupérer ou transférer depuis un + autre dépôt. La prochaine étape est donc de fusionner les + modifications de différents dépôts. + + + Fusionner différents travaux + La fusion est un aspect fondamental lorsqu'on + travaille iavec un gestionnaire de source distribué. + + + + Alice et Bob ont chacun une copie personnelle du dépôt d'un + projet sur lequel ils collaborent. Alice corrige un bug + dans son dépôt, et Bob ajoute une nouvelle fonctionnalité dans le + sien. Ils veulent un dépôt partagé avec à la fois le correctif du + bug et la nouvelle fonctionnalité. + + + Je travaille régulièrement sur plusieurs tâches différentes sur + un seul projet en même temps, chacun isolé dans son propre dépôt. + Travailler ainsi signifie que je dois régulièrement fusionner une + partie de mon code avec celui des autres. + + + + Parce que la fusion est une opération si commune à réaliser, + Mercurial la rend facile. Étudions ensemble le déroulement des + opérations. Nous commencerons encore par faire un clone d'un autre + dépôt (vous voyez que l'on fait ça tout le temps ?) puis nous ferons + quelques modifications dessus. + + +$ cd .. +$ hg clone hello my-new-hello +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd my-new-hello +# Make some simple edits to hello.c. +$ my-text-editor hello.c +$ hg commit -m 'A new hello for a new day.' + + + + + Nous devrions avoir maintenant deux copies de + hello.c avec des contenus différents. Les + historiques de ces deux dépôts ont aussi divergés, comme illustré dans + la figure . + + +$ cat hello.c +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("once more, hello.\n"); + printf("hello, world!\"); + printf("hello again!\n"); + return 0; +} + + + + + Et ici est notre légèrement différente version du + dépôt. + + +$ cat ../my-hello/hello.c +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("hello, world!\"); + printf("hello again!\n"); + return 0; +} + + + + +
+ Historique divergent des dépôts <filename class="directory" moreinfo="none">my-hello</filename> et <filename class="directory" moreinfo="none">my-new-hello</filename>. + + + XXX ajoute un test + +
+ + Nous savons déjà que récupérer les modifications depuis + notre dépôt my-hello n'aura + aucun effet sur l'espace de travail. + + +$ hg pull ../my-hello +pulling from ../my-hello +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) + + + + + Néanmoins, la commande hg + pull nous indique quelque chose au sujet des + heads. + + + Les révisions 'heads' + + Rappellez vous que Mercurial enregistre quelle révision + est le parent de chaque révision. Si une révision a un parent, nous + l'appelons un enfant(child) ou un descendant de ce parent. Une + "head" est une révision qui n'a donc pas d'enfant. La révision tip + est donc une "head", car c'est la révision la plus récente du dépôt + qui n'a pas d'enfant. Il y a des moments où un dépôt peut contenir + plusieurs "head". + +
+ Contenu du dépôt après une récupération ("pull") depuis le + dépôt <filename class="directory" moreinfo="none">my-hello</filename> vers le dépôt <filename class="directory" moreinfo="none">my-new-hello</filename> + + + + + XXX ajoute un texte + +
+ + Dans la figure , + vous pouvez constater l'effet d'un \textit{pull} depuis le dépôt + my-hello dans le dépôt + my-new-hello. L'historique qui + était déjà présent dans le dépôt my-new-hello reste intact, mais une + nouvelle révision a été ajoutée. En vous reportant à la figure , vous pouvez voir que le + ID de révision (changeset ID) reste le même dans + le nouveau dépôt, mais que le numéro de + révision reste le même. (Ceci est un parfait exemple de + pourquoi il n'est fiable d'utiliser les numéros de révision lorsque + l'on discute d'un \textit{changeset}.) Vous pouvez voir les "heads" + présentes dans le dépôt en utilisant la commande hg heads. + + +$ hg heads +changeset: 6:c94f208d1dfb +tag: tip +parent: 4:2278160e78d4 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + +changeset: 5:5f06f94fbeca +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:31 2009 +0000 +summary: A new hello for a new day. + + + + +
+ + + Effectuer la fusion + + Que se passe-t-il quand vous essayez d'utiliser la + commande hg update pour mettre à + jour votre espace de travail au nouveau "tip" + + +$ hg update +abort: crosses branches (use 'hg merge' or 'hg update -C') + + + + + + Mercurial nous prévient que la commande hg update n'effectuera pas + la fusion, il ne veut pas mettre à jour l'espace de travail quand il + estime que nous pourrions avoir besoin d'une fusion, à moins de lui + forcer la main. À la place, il faut utiliser la commande hg merge pour fusionner les deux + "heads". + + Pour commencer une fusion (merge) entre deux "heads", + nous utilisons la commande hg merge. + + +$ hg merge +merging hello.c +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) + + + + + Nous résolvons les conflits dans le fichier + hello.c. Ceci met à jour le répertoire de travail + de sorte qu'il ne contienne les modifications ne provenance des + deux "heads", ce qui est indiqué par la + la sortie de la commande hg + parents et le contenu du fichier + hello.c. + + +$ hg parents +changeset: 5:5f06f94fbeca +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:31 2009 +0000 +summary: A new hello for a new day. + +changeset: 6:c94f208d1dfb +tag: tip +parent: 4:2278160e78d4 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:26 2009 +0000 +summary: Added an extra line of output + +$ cat hello.c +/* + * Placed in the public domain by Bryan O'Sullivan. This program is + * not covered by patents in the United States or other countries. + */ + +#include <stdio.h> + +int main(int argc, char **argv) +{ + printf("once more, hello.\n"); + printf("hello, world!\"); + printf("hello again!\n"); + return 0; +} + + + + + + + Effectuer l'ajout (commit) du résultat de la fusion + + Dès l'instant où vous avez effectué une fusion + (merge), hg parents vous + affichera deux parents, avant que vous n'exécutiez la commande + hg commit sur le résultat de la + fusion. + + +$ hg commit -m 'Merged changes' + + + + + Nous avons maintenant un nouveau tip, remarquer qu'il + contient à la fois nos anciennes "heads" et leurs + parents. Ce sont les mêmes révisions que nous avions affichées avec + la commande hg parents. + + +$ hg tip +changeset: 7:b8e1e756ef55 +tag: tip +parent: 5:5f06f94fbeca +parent: 6:c94f208d1dfb +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:33 2009 +0000 +summary: Merged changes + + + + + + Dans la figure , + vous pouvez voir une représentation de ce qui se passe dans l'espace + de travail pendant la fusion, et comment ceci affecte le dépôt lors + du "commit". Pendant la fusion, l'espace de travail, qui a deux + révisions (changesets) comme parents, voit ces derniers devenir le parent + d'une nouvelle révision (changeset). + +
+ Working directory and repository during merge, and + following commit + + + + + XXX ajoute texte + +
+ +
+
+ + + Fusionner les modifications en conflit + + La plupart des fusions sont assez simple à réaliser, mais + parfois vous vous retrouverez à fusionner des fichiers où la modification + touche la même portion de code, au sein d'un même fichier. À moins + que ces modification ne soient identiques, ceci aboutira à un + conflit, et vous devrez décider comment réconcilier + les différentes modifications dans un tout cohérent. + +
+ Modifications en conflits dans un document + + + XXX ajoute texte + +
+ + La figure + illustre un cas de modifications conflictuelles dans un document. Nous + avons commencé avec une version simple de ce fichier, puis nous avons + ajouté des modifications, pendant que quelqu'un d'autre modifiait le même + texte. Notre tâche dans la résolution du conflit est de décider à quoi le + fichier devrait ressembler. + + Mercurial n'a pas de mécanisme interne pour gérer + les conflits. À la place, il exécute un programme externe appelé + hgmerge. Il s'agit d'un script shell qui est + embarqué par Mercurial, vous pouvez le modifier si vous le voulez. + Ce qu'il fait par défaut est d'essayer de trouver un des différents + outils de fusion qui seront probablement installés sur le système. + Il commence par les outils totalement automatiques, et si ils + échouent (parce que la résolution du conflit nécessite une + intervention humaine) ou si ils sont absents, le script tente + d'exécuter certains outils graphiques de fusion. + + Il est aussi possible de demander à Mercurial d'exécuter + un autre programme ou un autre script en définissant la variable + d'environnement HGMERGE avec le nom + du programme de votre choix. + + + Utiliser un outil graphique de fusion + + Mon outil de fusion préféré est + kdiff3, que j'utilise ici pour illustrer les + fonctionnalités classiques des outils graphiques de fusion. Vous pouvez + voir une capture d'écran de l'utilisation de kdiff3 + dans la figure . Cet outil + effectue une fusion \textit{three-way}, car il y a + trois différentes versions du fichier qui nous intéresse. Le fichier + découpe la partie supérieure de la fenêtre en trois panneaux: + + A gauche on la version de + base du fichier, soit la plus récente version + des deux versions qu'on souhaite fusionner. + Au centre, il y a notre + version du fichier, avec le contenu que nous avons modifié. + Sur la droite, on trouve + leur version du fichier, celui qui contient la + révision que nous souhaitons intégré. + + Dans le panneau en dessous, on trouve le + résultat actuel de notre fusion. Notre tâche + consiste donc à remplacement tous les textes en rouges, + qui indiquent des conflits non résolus, avec une fusion manuelle et + pertinente de notre version et de la leur. + + + Tous les quatre panneaux sont accrochés ensemble, + si nous déroulons les ascenseurs verticalement ou horizontalement dans chacun + d'entre eux, les autres sont mis à jour avec la section correspondante dans leurs + fichiers respectifs. + +
+ Utiliser <command moreinfo="none">kdiff3</command> pour fusionner les + différentes version d'un fichier. + + + + + XXX ajoute texte + + +
+ + Pour chaque portion de fichier posant problème, nous + pouvons choisir de résoudre le conflit en utilisant une combinaison de + texte depuis la version de base, la notre, ou la leur. Nous pouvons + aussi éditer manuellement les fichiers à tout moment, si c'est nécessaire. + + Il y a beaucoup d'outils de + fusion disponibles, bien trop pour en parler de tous ici. Leurs + disponibilités varient selon les plate formes ainsi que leurs + avantages et inconvénients. La plupart sont optimisé pour + la fusion de fichier contenant un texte plat, certains sont spécialisé + dans un format de fichier précis (généralement XML). +
+ + + Un exemple concret + + Dans cet exemple, nous allons reproduire la + modification de l'historique du fichier de la figure ci dessus. Commençons par créer + un dépôt avec une version de base de notre document. + + +$ cat > letter.txt <<EOF +> Greetings! +> I am Mariam Abacha, the wife of former +> Nigerian dictator Sani Abacha. +> EOF +$ hg add letter.txt +$ hg commit -m '419 scam, first draft' + + + + + Créons un clone de ce dépôt et faisons une + modification dans le fichier. + + +$ cd .. +$ hg clone scam scam-cousin +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd scam-cousin +$ cat > letter.txt <<EOF +> Greetings! +> I am Shehu Musa Abacha, cousin to the former +> Nigerian dictator Sani Abacha. +> EOF +$ hg commit -m '419 scam, with cousin' + + + + + Et un autre clone, pour simuler que quelqu'un d'autre effectue une + modification sur le fichier. (Ceci pour suggérer qu'il n'est pas rare + de devoir effectuer des fusions (merges) avec vos propres travaux quand + vous isolez les tâches dans des dépôts distincts. En effet, vous + aurez alors à trouver et résoudre certains conflits). + + +$ cd .. +$ hg clone scam scam-son +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd scam-son +$ cat > letter.txt <<EOF +> Greetings! +> I am Alhaji Abba Abacha, son of the former +> Nigerian dictator Sani Abacha. +> EOF +$ hg commit -m '419 scam, with son' + + + + + Maintenant que ces deux versions différentes du même fichier sont + créées, nous allons configurer l'environnement de manière appropriée pour + exécuter notre fusion (merge). + + +$ cd .. +$ hg clone scam-cousin scam-merge +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd scam-merge +$ hg pull -u ../scam-son +pulling from ../scam-son +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +not updating, since new heads added +(run 'hg heads' to see heads, 'hg merge' to merge) + + + + + Dans cette exemple, je n'utiliserais pas la commande Mercurial + habituelle hgmerge pour effectuer le + fusion (merge), car il me faudrait abandonner ce joli petit exemple automatisé + pour utiliser un outil graphique. À la place, je vais définir la + variable d'environnement HGMERGE pour indiquer à + Mercurial d'utiliser la commande non-interactive merge. + Cette dernière est embarquée par de nombreux systèmes à la Unix. + Si vous exécutez cet exemple depuis votre ordinateur, ne vous + occupez pas de définir HGMERGE. + + +$ export HGMERGE=merge +$ hg merge +merging letter.txt +merge: warning: conflicts during merge +merging letter.txt failed! +0 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 +$ cat letter.txt +Greetings! +<<<<<<< /tmp/tour-merge-conflictk3twLJ/scam-merge/letter.txt +I am Shehu Musa Abacha, cousin to the former +======= +I am Alhaji Abba Abacha, son of the former +>>>>>>> /tmp/letter.txt~other.4O623C +Nigerian dictator Sani Abacha. + + + + + + Parce que merge ne peut pas résoudre + les modifications conflictuelles, il laisse des marqueurs de + différences à l'intérieur du fichier qui a des conflits, + indiquant clairement quelles lignes sont en conflits, et si elles + viennent de notre fichier ou du fichier externe. + + + Mercurial peut distinguer, à la manière dont la + commande merge se termine, qu'elle n'a pas été + capable d'effectuer la fusion (merge), alors il nous indique que nous + devons effectuer de nouveau cette opération. Ceci peut être très utile + si, par exemple, nous exécutons un outil graphique de fusion et que + nous le quittons sans nous rendre compte qu'il reste des conflits ou + simplement par erreur. + + Si la fusion (merge) automatique ou manuelle échoue, + il n'y a rien pour nous empêcher de corriger le tir en + modifiant nous même les fichiers, et enfin effectuer le "commit" du + fichier: + + +$ cat > letter.txt <<EOF +> Greetings! +> I am Bryan O'Sullivan, no relation of the former +> Nigerian dictator Sani Abacha. +> EOF +$ hg resolve -m letter.txt +$ hg commit -m 'Send me your money' +$ hg tip +changeset: 3:0954bda76c6b +tag: tip +parent: 1:1ac156b6e708 +parent: 2:7ee20631b33b +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:34 2009 +0000 +summary: Send me your money + + + + + + + Où est la <command moreinfo="none">hg resolve</command> ? + + La commande hg resolve a été + introduit dans la version 1.1 de Mercurial, qui a été publié en + décembre 2008. Si vous utilisez une version plus anciennne de + Mercurial (exécutez la command hg version pour en + avoir le coeur net), cette commande ne sera pas disponible. Si votre + version de Mercurial est plus ancienne que la 1.1, vous devriez très + fortement considérer une mise à jour à une version plus récente avant + d'essayer de régler des fusions complexes. + + +
+ + + Simplification de la séquence pull-merge-commit + + La procédure pour effectuer la fusion indiquée + ci-dessus est simple, mais requiert le lancement de trois commandes à la + suite. + + hg pull -u +hg merge +hg commit -m 'Merged remote changes' + + Lors du "commit" final, vous devez également saisir un + message, qui aura vraisemblablement assez peu d'intérêt. + + Il serait assez sympathique de pouvoir réduire le + nombre d'opérations nécessaire, si possible. De fait Mercurial est + fourni avec une extension appelé fetch + qui fait justement cela. + + Mercurial fourni un mécanisme d'extension flexible qui permet à chacun + d'étendre ces fonctionnalités, tout en conservant le cœur de Mercurial + léger et facile à utiliser. Certains extensions ajoutent de nouvelles + commandes que vous pouvez utiliser en ligne de commande, alors que + d'autres travaillent en coulisse, par exemple en ajoutant des + possibilités au serveur. + + L'extension fetch + ajoute une nouvelle commande nommée, sans surprise, hg fetch. Cette extension résulte en une + combinaison de hg pull, hg update and hg + merge. Elle commence par récupérer les modifications d'un + autre dépôt dans le dépôt courant. Si elle trouve que les + modifications ajoutent une nouvelle "head", elle effectue un "merge", + et ensuite "commit" le résultat du "merge" avec un message généré + automatiquement. Si aucune "head" n'ont été ajouté, elle met à jour le + répertoire de travail au niveau de la nouvelle révision tip. + + Activer l'extension fetch est facile. Modifiez votre .hgrc, et soit allez à la section extensions soit créer une section + extensions. Ensuite ajoutez + une ligne qui consiste simplement en \Verb+fetch =. + + [extensions] +fetch = + + (Normalement, sur la partie droite de + = devrait apparaître le chemin de + l'extension, mais étant donné que l'extension fetch fait partie de la distribution standard, + Mercurial sait où la trouver.) + + + + + Renommer, copier, et fusionner (merge) + + En cours de la vie d'un projet, nous allons souvent + vouloir changer la disposition de ses fichiers et de ses répertoires. + Ceci peut être aussi simple que de changer le nom d'un seul fichier, + et aussi compliqué que de restructurer une hiérarchie entiere de fichier + au sein du projet. + + Mercurial permet de faire ce genre de modification de + manière fluide, à condition de l'informer de ce que nous faisons. Si + vous voulez renommenr un ficher, vous devriez utiliser les commande + hg rename + Si vous un utilisateur de Unix, vous serez content + de savoir que la commande hg rename command + peut être abrégée en hg mv. + pour changer son nom, ainsi Mercurial peut ensuite prendre + la bonne décision, plus tard, en cas de fusionv (merge). + + Nous étudierojns en détail l'utilisation de ces commandes, + en détail, dans le chapitre . + +
+ + + + + + + + + Derrière le décor + + À la différence de beaucoup d'outils de gestion de versions, + les concepts sur lesquels se base Mercurial sont assez simples pour + qu'il soit facile de comprendre comment le logiciel fonctionne. + Bien que leur connaissance ne soit pas nécéssaire, je trouve utile + d'avoir un modèle mental de ce qui se passe. + + En effet, cette compréhension m'apporte la confiance que + Mercurial a été développé avec soin pour être à la fois + sûr et efficace. De surcroît, + si il m'est facile de garder en tête ce que le logiciel fait lorsque + j'accompli des tâches de révision, j'aurai moins de risques d'être + surpris par son comportement. + + Dans ce chapitre, nous décrirons tout d'abord les concepts + essentiels de l'architecture de Mercurial, pour ensuite discuter quelques + uns des détails intéressants de son implémentation. + + + Conservation de l'historique sous Mercurial + + Suivi de l'historique pour un seul fichier + + Lorsque Mercurial effectue un suivi des modifications + faites à un fichier, il conserve l'historique pour ce fichier dans un + filelog sous forme de métadonnées. Chaque entrée + dans le filelog contient assez d'informations pour reconstituer une + révision du fichier correspondant. Les filelogs sont des fichiers + stockés dans le répertoire .hg/store/data. Un filelog contient + des informations de deux types: les données de révision, et un index + pour permettre à Mercurial une recherche efficace d'une révision + donnée. + + Lorsqu'un fichier devient trop gros ou a un long + historique, son filelog se voit stocker dans un fichier de données + (avec un suffixe .d) et un fichier + index (avec un suffixe.i) + distincts. La relation entre un fichier dans le répertoire de travail + et le filelog couvrant le suivi de son historique dans le dépôt est + illustré à la figure . + +
+ Relations entre les fichiers dans le répertoire de travail et + leurs filelogs dans le dépôt + + XXX add text +
+ +
+ + Gestion des fichiers suivis + + Mercurial a recours à une structure nommée + manifest pour rassembler les informations sur + les fichiers dont il gère le suivi. Chaque entrée dans ce manifest + contient des informations sur les fichiers présents dans une révision + donnée. Une entrée store la liste des fichiers faisant partie de la + révision, la version de chaque fichier, et quelques autres + métadonnées sur ces fichiers. + + + + Recording changeset information + + The changelog contains + information about each changeset. Each revision records who + committed a change, the changeset comment, other pieces of + changeset-related information, and the revision of the manifest to + use. + + + + Relationships between revisions + + Within a changelog, a manifest, or a filelog, each + revision stores a pointer to its immediate parent (or to its + two parents, if it's a merge revision). As I mentioned above, + there are also relationships between revisions + across these structures, and they are + hierarchical in nature. + + For every changeset in a repository, there is exactly one + revision stored in the changelog. Each revision of the + changelog contains a pointer to a single revision of the + manifest. A revision of the manifest stores a pointer to a + single revision of each filelog tracked when that changeset + was created. These relationships are illustrated in + . + +
+ Metadata relationships + + + XXX add text + +
+ + As the illustration shows, there is + not a one to one + relationship between revisions in the changelog, manifest, or + filelog. If a file that + Mercurial tracks hasn't changed between two changesets, the + entry for that file in the two revisions of the manifest will + point to the same revision of its filelog + It is possible (though unusual) for the manifest to + remain the same between two changesets, in which case the + changelog entries for those changesets will point to the + same revision of the manifest. + . + +
+
+ + Safe, efficient storage + + The underpinnings of changelogs, manifests, and filelogs are + provided by a single structure called the + revlog. + + + Efficient storage + + The revlog provides efficient storage of revisions using a + delta mechanism. Instead of storing a + complete copy of a file for each revision, it stores the + changes needed to transform an older revision into the new + revision. For many kinds of file data, these deltas are + typically a fraction of a percent of the size of a full copy + of a file. + + Some obsolete revision control systems can only work with + deltas of text files. They must either store binary files as + complete snapshots or encoded into a text representation, both + of which are wasteful approaches. Mercurial can efficiently + handle deltas of files with arbitrary binary contents; it + doesn't need to treat text as special. + + + + Safe operation + + Mercurial only ever appends data to + the end of a revlog file. It never modifies a section of a + file after it has written it. This is both more robust and + efficient than schemes that need to modify or rewrite + data. + + In addition, Mercurial treats every write as part of a + transaction that can span a number of + files. A transaction is atomic: either + the entire transaction succeeds and its effects are all + visible to readers in one go, or the whole thing is undone. + This guarantee of atomicity means that if you're running two + copies of Mercurial, where one is reading data and one is + writing it, the reader will never see a partially written + result that might confuse it. + + The fact that Mercurial only appends to files makes it + easier to provide this transactional guarantee. The easier it + is to do stuff like this, the more confident you should be + that it's done correctly. + + + + Fast retrieval + + Mercurial cleverly avoids a pitfall common to + all earlier revision control systems: the problem of + inefficient retrieval. Most revision + control systems store the contents of a revision as an + incremental series of modifications against a + snapshot. (Some base the snapshot on the + oldest revision, others on the newest.) To reconstruct a + specific revision, you must first read the snapshot, and then + every one of the revisions between the snapshot and your + target revision. The more history that a file accumulates, + the more revisions you must read, hence the longer it takes to + reconstruct a particular revision. + +
+ Snapshot of a revlog, with incremental deltas + + + XXX add text + +
+ + The innovation that Mercurial applies to this problem is + simple but effective. Once the cumulative amount of delta + information stored since the last snapshot exceeds a fixed + threshold, it stores a new snapshot (compressed, of course), + instead of another delta. This makes it possible to + reconstruct any revision of a file + quickly. This approach works so well that it has since been + copied by several other revision control systems. + + illustrates + the idea. In an entry in a revlog's index file, Mercurial + stores the range of entries from the data file that it must + read to reconstruct a particular revision. + + + Aside: the influence of video compression + + If you're familiar with video compression or + have ever watched a TV feed through a digital cable or + satellite service, you may know that most video compression + schemes store each frame of video as a delta against its + predecessor frame. + + Mercurial borrows this idea to make it + possible to reconstruct a revision from a snapshot and a + small number of deltas. + + +
+ + Identification and strong integrity + + Along with delta or snapshot information, a revlog entry + contains a cryptographic hash of the data that it represents. + This makes it difficult to forge the contents of a revision, + and easy to detect accidental corruption. + + Hashes provide more than a mere check against corruption; + they are used as the identifiers for revisions. The changeset + identification hashes that you see as an end user are from + revisions of the changelog. Although filelogs and the + manifest also use hashes, Mercurial only uses these behind the + scenes. + + Mercurial verifies that hashes are correct when it + retrieves file revisions and when it pulls changes from + another repository. If it encounters an integrity problem, it + will complain and stop whatever it's doing. + + In addition to the effect it has on retrieval efficiency, + Mercurial's use of periodic snapshots makes it more robust + against partial data corruption. If a revlog becomes partly + corrupted due to a hardware error or system bug, it's often + possible to reconstruct some or most revisions from the + uncorrupted sections of the revlog, both before and after the + corrupted section. This would not be possible with a + delta-only storage model. + +
+ + + Revision history, branching, and merging + + Every entry in a Mercurial revlog knows the identity of its + immediate ancestor revision, usually referred to as its + parent. In fact, a revision contains room + for not one parent, but two. Mercurial uses a special hash, + called the null ID, to represent the idea + there is no parent here. This hash is simply a + string of zeroes. + + In , you can see + an example of the conceptual structure of a revlog. Filelogs, + manifests, and changelogs all have this same structure; they + differ only in the kind of data stored in each delta or + snapshot. + + The first revision in a revlog (at the bottom of the image) + has the null ID in both of its parent slots. For a + normal revision, its first parent slot contains + the ID of its parent revision, and its second contains the null + ID, indicating that the revision has only one real parent. Any + two revisions that have the same parent ID are branches. A + revision that represents a merge between branches has two normal + revision IDs in its parent slots. + +
+ The conceptual structure of a revlog + + + XXX add text + +
+ +
+ + The working directory + + In the working directory, Mercurial stores a snapshot of the + files from the repository as of a particular changeset. + + The working directory knows which changeset + it contains. When you update the working directory to contain a + particular changeset, Mercurial looks up the appropriate + revision of the manifest to find out which files it was tracking + at the time that changeset was committed, and which revision of + each file was then current. It then recreates a copy of each of + those files, with the same contents it had when the changeset + was committed. + + The dirstate is a special + structure that contains Mercurial's knowledge of the working + directory. It is maintained as a file named + .hg/dirstate inside a repository. The + dirstate details which changeset the working directory is + updated to, and all of the files that Mercurial is tracking in + the working directory. It also lets Mercurial quickly notice + changed files, by recording their checkout times and + sizes. + + Just as a revision of a revlog has room for two parents, so + that it can represent either a normal revision (with one parent) + or a merge of two earlier revisions, the dirstate has slots for + two parents. When you use the hg + update command, the changeset that you update to is + stored in the first parent slot, and the null ID + in the second. When you hg + merge with another changeset, the first parent + remains unchanged, and the second parent is filled in with the + changeset you're merging with. The hg + parents command tells you what the parents of the + dirstate are. + + + What happens when you commit + + The dirstate stores parent information for more than just + book-keeping purposes. Mercurial uses the parents of the + dirstate as the parents of a new + changeset when you perform a commit. + +
+ The working directory can have two parents + + + XXX add text + +
+ + shows the + normal state of the working directory, where it has a single + changeset as parent. That changeset is the + tip, the newest changeset in the + repository that has no children. + +
+ The working directory gains new parents after a + commit + + + XXX add text + +
+ + It's useful to think of the working directory as + the changeset I'm about to commit. Any files + that you tell Mercurial that you've added, removed, renamed, + or copied will be reflected in that changeset, as will + modifications to any files that Mercurial is already tracking; + the new changeset will have the parents of the working + directory as its parents. + + After a commit, Mercurial will update the + parents of the working directory, so that the first parent is + the ID of the new changeset, and the second is the null ID. + This is shown in . Mercurial + doesn't touch any of the files in the working directory when + you commit; it just modifies the dirstate to note its new + parents. + +
+ + Creating a new head + + It's perfectly normal to update the working directory to a + changeset other than the current tip. For example, you might + want to know what your project looked like last Tuesday, or + you could be looking through changesets to see which one + introduced a bug. In cases like this, the natural thing to do + is update the working directory to the changeset you're + interested in, and then examine the files in the working + directory directly to see their contents as they were when you + committed that changeset. The effect of this is shown in + . + +
+ The working directory, updated to an older + changeset + + + XXX add text + +
+ + Having updated the working directory to an + older changeset, what happens if you make some changes, and + then commit? Mercurial behaves in the same way as I outlined + above. The parents of the working directory become the + parents of the new changeset. This new changeset has no + children, so it becomes the new tip. And the repository now + contains two changesets that have no children; we call these + heads. You can see the structure that + this creates in . + +
+ After a commit made while synced to an older + changeset + + + XXX add text + +
+ + + If you're new to Mercurial, you should keep + in mind a common error, which is to use the + hg pull command without any + options. By default, the hg + pull command does not + update the working directory, so you'll bring new changesets + into your repository, but the working directory will stay + synced at the same changeset as before the pull. If you + make some changes and commit afterwards, you'll thus create + a new head, because your working directory isn't synced to + whatever the current tip is. To combine the operation of a + pull, followed by an update, run hg pull + -u. + + I put the word error in quotes + because all that you need to do to rectify the situation + where you created a new head by accident is + hg merge, then hg commit. In other words, this + almost never has negative consequences; it's just something + of a surprise for newcomers. I'll discuss other ways to + avoid this behavior, and why Mercurial behaves in this + initially surprising way, later on. + + +
+ + Merging changes + + When you run the hg + merge command, Mercurial leaves the first parent + of the working directory unchanged, and sets the second parent + to the changeset you're merging with, as shown in . + +
+ Merging two heads + + + + + XXX add text + +
+ + Mercurial also has to modify the working directory, to + merge the files managed in the two changesets. Simplified a + little, the merging process goes like this, for every file in + the manifests of both changesets. + + If neither changeset has modified a file, do + nothing with that file. + + If one changeset has modified a file, and the + other hasn't, create the modified copy of the file in the + working directory. + + If one changeset has removed a file, and the + other hasn't (or has also deleted it), delete the file + from the working directory. + + If one changeset has removed a file, but the + other has modified the file, ask the user what to do: keep + the modified file, or remove it? + + If both changesets have modified a file, + invoke an external merge program to choose the new + contents for the merged file. This may require input from + the user. + + If one changeset has modified a file, and the + other has renamed or copied the file, make sure that the + changes follow the new name of the file. + + There are more details—merging has plenty of corner + cases—but these are the most common choices that are + involved in a merge. As you can see, most cases are + completely automatic, and indeed most merges finish + automatically, without requiring your input to resolve any + conflicts. + + When you're thinking about what happens when you commit + after a merge, once again the working directory is the + changeset I'm about to commit. After the hg merge command completes, the + working directory has two parents; these will become the + parents of the new changeset. + + Mercurial lets you perform multiple merges, but + you must commit the results of each individual merge as you + go. This is necessary because Mercurial only tracks two + parents for both revisions and the working directory. While + it would be technically feasible to merge multiple changesets + at once, Mercurial avoids this for simplicity. With multi-way + merges, the risks of user confusion, nasty conflict + resolution, and making a terrible mess of a merge would grow + intolerable. + +
+ + + Merging and renames + + A surprising number of revision control systems pay little + or no attention to a file's name over + time. For instance, it used to be common that if a file got + renamed on one side of a merge, the changes from the other + side would be silently dropped. + + Mercurial records metadata when you tell it to perform a + rename or copy. It uses this metadata during a merge to do the + right thing in the case of a merge. For instance, if I rename + a file, and you edit it without renaming it, when we merge our + work the file will be renamed and have your edits + applied. + +
+ + + Other interesting design features + + In the sections above, I've tried to highlight some of the + most important aspects of Mercurial's design, to illustrate that + it pays careful attention to reliability and performance. + However, the attention to detail doesn't stop there. There are + a number of other aspects of Mercurial's construction that I + personally find interesting. I'll detail a few of them here, + separate from the big ticket items above, so that + if you're interested, you can gain a better idea of the amount + of thinking that goes into a well-designed system. + + + Clever compression + + When appropriate, Mercurial will store both snapshots and + deltas in compressed form. It does this by always + trying to compress a snapshot or delta, + but only storing the compressed version if it's smaller than + the uncompressed version. + + This means that Mercurial does the right + thing when storing a file whose native form is + compressed, such as a zip archive or a JPEG + image. When these types of files are compressed a second + time, the resulting file is usually bigger than the + once-compressed form, and so Mercurial will store the plain + zip or JPEG. + + Deltas between revisions of a compressed file are usually + larger than snapshots of the file, and Mercurial again does + the right thing in these cases. It finds that + such a delta exceeds the threshold at which it should store a + complete snapshot of the file, so it stores the snapshot, + again saving space compared to a naive delta-only + approach. + + + Network recompression + + When storing revisions on disk, Mercurial uses the + deflate compression algorithm (the same one + used by the popular zip archive format), + which balances good speed with a respectable compression + ratio. However, when transmitting revision data over a + network connection, Mercurial uncompresses the compressed + revision data. + + If the connection is over HTTP, Mercurial recompresses + the entire stream of data using a compression algorithm that + gives a better compression ratio (the Burrows-Wheeler + algorithm from the widely used bzip2 + compression package). This combination of algorithm and + compression of the entire stream (instead of a revision at a + time) substantially reduces the number of bytes to be + transferred, yielding better network performance over most + kinds of network. + + If the connection is over + ssh, Mercurial + doesn't recompress the stream, because + ssh can already do this itself. You can + tell Mercurial to always use ssh's + compression feature by editing the + .hgrc file in your home directory as + follows. + + [ui] +ssh = ssh -C + + + + + Read/write ordering and atomicity + + Appending to files isn't the whole story when + it comes to guaranteeing that a reader won't see a partial + write. If you recall , + revisions in the changelog point to revisions in the manifest, + and revisions in the manifest point to revisions in filelogs. + This hierarchy is deliberate. + + A writer starts a transaction by writing filelog and + manifest data, and doesn't write any changelog data until + those are finished. A reader starts by reading changelog + data, then manifest data, followed by filelog data. + + Since the writer has always finished writing filelog and + manifest data before it writes to the changelog, a reader will + never read a pointer to a partially written manifest revision + from the changelog, and it will never read a pointer to a + partially written filelog revision from the manifest. + + + + Concurrent access + + The read/write ordering and atomicity guarantees mean that + Mercurial never needs to lock a + repository when it's reading data, even if the repository is + being written to while the read is occurring. This has a big + effect on scalability; you can have an arbitrary number of + Mercurial processes safely reading data from a repository + all at once, no matter whether it's being written to or + not. + + The lockless nature of reading means that if you're + sharing a repository on a multi-user system, you don't need to + grant other local users permission to + write to your repository in order for + them to be able to clone it or pull changes from it; they only + need read permission. (This is + not a common feature among revision + control systems, so don't take it for granted! Most require + readers to be able to lock a repository to access it safely, + and this requires write permission on at least one directory, + which of course makes for all kinds of nasty and annoying + security and administrative problems.) + + Mercurial uses locks to ensure that only one process can + write to a repository at a time (the locking mechanism is safe + even over filesystems that are notoriously hostile to locking, + such as NFS). If a repository is locked, a writer will wait + for a while to retry if the repository becomes unlocked, but + if the repository remains locked for too long, the process + attempting to write will time out after a while. This means + that your daily automated scripts won't get stuck forever and + pile up if a system crashes unnoticed, for example. (Yes, the + timeout is configurable, from zero to infinity.) + + + Safe dirstate access + + As with revision data, Mercurial doesn't take a lock to + read the dirstate file; it does acquire a lock to write it. + To avoid the possibility of reading a partially written copy + of the dirstate file, Mercurial writes to a file with a + unique name in the same directory as the dirstate file, then + renames the temporary file atomically to + dirstate. The file named + dirstate is thus guaranteed to be + complete, not partially written. + + + + + Avoiding seeks + + Critical to Mercurial's performance is the avoidance of + seeks of the disk head, since any seek is far more expensive + than even a comparatively large read operation. + + This is why, for example, the dirstate is stored in a + single file. If there were a dirstate file per directory that + Mercurial tracked, the disk would seek once per directory. + Instead, Mercurial reads the entire single dirstate file in + one step. + + Mercurial also uses a copy on write scheme + when cloning a repository on local storage. Instead of + copying every revlog file from the old repository into the new + repository, it makes a hard link, which is a + shorthand way to say these two names point to the same + file. When Mercurial is about to write to one of a + revlog's files, it checks to see if the number of names + pointing at the file is greater than one. If it is, more than + one repository is using the file, so Mercurial makes a new + copy of the file that is private to this repository. + + A few revision control developers have pointed out that + this idea of making a complete private copy of a file is not + very efficient in its use of storage. While this is true, + storage is cheap, and this method gives the highest + performance while deferring most book-keeping to the operating + system. An alternative scheme would most likely reduce + performance and increase the complexity of the software, but + speed and simplicity are key to the feel of + day-to-day use. + + + + Other contents of the dirstate + + Because Mercurial doesn't force you to tell it when you're + modifying a file, it uses the dirstate to store some extra + information so it can determine efficiently whether you have + modified a file. For each file in the working directory, it + stores the time that it last modified the file itself, and the + size of the file at that time. + + When you explicitly hg + add, hg remove, + hg rename or hg copy files, Mercurial updates the + dirstate so that it knows what to do with those files when you + commit. + + The dirstate helps Mercurial to efficiently + check the status of files in a repository. + + + + When Mercurial checks the state of a file in the + working directory, it first checks a file's modification + time against the time in the dirstate that records when + Mercurial last wrote the file. If the last modified time + is the same as the time when Mercurial wrote the file, the + file must not have been modified, so Mercurial does not + need to check any further. + + + If the file's size has changed, the file must have + been modified. If the modification time has changed, but + the size has not, only then does Mercurial need to + actually read the contents of the file to see if it has + changed. + + + + Storing the modification time and size dramatically + reduces the number of read operations that Mercurial needs to + perform when we run commands like hg status. + This results in large performance improvements. + + +
+ + + + + + + + + Mercurial pour une utilisation de tous les jours + + + Informer Mercurial des fichier à suivre + + Mercurial ne suit pas les fichiers de votre dépôt tant + que vous ne lui avez pas dit de les gérer. La commande hg status vous dira quels fichiers sont + inconnus de Mercurial. Il utilise un + ? pour montrer ces fichiers. + + Pour informer Mercurial de suivre un fichier, utilisez + la commande hg add. Une fois que vous + avez ajouté un fichier, la ligne correspondante à ce fichier dans la + sortie de hg status change de + ? à + A. + + +$ hg init add-example +$ cd add-example +$ echo a > myfile.txt +$ hg status +? myfile.txt +$ hg add myfile.txt +$ hg status +A myfile.txt +$ hg commit -m 'Added one file' +$ hg status + + + + + Après avoir exécuté un hg + commit, les fichiers que vous avez ajoutés avant le commit + ne seront plus listés dans la sortie de hg + status. La raison de ceci est que, par défaut, hg status ne vous montre que les fichiers + intéressants —ceux que vous avez (par exemple) + modifiés, supprimés ou renommés. Si vous aviez un dépôt qui contient un + millier de fichiers, vous ne voudriez certainement que rarement entendre + parler des fichiers que Mercurial suit, mais qui n'ont pas changés. + (Vous pouvez quand même avoir cette information, nous y reviendrons + plus tard.) + + Une fois que vous ajoutez un fichier, Mercurial ne fait + rien du tout avec celui-ci immédiatement. Au lieu de ça, il va prendre + un "snapshot" de l'état du fichier la prochaine fois que vous + exécuterez un commit. Il continuera ensuite à suivre les changements + que vous avez fait au fichier chaque fois que vous committerez, et ce, + jusqu'à ce que vous supprimiez le fichier. + + + Nommage des fichiers explicite versus implicite + + Un comportement utile que Mercurial possède est que si + vous passez le nom d'un répertoire à une commande, toute commande + Mercurial la traitera comme : Je veux opérer sur chaque fichier + dans ce répertoire et ses sous-répertoires. + + +$ mkdir b +$ echo b > b/somefile.txt +$ echo c > b/source.cpp +$ mkdir b/d +$ echo d > b/d/test.h +$ hg add b +adding b/d/test.h +adding b/somefile.txt +adding b/source.cpp +$ hg commit -m 'Added all files in subdirectory' + + + + + Remarquez que dans cet exemple, Mercurial affiche le + nom des fichiers qu'il a ajouté, alors qu'il ne l'a pas fait lorsque + nous avons ajouté le fichier nommé myfile.txt + dans l'exemple précédent. + + Ce qu'il se passe est que dans le premier cas, nous + avons nommé explicitement le fichier à ajouter sur la ligne de + commande. Ce que Mercurial suppose dans ce cas est que nous savons ce + que nous faisons, il n'affiche donc rien en sortie. + + Cependant, lorsque nous avons + implicitement donné les fichiers à l'aide du nom + d'un répertoire, Mercurial prend l'initiative d'afficher le nom de + chaque fichier avec lequel il fait quelque chose. Ceci clarifie ce + qu'il se passe et réduit la probabilité d'une mauvaise surprise + restée silencieuse. Ce comportement est commun à la plupart des + commandes Mercurial. + + + Mercurial suit les fichiers, pas les répertoires + + Mercurial ne suit pas les informations sur les + répertoires. En contrepartie, il suit le chemin vers un fichier. Avant + de créer un fichier, il crée au préalable les répertoires manquants + dans le chemin. Après avoir supprimé un fichier, il supprime chaque + répertoire vide qui apparaît dans le chemin du fichier. Ceci apparaît + comme une distinction triviale, cependant, cela a une conséquence + pratique mineure : il n'est pas possible de représenter un répertoire + totalement vide dans Mercurial. + + Les répertoires vides sont rarement utiles. Il existe + cependant des solutions alternatives et non intrusives que vous + pouvez utiliser pour obtenir l'effet approprié. Les développeurs de + Mercurial ont ainsi pensé que la complexité requise pour gérer les + répertoires n'était pas aussi importante que le bénéfice que cette + fonctionnalité apporterait. + + Si vous avez besoin d'un répertoire vide dans votre + dépôt, il existe quelques façons d'y arriver. L'une d'elles est de + créer un répertoire et ensuite, de faire un hg + add sur un fichier caché dans ce + répertoire. Sur les systèmes de type Unix, tout fichier dont le nom + commence avec un point (.) est + considéré comme caché par la plupart des commandes et outils + graphiques. Cette approche est illustrée ci-après. + + +$ hg init hidden-example +$ cd hidden-example +$ mkdir empty +$ touch empty/.hidden +$ hg add empty/.hidden +$ hg commit -m 'Manage an empty-looking directory' +$ ls empty +$ cd .. +$ hg clone hidden-example tmp +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ ls tmp +empty +$ ls tmp/empty + + + + + Une autre façon de s'attaquer au besoin d'un + répertoire vide est de simplement d'en créer un dans vos scripts + de construction avant qu'ils n'en aient le besoin. + + + + + Comment arrêter de suivre un fichier + + Une fois que vous décidez qu'un fichier n'appartient + plus à votre dépôt, utilisez la commande hg + remove. Ceci supprime le fichier et informe Mercurial + d'arrêter de le suivre (ce qui prendra effet lors du prochain commit). + Un fichier supprimé est représenté dans la sortie de la commande + hg status par un + R. + + +$ hg init remove-example +$ cd remove-example +$ echo a > a +$ mkdir b +$ echo b > b/b +$ hg add a b +adding b/b +$ hg commit -m 'Small example for file removal' +$ hg remove a +$ hg status +R a +$ hg remove b +removing b/b + + + + + Après avoir fait un hg + remove sur un fichier, Mercurial ne suivra plus aucun + changement sur ce fichier, même si vous recréez un fichier avec le même + nom dans votre répertoire de travail. Si vous recréez un fichier avec le + même nom et que vous désirez que Mercurial suive ce dernier, faite + simplement un hg add sur celui-ci. + Mercurial saura alors que le nouveau fichier ne fait pas référence à + l'ancien fichier qui portait le même nom. + + + Supprimer un fichier n'affecte pas son historique + + Il est important de comprendre que supprimer un fichier + n'a que deux effets. + + + Il supprime la version actuelle de ce + fichier du répertoire de travail. + + Il arrête, à partir du prochain commit, le + suivi de Mercurial sur les changements qui ont lieu sur ce + fichier. + + + Supprimer un fichier n'affecte en + aucun cas l'historique du + fichier. + + Si vous mettez à jour le répertoire de travail à un + changeset qui a été committé alors que le fichier que vous venez de + supprimer était encore suivi, ce fichier réapparaîtra dans le + répertoire de travail, avec le contenu qu'il avait lorsque vous aviez + committé ce changeset. Si vous mettez à jour (update) le répertoire de + travail à un changeset ultérieur dans lequel le fichier a été + supprimé, Mercurial supprimera une nouvelle fois le fichier du + répertoire de travail. + + + + Fichiers manquants + + Mercurial considère qu'un fichier que vous avez + supprimé sans utiliserhg remove + comme étant manquant. Un fichier manquant est + représenté avec un ! en sortie de + hg status. + Les commandes Mercurial ne feront rien avec les fichiers + manquants. + + +$ hg init missing-example +$ cd missing-example +$ echo a > a +$ hg add a +$ hg commit -m 'File about to be missing' +$ rm a +$ hg status +! a + + + + + Si votre dépôt contient un fichier que hg status reporte comme manquant, et que + vous voulez que ce fichier reste supprimé, vous pouvez exécuter + hg remove à tout moment + pour dire à Mercurial que vous aviez bien voulu supprimer ce + fichier. + + +$ hg remove --after a +$ hg status +R a + + + + + D'un autre coté, si vous avez supprimé le fichier + manquant par accident, donnez à la commande hg + revert le nom du fichier à retrouver. Il réapparaitra dans + sa forme non modifiée. + + +$ hg revert a +$ cat a +a +$ hg status + + + + + + + + Entre nous : Pourquoi dire explicitement à Mercurial de supprimer un + fichier ? + + Vous pourriez vous demander pourquoi il est nécessaire + de dire explicitement à Mercurial que vous souhaitez supprimer un + fichier. Au début du développement de Mercurial, celui ci vous + laissait pourtant supprimer un fichier sans soucis ; Mercurial vous + aurait automatiquement informé de l'absence du fichier lorsque vous + auriez lancé un hg commit et arrêté + de le suivre. En pratique, ceci a montré qu'il était trop facile de + supprimer accidentellement un fichier sans le remarquer. + + + + Raccourci utile—ajouter et supprimer des fichiers en une + seule étape. + + Mercurial offre une commande combinée, hg addremove, qui ajoute les fichiers non + suivis et marque les fichiers manquants comme supprimés. + + +$ hg init addremove-example +$ cd addremove-example +$ echo a > a +$ echo b > b +$ hg addremove +adding a +adding b + + + + + La commande hg commit + fournit aussi une option qui + exécute le même ajouter-et-supprimer, immédiatement suivi d'un + commit. + + +$ echo c > c +$ hg commit -A -m 'Commit with addremove' +adding c + + + + + + + + + Copier des fichiers + + Mercurial fournit une commande hg + copy qui vous permet de faire une nouvelle copie d'un + fichier. Lorsque vous copiez un fichier en utilisant cette commande, + Mercurial crée un enregistrement du fait que ce nouveau fichier est une + copie du fichier originel. Il traite ces fichiers copiés spécialement + lorsque vous fusionnez (merge) votre travail avec quelqu'un + d'autre. + + + Les résultats d'une copie durant une fusion (merge) + + Ce qu'il se passe durant une fusion (merge) est que + les changements suivent une copie. Pour illustrer ce + que cela veut dire de la meilleure façon, créons un exemple. Nous + allons commencer avec le mini dépôt usuel qui contient un simple + fichier. + + +$ hg init my-copy +$ cd my-copy +$ echo line > file +$ hg add file +$ hg commit -m 'Added a file' + + + + + Nous devons faire du travail en parallèle, ainsi, + nous aurons quelque chose à fusionner (merge). Donc clonons notre + dépôt. + + +$ cd .. +$ hg clone my-copy your-copy +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + De retour dans notre dépôt initial, utilisons la + commande hg copy pour faire une + copie du premier fichier que nous avons créé. + + +$ cd my-copy +$ hg copy file new-file + + + + + Si nous regardons ensuite à la sortie de la commande + hg status, les fichiers copiés + ont l'air de fichiers normalement ajoutés. + + +$ hg status +A new-file + + + + + Mais si nous passons l'option à hg + status, il affiche une autre ligne de sortie : il s'agit + du fichier source pour notre copie. + + +$ hg status -C +A new-file + file +$ hg commit -m 'Copied file' + + + + + Maintenant, de retour dans le dépôt que nous avons + cloné, créons un changement en parallèle. Nous allons ajouter une + ligne de contenu au fichier original qui a été créé. + + +$ cd ../your-copy +$ echo 'new contents' >> file +$ hg commit -m 'Changed file' + + + + + Nous avons alors un fichier file + modifié dans ce dépôt. Lorsque nous récupérons (pull) les changements + depuis le premier répertoire et fusionnons (merge) les deux "heads", + Mercurial propagera les changements que nous avons faits localement + au fichier file dans sa copie + new-file. + + +$ hg pull ../my-copy +pulling from ../my-copy +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) +$ hg merge +merging file and new-file to new-file +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ cat new-file +line +new contents + + + + + + + Pourquoi est-ce que les changements devraient suivre les copies + ? + + Ce comportement—des changements d'un fichiers + qui se propagent aux copies de ce fichier—peut sembler + ésotérique, mais, dans la plupart des cas, c'est hautement + désirable. + + Pour commencer, souvenez vous que cette propagation + a lieue seulement lors des fusions (merge). + Donc, si vous faites un hg copy sur + un fichier, et par la suite modifiez le fichier original durant le + cours normal de votre travail, rien n'a lieu. + + La deuxième chose à savoir c'est que les modifications + ne se propageront à travers une copie que si le changeset à partir + duquel vous faites une fusion (merge) n'a pas encore + vu la copie. + + La raison pour laquelle Mercurial fait ainsi est une + règle. Imaginons que je corrige un important bug dans un fichier source + et que je commit mes changements. Pendant ce temps, vous avez décidé de + faire un hg copy du fichier dans + votre dépôt, sans rien savoir au sujet du bug ou à propos de la + correction. Vous avez alors commencé à "hacker" sur votre copie du + fichier. + + Si vous aviez récupéré (pull) et fusionné (merge) mes + changements, et que Mercurial n'avait pas + propagé les changements à travers les copies, votre nouveau fichier + source contiendrait maintenant le bug, et à moins que vous ne sachiez + qu'il faille propager la correction du bug à la main, le bug aurait + subsisté dans votre copie du fichier. + + En propageant automatiquement les changements qui + fixent les bugs à partir du fichier original vers les copies, + Mercurial prévient ce type de problèmes. A ma connaissance, Mercurial + est le seul système de gestion de révisions qui + propage les changements à travers les copies comme ceci. + + Une fois que votre historique des changements a un + enregistrement concernant une copie et qu'une fusion postérieure a + eu lieue, il n'y a d'habitude pas d'autre besoin de propager les + changements du fichier originel vers le fichier copié. C'est pourquoi + Mercurial ne propage les changements à travers les copies qu'à la + première fusion, et pas d'avantage. + + + + Comment faire des changements qui <emphasis>ne</emphasis> + suivent <emphasis>pas</emphasis> une copie + + Si pour une raison ou une autre, vous décidez que + cette fonctionnalité de propager automatiquement les changements à + travers les copies n'est pas pour vous, utilisez simplement la + commande normale de copie de votre système (sur les systèmes de type + Unix, il s'agit de cp) pour faire une copie d'un + fichier. Utilisez ensuite hg add + pour ajouter les nouveaux fichiers à la main. Cependant, avant d'en + faire ainsi, relisez , et faites + un choix en connaissance de cause comme quoi cette fonctionnalité + n'est pas appropriée à votre cas spécifique. + + + + Comportement de la commande <command role="hg-cmd" moreinfo="none">hg copy</command> + + Lorsque vous utilisez la commande hg copy, Mercurial crée une copie de chaque + fichier source tel qu'il est actuellement dans le répertoire de + travail. Cela signifie que si vous effectuez des modifications sur un + fichier, puis faites un hg copy sur + celui-ci sans avoir au préalable committé ces changements, la nouvelle + copie contiendra aussi les modifications que vous avez fait jusqu'à + ce point. (Je trouve ce comportement quelque peu contre intuitif, + c'est pourquoi j'en fais mention ici.) + + + La commande hg copy + agit comme la commande Unix cp (vous pouvez + utilisez l'alias hg cp si vous + préférez). Nous devons lui donner deux ou plus arguments où le + dernier est considéré comme la destination, et + les autres comme les sources. + + Si vous passez à hg + copy un seul fichier source, et que la destination + n'existe pas, ceci créera un nouveau fichier avec ce nom. + + +$ mkdir k +$ hg copy a k +$ ls k +a + + + + + Si la destination est un répertoire, Mercurial copie + les sources dans ce répertoire. + + +$ mkdir d +$ hg copy a b d +$ ls d +a b + + + + + La copie de répertoire est récursive et préserve la + structure du répertoire source. + + +$ hg copy z e +copying z/a/c to e/a/c + + + + + Si la source et la destination sont tous deux des + répertoires, l'arborescence de la source est recréée dans le + répertoire destination. + + +$ hg copy z d +copying z/a/c to d/z/a/c + + + + + Comme avec la commande hg + remove, si vous copiez un fichier manuellement et voulez + que Mercurial sache qu'il s'agit d'une copie, utilisez simplement + l'option avec hg copy. + + +$ cp a n +$ hg copy --after a n + + + + + + + + Renommer les fichiers + + Il est plus commun d'avoir besoin de renommer un + fichier que d'en faire une copie. La raison pour laquelle j'ai discuté + de la commande hg copy avant de parler + de renommage des fichiers est que Mercurial traite les renommages + essentiellement comme une copie. Ainsi, savoir comment Mercurial traite + les copies de fichiers vous informe sur ce que vous êtes en droit + d'attendre lorsque vous renommez un fichier. + + Lorsque vous utilisez la commande hg rename, Mercurial crée une copie de tous + les fichiers sources, les supprime et marque ces fichiers comme étant + supprimés. + + +$ hg rename a b + + + + + La commande hg status + montre les nouveaux fichiers comme ajoutés et les fichiers originaux + comme supprimés. + + +$ hg status +A b +R a + + + + + A cause du hg copy, + nous devons utiliser l'option + pour la commande hg status afin + d'observer que le fichier ajouté est bien suivi par Mercurial comme + étant une copie de l'original maintenant supprimé. + + +$ hg status -C +A b + a +R a + + + + + Comme avec hg remove et + hg copy, vous pouvez informer + Mercurial au sujet d'un renommage après coup en utilisant l'option + . Dans le plus grand + respect, le comportement de la commande hg + rename, et les options qu'il accepte sont similaires à la + commande hg copy. + + Si vous êtes familier avec la ligne de commande Unix, + vous serez heureux d'apprendre que la commande hg rename peut être invoquée par hg mv. + + + Renommer les fichiers et fusionner (merge) les changements + + Puise que le "rename" de Mercurial est implanté comme un + "copy-and-remove", la même propagation des changements a lieue après + un "rename" qu'après un "copy" lorsque vous fusionnez (merge). + + Si je modifie un fichier et que vous le renommez, si + ensuite nous fusionnons nos changements respectifs, mes modifications + sur le fichier sous son nom originel seront propagés vers le même + fichier sous son nouveau nom. (C'est quelque chose que vous pourriez + espérer voir fonctionner simplement, mais tous les + systèmes de gestion de version ne le font pas.) + + Tandis qu'avoir des changements qui suivent une copie + est une fonctionnalité où vous hocheriez sûrement la tête en disant + oui, cela pourrait être utile, il est clair que les + voir suivre un renommage est définitivement important. Sans cette + aptitude, il serait vraiment trop facile d'avoir des changements + qui deviennent orphelins lorsque des fichiers sont renommés. + + + + Renommages divergeants et fusion (merge) + + Le cas de noms divergeants a lieu lorsque deux + développeurs commencent avec un fichier—appelons le + foo—dans leurs dépôts respectifs. + + +$ hg clone orig anne +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg clone orig bob +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Anne renomme le fichier en + bar. + + +$ cd anne +$ hg rename foo bar +$ hg ci -m 'Rename foo to bar' + + + + + Pendant ce temps, Bob le renomme en + quux. (Souvenez vous que hg mv est un alias pour hg rename.) + + +$ cd ../bob +$ hg mv foo quux +$ hg ci -m 'Rename foo to quux' + + + + + J'aime à penser qu'il s'agit d'un conflit puisque + chaque développeur a exprimé différentes intentions au sujet de ce + que le nom de ce fichier aurait du être. + + Que pensez vous qu'il devrait se produire lorsqu'ils + fusionnent (merge) leurs travaux ? Le comportement actuel de + Mercurial est qu'il préserve toujours les deux + noms lorsqu'il fusionne (merge) des changesets qui contiennent des + renommages divergeants. + + +# See http://www.selenic.com/mercurial/bts/issue455 +$ cd ../orig +$ hg pull -u ../anne +pulling from ../anne +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ hg pull ../bob +pulling from ../bob +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) +$ hg merge +warning: detected divergent renames of foo to: + bar + quux +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ ls +bar quux + + + + + Remarquez que bien que Mercurial vous avertisse au + sujet de la divergeance des renommages, il vous laisse faire quelque + chose au sujet de la divergeance après la fusion (merge). + + + + Renommages et fusion convergeants + + Un autre type de conflit de renommage intervient + lorsque deux personne choisissent de renommer différents fichiers + source vers la même + destination. Dans ce cas, Mercurial exécute la + machinerie normale de fusion (merge) et vous guide vers une + solution convenable. + + + + Autres cas anguleux relatifs aux noms + + Mercurial possède un bug de longue date dans lequel il + échoue à traiter une fusion (merge) où un coté a un fichier avec un + nom donné, alors que l'autre coté possède un répertoire avec le même nom. + Ceci est documenté dans l'issue + 29. + + +$ hg init issue29 +$ cd issue29 +$ echo a > a +$ hg ci -Ama +adding a +$ echo b > b +$ hg ci -Amb +adding b +$ hg up 0 +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ mkdir b +$ echo b > b/b +$ hg ci -Amc +adding b/b +created new head +$ hg merge +abort: Is a directory: /tmp/issue29vhrzWD/issue29/b + + + + + + + + + Récupération d'erreurs + + Mercurial possède certaines commandes utiles qui vont + vous aider à récupérer de certaines erreurs communes. + + La commande hg revert + vous permet d'annuler les changements que vous avez faits dans votre + répertoire de travail. Par exemple, si vous faites un hg add sur un fichier par accident, exécutez + juste hg revert avec le nom du fichier + que vous avez ajouté et tandis que le fichier ne sera touché d'une + quelconque manière, il ne sera plus suivi comme ajouté par Mercurial. + Vous pouvez aussi utiliser la commande hg + revert pour vous débarrasser de modifications erronés + apportées à un fichier. + + Il est utile de se souvenir que la commande hg revert est utile pour les modifications + qui n'ont pas encore été committées. Une fois que vous avez committé un + changement, si vous décidez qu'il s'agissait d'une erreur, vous pouvez + toujours faire quelque chose à ce sujet, bien que vos options soient + un peu plus limitées. + + Pour plus d'informations au sujet de la commande + hg revert, et des détails sur comment + traiter les modifications que vous avez déjà committées, référez vous à + . + + + + Traiter avec les fusions (merge) malicieuses + + Dans des projets compliqués ou conséquents, il n'est pas + rare qu'une fusion (merge) de deux changesets finisse par une migraine. + Supposez qu'il y ait un gros fichier source qui ait été largement édité de + chaque coté de la fusion (merge) : ceci va inévitablement résulter en + conflits, dont certains peuvent prendre plusieurs essais pour s'en + sortir. + + Développons en un cas simple pour voir comment le gérer. + Nous allons commencer avec un dépôt contenant un fichier, et le + cloner deux fois. + + +$ hg init conflict +$ cd conflict +$ echo first > myfile.txt +$ hg ci -A -m first +adding myfile.txt +$ cd .. +$ hg clone conflict left +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg clone conflict right +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Dans un des clones, nous allons modifier le fichier + d'une façon. + + +$ cd left +$ echo left >> myfile.txt +$ hg ci -m left + + + + + Dans un autre, nous allons modifier le fichier + différemment. + + +$ cd ../right +$ echo right >> myfile.txt +$ hg ci -m right + + + + + Ensuite, nous allons récupérer (pull) chaque ensemble de + changement dans notre dépôt original. + + +$ cd ../conflict +$ hg pull -u ../left +pulling from ../left +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg pull -u ../right +pulling from ../right +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files (+1 heads) +not updating, since new heads added +(run 'hg heads' to see heads, 'hg merge' to merge) + + + + + Nous nous attendons à ce que notre dépôt contienne deux + "heads". + + +$ hg heads +changeset: 2:85f1afc84c33 +tag: tip +parent: 0:14a820f81f48 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:51 2009 +0000 +summary: right + +changeset: 1:085ebbf44348 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:51 2009 +0000 +summary: left + + + + + + Normalement, si nous lançons hg + merge à ce point, il nous renverra vers une interface + utilisateur qui nous permettra de résoudre manuellement les éditions + conflictuelles sur le fichier myfile.txt. + Cependant, pour simplifier ici les choses dans la présentation, nous + aimerions plutôt que la fusion (merge) échoue immédiatement. Voici une + façon de le faire. + + +$ export HGMERGE=false + + + + + Nous avons dit au processus de fusion de Mercurial + d'exécuter la commande false (qui échoue + immédiatement, à la demande) s'il détecte une fusion (merge) qu'il ne + peut pas arranger automatiquement. + + Si nous appelons maintenant hg + merge, il devrait échouer et reporter une erreur. + + +$ hg merge +merging myfile.txt +merging myfile.txt failed! +0 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 + + + + + Même si nous ne remarquons pas qu'une fusion (merge) a + échoué, Mercurial nous empêchera de committer le résultat d'une fusion + ratée. + + +$ hg commit -m 'Attempt to commit a failed merge' +abort: unresolved merge conflicts (see hg resolve) + + + + + Lorsque hg commit + échoue dans ce cas, il suggère que nous utilisons la commande peu + connue hg resolve. Comme d'habitude, + hg help resolve affichera une aide + sommaire. + + + États de résolution des fichiers + + + Lorsqu'une fusion intervient, la plupart des fichiers + vont, la plupart du temps, rester sans modification. Pour chaque + fichier sur lequel Mercurial doit faire quelque chose, il suit l'état + de celui-ci. + + + Un fichier + resolved a été fusionné + (merge) avec succès, que ce soit automatiquement par Mercurial ou + manuellement par une intervention humaine. + Un fichier + unresolved n'a pas été + fusionné (merge) correctement et a besoin de plus + d'attention. + + + + Si Mercurial voit un fichier + quelconque dans un état + unresolved après une fusion (merge), il considère que + la fusion (merge) a échoué. Heureusement, nous n'avons pas à + recommencer la procédure à partir du début. + + L'option + ou passée à hg resolve liste l'état de chaque fichier + fusionné (merge). + + +$ hg resolve -l +U myfile.txt + + + + + En sortie de hg + resolve, un fichier "resolved" est marqué avec un + R, alors qu'un fichier "unresolved" est marqué + d'un U. S'il existe un fichier listé avec un + U, nous savons qu'essayer de committer le résultat + de la fusion (merge) échouera. + + + + Résoudre une fusion de fichier + + Nous avons plusieurs options pour changer l'état d'un + fichier de "unresolved" à "resolved". Le plus commun est de relancer + hg resolve. Si nous passons les noms + des fichiers individuels ou des répertoires, ceci retentera la fusion + de tous les fichiers présents à cet endroit. Nous pouvons aussi + passer l'option ou + qui tentera de fusionner + tous les fichiers "unresolved". + + Mercurial nous laisse aussi modifier la résolution + d'un fichier directement. Nous pouvons marquer un fichier "resolved" + en utilisant l'option , + ou "unresolved" en utilisant l'option . Ceci nous autorise à + nettoyer une fusion particulièrement compliquée à la main, et de + garder un suivi de nos progrès avec chaque fichier pendant que nous + procédons. + + + + + Des "diffs" plus utiles + + La sortie par défaut de la commande hg diff est compatible rétrospectivement avec + la commande régulière diff, mais ceci a quelques + inconvénients. + + Considérez le cas où nous utilisons hg + rename pour renommer un fichier. + + +$ hg rename a b +$ hg diff +diff -r f5deb7868663 a +--- a/a Sun Aug 16 14:04:49 2009 +0000 ++++ /dev/null Thu Jan 01 00:00:00 1970 +0000 +@@ -1,1 +0,0 @@ +-a +diff -r f5deb7868663 b +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/b Sun Aug 16 14:04:49 2009 +0000 +@@ -0,0 +1,1 @@ ++a + + + + + La sortie de hg diff + ci-dessus cache le fait que nous avons simplement renommé un fichier. + La commande hg diff accepte l'option + ou pour utiliser un nouveau + format de diff qui montre ces informations sous une forme plus + expressive. + + +$ hg diff -g +diff --git a/a b/b +rename from a +rename to b + + + + + Cette option peut aussi aider avec le cas autrement + confus : un fichier qui apparaît comme étant modifié en accord avec + hg status, mais où hg diff n'affiche rien. Cette situation peut + survenir si nous changeons les permissions d'exécution du + fichier. + + +$ chmod +x a +$ hg st +M a +$ hg diff + + + + + La commande normale diff ne fait pas + attention aux permissions des fichiers, ce qui explique pourquoi + hg diff n'affiche rien du tout par + défaut. Si nous lui passons l'option , ceci nous + informe de ce qu'il s'est vraiment passé. + + +$ hg diff -g +diff --git a/a b/a +old mode 100644 +new mode 100755 + + + + + + + Quels fichiers suivre et lesquels éviter + + Les systèmes de gestion de révisions sont en général + meilleurs pour gérer les fichiers textes qui sont écrits par les + humains, comme le code source, où les fichiers ne changent pas + énormément d'une révision à l'autre. Certains systèmes de gestion de + révisions centralisés peuvent aussi traiter très convenablement les + fichiers binaires, tels que les images bitmap. + + Par exemple, une équipe de développement de jeux va + probablement gérer les deux types : ses codes source et tous ses binaires + (ex. données géométriques, textures, schémas de cartes) dans un système + de contrôle de révisions. + + + Puisqu'il est d'habitude impossible de fusionner (merge) + deux modifications conflictuelles sur un fichier binaire, les systèmes + de version centralisés offrent souvent un mécanisme de verrou (lock) qui + permet à un utilisateur de dire Je suis la seule personne qui + peut éditer ce fichier. + + En comparaison avec un système centralisé, un système + décentralisé de gestion de révision change certains facteurs qui + guident les décisions sur quels fichiers gérer et comment. + + Par exemple, un système distribué de gestion de révisions + ne peut pas, par sa nature, offrir un système de véroux (lock) sur les + fichiers. Il n'y a donc pas de mécanisme inclus pour empêcher deux + personnes de faire des modifications conflictuelles sur un fichier + binaire. Si vous avez une équipe où plusieurs personnes peuvent souvent + éditer un fichier binaire, cela ne serait pas une très bonne idée + d'utiliser Mercurial —ou tout autre système distribué de gestion + de révisions—pour gérer ces fichiers. + + Lorsque vous sauvegardez les modifications sur un + fichier, Mercurial ne sauvegarde d'habitude que les différences entre + la version précédente et la version actuelle d'un fichier. Pour la + plupart des fichiers texte, ceci est très efficace. Cependant, certains + fichiers (en particulier les fichiers binaires) sont construits d'une + façon que même un petit changement sur un contenu logique résulte sur + un changement de la plupart des octets du fichier. Par exemple, les + fichiers compressés sont particulièrement sujets à ce comportement. Si + les différences entre deux versions successives d'un fichier sont + toujours très grandes, Mercurial ne sera pas capable de sauvegarder + l'historique des révisions sur le fichier très efficacement. Ceci peut + affecter aussi bien les besoins pour la sauvegarde locale que le temps + nécessaire à cloner le dépôt. + + Pour avoir une idée de comment ceci pourrait vous + affecter en pratique, supposez que nous voulions que Mercurial gère des + documents OpenOffice. OpenOffice sauvegarde les documents sur le disque + comme des fichiers compressés zip. Même le fait d'éditer ces fichiers + d'une seule lettre, changera les bits de la quasi totalité du fichier + lorsque vous le sauvegarderez. Maintenant, supposez que ce fichier + fasse une taille de 2Mo. Puisque la plupart du fichier change à chaque + fois que vous sauvegardez, Mercurial aura à sauvegarder tous les 2Mo du + fichier à chaque commit, alors que de votre point de vue, il n'y a + que peu de mots qui changent à chaque fois. Un seul fichier + souvent édité qui n'est pas bien traité par les hypothèses que Mercurial + fait sur les sauvegardes peut facilement avoir un effet colossal sur la + taille du dépôt. + + Même pire, si vous et quelqu'un d'autre éditez le même + document OpenOffice sur lequel vous travaillez, il n'y a pas de façon + utile pour fusionner votre travail. En fait, il n'y a pas de moyen + utile de montrer que les différences sont faites à partir de votre + vision des modifications. + + Il y a ainsi quelques recommandations claires sur les + types de fichiers spécifiques avec lesquels faire très + attention. + + + Les fichier qui sont très gros et + incompressibles, comme les images ISO de CD-ROM, sont, par + construction très gros et les cloner à travers un réseau sera très + long. + + Les fichiers qui changent beaucoup d'une + révision à l'autre peuvent être très chers à sauvegarder si vous + les éditez fréquemment, de même que les conflits entre deux éditions + concurrentes peuvent être difficiles à résoudre. + + + + + + Sauvegardes et miroirs + + Puisque Mercurial maintient une copie complète de + l'historique de chaque clone, toute personne qui utilise Mercurial pour + collaborer à un projet peut potentiellement agir comme une source de + sauvegarde si une catastrophe survenait. Si un dépôt central devient + indisponible, vous pouvez construire un remplaçant en clonant une copie + du dépôt à partir d'un des contributeurs en récupérant (pull) tous les + changements qui n'auraient pas été vus par les autres. + + Il est simple d'utiliser Mercurial pour construire des + serveurs hors site de sauvegarde et des miroirs distants. Initiez une + tâche périodique (ex. via la commande cron) sur un + serveur distant pour récupérer (pull) les changements de votre dépôt + distant chaque heure. Ceci sera difficile seulement dans le cas + improbable où le nombre des dépôts maîtres que vous maintenez change + souvent, auquel cas vous aurez besoin de faire un peu de scripting pour + rafraichir la liste des dépôt à sauvegarder. + + Si vous exécutez des sauvegardes traditionnelles de + votre dépôt maître sur bande ou disque, et que vous voulez sauvegarder + un dépôt nommé myrepo, utilisez la commande + hg clone -U myrepo myrepo.bak pour créer un clone de + myrepo avant de commencer vos backups. + L'option ne crée pas de répertoire de travail après + que le clone soit accompli, puisque ceci serait superflu et ferait que + la sauvegarde prenne plus de temps. + + Si vous voulez ensuite sauvegarder + myrepo.bak au lieu de myrepo, + vous aurez la garantie d'avoir une image (snapshot) consistante de + votre dépôt sur lequel un développeur insomniaque n'enverra (push) pas de + changements en milieu de sauvegarde. + + + + + + + + + + + Collaborating with other people + + As a completely decentralised tool, Mercurial doesn't impose + any policy on how people ought to work with each other. However, + if you're new to distributed revision control, it helps to have + some tools and examples in mind when you're thinking about + possible workflow models. + + + Mercurial's web interface + + Mercurial has a powerful web interface that provides several + useful capabilities. + + For interactive use, the web interface lets you browse a + single repository or a collection of repositories. You can view + the history of a repository, examine each change (comments and + diffs), and view the contents of each directory and file. You + can even get a view of history that gives a graphical view of + the relationships between individual changes and merges. + + Also for human consumption, the web interface provides + Atom and RSS feeds of the changes in a repository. This lets you + subscribe to a repository using your favorite + feed reader, and be automatically notified of activity in that + repository as soon as it happens. I find this capability much + more convenient than the model of subscribing to a mailing list + to which notifications are sent, as it requires no additional + configuration on the part of whoever is serving the + repository. + + The web interface also lets remote users clone a repository, + pull changes from it, and (when the server is configured to + permit it) push changes back to it. Mercurial's HTTP tunneling + protocol aggressively compresses data, so that it works + efficiently even over low-bandwidth network connections. + + The easiest way to get started with the web interface is to + use your web browser to visit an existing repository, such as + the master Mercurial repository at http://www.selenic.com/repo/hg. + + If you're interested in providing a web interface + to your own repositories, there are several good ways to do + this. + + The easiest and fastest way to get started in an informal + environment is to use the hg + serve command, which is best suited to short-term + lightweight serving. See below for details of how to use + this command. + + For longer-lived repositories that you'd like to + have permanently available, there are several public hosting + services available. Some are free to open source projects, + while others offer paid commercial hosting. An up-to-date list + is available at http://www.selenic.com/mercurial/wiki/index.cgi/MercurialHosting. + + If you would prefer to host your own repositories, Mercurial + has built-in support for several popular hosting technologies, + most notably CGI (Common Gateway Interface), and WSGI (Web + Services Gateway Interface). See for details of CGI and WSGI + configuration. + + + + Collaboration models + + With a suitably flexible tool, making decisions about + workflow is much more of a social engineering challenge than a + technical one. Mercurial imposes few limitations on how you can + structure the flow of work in a project, so it's up to you and + your group to set up and live with a model that matches your own + particular needs. + + + Factors to keep in mind + + The most important aspect of any model that you must keep + in mind is how well it matches the needs and capabilities of + the people who will be using it. This might seem + self-evident; even so, you still can't afford to forget it for + a moment. + + I once put together a workflow model that seemed to make + perfect sense to me, but that caused a considerable amount of + consternation and strife within my development team. In spite + of my attempts to explain why we needed a complex set of + branches, and how changes ought to flow between them, a few + team members revolted. Even though they were smart people, + they didn't want to pay attention to the constraints we were + operating under, or face the consequences of those constraints + in the details of the model that I was advocating. + + Don't sweep foreseeable social or technical problems under + the rug. Whatever scheme you put into effect, you should plan + for mistakes and problem scenarios. Consider adding automated + machinery to prevent, or quickly recover from, trouble that + you can anticipate. As an example, if you intend to have a + branch with not-for-release changes in it, you'd do well to + think early about the possibility that someone might + accidentally merge those changes into a release branch. You + could avoid this particular problem by writing a hook that + prevents changes from being merged from an inappropriate + branch. + + + + Informal anarchy + + I wouldn't suggest an anything goes + approach as something sustainable, but it's a model that's + easy to grasp, and it works perfectly well in a few unusual + situations. + + As one example, many projects have a loose-knit group of + collaborators who rarely physically meet each other. Some + groups like to overcome the isolation of working at a distance + by organizing occasional sprints. In a sprint, + a number of people get together in a single location (a + company's conference room, a hotel meeting room, that kind of + place) and spend several days more or less locked in there, + hacking intensely on a handful of projects. + + A sprint or a hacking session in a coffee shop are the perfect places to use the + hg serve command, since + hg serve does not require any + fancy server infrastructure. You can get started with + hg serve in moments, by + reading below. Then simply + tell the person next to you that you're running a server, send + the URL to them in an instant message, and you immediately + have a quick-turnaround way to work together. They can type + your URL into their web browser and quickly review your + changes; or they can pull a bugfix from you and verify it; or + they can clone a branch containing a new feature and try it + out. + + The charm, and the problem, with doing things + in an ad hoc fashion like this is that only people who know + about your changes, and where they are, can see them. Such an + informal approach simply doesn't scale beyond a handful + people, because each individual needs to know about + n different repositories to pull + from. + + + + A single central repository + + For smaller projects migrating from a centralised revision + control tool, perhaps the easiest way to get started is to + have changes flow through a single shared central repository. + This is also the most common building block for + more ambitious workflow schemes. + + Contributors start by cloning a copy of this repository. + They can pull changes from it whenever they need to, and some + (perhaps all) developers have permission to push a change back + when they're ready for other people to see it. + + Under this model, it can still often make sense for people + to pull changes directly from each other, without going + through the central repository. Consider a case in which I + have a tentative bug fix, but I am worried that if I were to + publish it to the central repository, it might subsequently + break everyone else's trees as they pull it. To reduce the + potential for damage, I can ask you to clone my repository + into a temporary repository of your own and test it. This + lets us put off publishing the potentially unsafe change until + it has had a little testing. + + If a team is hosting its own repository in this + kind of scenario, people will usually use the + ssh protocol to securely push changes to + the central repository, as documented in . It's also usual to publish a + read-only copy of the repository over HTTP, as in + . Publishing over HTTP + satisfies the needs of people who don't have push access, and + those who want to use web browsers to browse the repository's + history. + + + + A hosted central repository + + A wonderful thing about public hosting services like + Bitbucket is that + not only do they handle the fiddly server configuration + details, such as user accounts, authentication, and secure + wire protocols, they provide additional infrastructure to make + this model work well. + + For instance, a well-engineered hosting service will let + people clone their own copies of a repository with a single + click. This lets people work in separate spaces and share + their changes when they're ready. + + In addition, a good hosting service will let people + communicate with each other, for instance to say there + are changes ready for you to review in this + tree. + + + + Working with multiple branches + + Projects of any significant size naturally tend to make + progress on several fronts simultaneously. In the case of + software, it's common for a project to go through periodic + official releases. A release might then go into + maintenance mode for a while after its first + publication; maintenance releases tend to contain only bug + fixes, not new features. In parallel with these maintenance + releases, one or more future releases may be under + development. People normally use the word + branch to refer to one of these many slightly + different directions in which development is + proceeding. + + Mercurial is particularly well suited to managing a number + of simultaneous, but not identical, branches. Each + development direction can live in its own + central repository, and you can merge changes from one to + another as the need arises. Because repositories are + independent of each other, unstable changes in a development + branch will never affect a stable branch unless someone + explicitly merges those changes into the stable branch. + + Here's an example of how this can work in practice. Let's + say you have one main branch on a central + server. + + +$ hg init main +$ cd main +$ echo 'This is a boring feature.' > myfile +$ hg commit -A -m 'We have reached an important milestone!' +adding myfile + + + + + People clone it, make changes locally, test them, and push + them back. + + Once the main branch reaches a release milestone, you can + use the hg tag command to + give a permanent name to the milestone revision. + + +$ hg tag v1.0 +$ hg tip +changeset: 1:5e447fdaf941 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:47 2009 +0000 +summary: Added tag v1.0 for changeset 6412b791fd06 + +$ hg tags +tip 1:5e447fdaf941 +v1.0 0:6412b791fd06 + + + + + Let's say some ongoing + development occurs on the main branch. + + +$ cd ../main +$ echo 'This is exciting and new!' >> myfile +$ hg commit -m 'Add a new feature' +$ cat myfile +This is a boring feature. +This is exciting and new! + + + + + Using the tag that was recorded at the milestone, people + who clone that repository at any time in the future can use + hg update to get a copy of + the working directory exactly as it was when that tagged + revision was committed. + + +$ cd .. +$ hg clone -U main main-old +$ cd main-old +$ hg update v1.0 +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cat myfile +This is a boring feature. + + + + + In addition, immediately after the main branch is tagged, + we can then clone the main branch on the server to a new + stable branch, also on the server. + + +$ cd .. +$ hg clone -rv1.0 main stable +requesting all changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + If we need to make a change to the stable + branch, we can then clone that + repository, make our changes, commit, and push our changes + back there. + + +$ hg clone stable stable-fix +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd stable-fix +$ echo 'This is a fix to a boring feature.' > myfile +$ hg commit -m 'Fix a bug' +$ hg push +pushing to /tmp/branchingPsTziR/stable +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files + + + + + Because Mercurial repositories are independent, and + Mercurial doesn't move changes around automatically, the + stable and main branches are isolated + from each other. The changes that we made on the main branch + don't leak to the stable branch, and vice + versa. + + We'll often want all of our bugfixes on the stable + branch to show up on the main branch, too. Rather than + rewrite a bugfix on the main branch, we can simply pull and + merge changes from the stable to the main branch, and + Mercurial will bring those bugfixes in for us. + + +$ cd ../main +$ hg pull ../stable +pulling from ../stable +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) +$ hg merge +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ hg commit -m 'Bring in bugfix from stable branch' +$ cat myfile +This is a fix to a boring feature. +This is exciting and new! + + + + + The main branch will still contain changes that + are not on the stable branch, but it will also contain all of + the bugfixes from the stable branch. The stable branch + remains unaffected by these changes, since changes are only + flowing from the stable to the main branch, and not the other + way. + + + + Feature branches + + For larger projects, an effective way to manage change is + to break up a team into smaller groups. Each group has a + shared branch of its own, cloned from a single + master branch used by the entire project. + People working on an individual branch are typically quite + isolated from developments on other branches. + +
+ Feature branches + + + XXX add text + +
+ + When a particular feature is deemed to be in suitable + shape, someone on that feature team pulls and merges from the + master branch into the feature branch, then pushes back up to + the master branch. +
+ + + The release train + + Some projects are organized on a train + basis: a release is scheduled to happen every few months, and + whatever features are ready when the train is + ready to leave are allowed in. + + This model resembles working with feature branches. The + difference is that when a feature branch misses a train, + someone on the feature team pulls and merges the changes that + went out on that train release into the feature branch, and + the team continues its work on top of that release so that + their feature can make the next release. + + + + The Linux kernel model + + The development of the Linux kernel has a shallow + hierarchical structure, surrounded by a cloud of apparent + chaos. Because most Linux developers use + git, a distributed revision control tool + with capabilities similar to Mercurial, it's useful to + describe the way work flows in that environment; if you like + the ideas, the approach translates well across tools. + + At the center of the community sits Linus Torvalds, the + creator of Linux. He publishes a single source repository + that is considered the authoritative current + tree by the entire developer community. Anyone can clone + Linus's tree, but he is very choosy about whose trees he pulls + from. + + Linus has a number of trusted lieutenants. + As a general rule, he pulls whatever changes they publish, in + most cases without even reviewing those changes. Some of + those lieutenants are generally agreed to be + maintainers, responsible for specific + subsystems within the kernel. If a random kernel hacker wants + to make a change to a subsystem that they want to end up in + Linus's tree, they must find out who the subsystem's + maintainer is, and ask that maintainer to take their change. + If the maintainer reviews their changes and agrees to take + them, they'll pass them along to Linus in due course. + + Individual lieutenants have their own approaches to + reviewing, accepting, and publishing changes; and for deciding + when to feed them to Linus. In addition, there are several + well known branches that people use for different purposes. + For example, a few people maintain stable + repositories of older versions of the kernel, to which they + apply critical fixes as needed. Some maintainers publish + multiple trees: one for experimental changes; one for changes + that they are about to feed upstream; and so on. Others just + publish a single tree. + + This model has two notable features. The first is that + it's pull only. You have to ask, convince, or + beg another developer to take a change from you, because there + are almost no trees to which more than one person can push, + and there's no way to push changes into a tree that someone + else controls. + + The second is that it's based on reputation and acclaim. + If you're an unknown, Linus will probably ignore changes from + you without even responding. But a subsystem maintainer will + probably review them, and will likely take them if they pass + their criteria for suitability. The more good + changes you contribute to a maintainer, the more likely they + are to trust your judgment and accept your changes. If you're + well-known and maintain a long-lived branch for something + Linus hasn't yet accepted, people with similar interests may + pull your changes regularly to keep up with your work. + + Reputation and acclaim don't necessarily cross subsystem + or people boundaries. If you're a respected + but specialised storage hacker, and you try to fix a + networking bug, that change will receive a level of scrutiny + from a network maintainer comparable to a change from a + complete stranger. + + To people who come from more orderly project backgrounds, + the comparatively chaotic Linux kernel development process + often seems completely insane. It's subject to the whims of + individuals; people make sweeping changes whenever they deem + it appropriate; and the pace of development is astounding. + And yet Linux is a highly successful, well-regarded piece of + software. + + + + Pull-only versus shared-push collaboration + + A perpetual source of heat in the open source community is + whether a development model in which people only ever pull + changes from others is better than one in which + multiple people can push changes to a shared + repository. + + Typically, the backers of the shared-push model use tools + that actively enforce this approach. If you're using a + centralised revision control tool such as Subversion, there's + no way to make a choice over which model you'll use: the tool + gives you shared-push, and if you want to do anything else, + you'll have to roll your own approach on top (such as applying + a patch by hand). + + A good distributed revision control tool will + support both models. You and your collaborators can then + structure how you work together based on your own needs and + preferences, not on what contortions your tools force you + into. + + + Where collaboration meets branch management + + Once you and your team set up some shared + repositories and start propagating changes back and forth + between local and shared repos, you begin to face a related, + but slightly different challenge: that of managing the + multiple directions in which your team may be moving at once. + Even though this subject is intimately related to how your + team collaborates, it's dense enough to merit treatment of its + own, in . + +
+ + + The technical side of sharing + + The remainder of this chapter is devoted to the question of + sharing changes with your collaborators. + + + + Informal sharing with <command role="hg-cmd" moreinfo="none">hg + serve</command> + + Mercurial's hg serve + command is wonderfully suited to small, tight-knit, and + fast-paced group environments. It also provides a great way to + get a feel for using Mercurial commands over a network. + + Run hg serve inside a + repository, and in under a second it will bring up a specialised + HTTP server; this will accept connections from any client, and + serve up data for that repository until you terminate it. + Anyone who knows the URL of the server you just started, and can + talk to your computer over the network, can then use a web + browser or Mercurial to read data from that repository. A URL + for a hg serve instance running + on a laptop is likely to look something like + http://my-laptop.local:8000/. + + The hg serve command is + not a general-purpose web server. It can do + only two things: + + Allow people to browse the history of the + repository it's serving, from their normal web + browsers. + + Speak Mercurial's wire protocol, so that people + can hg clone or hg pull changes from that + repository. + + In particular, hg serve + won't allow remote users to modify your + repository. It's intended for read-only use. + + If you're getting started with Mercurial, there's nothing to + prevent you from using hg serve + to serve up a repository on your own computer, then use commands + like hg clone, hg incoming, and so on to talk to that + server as if the repository was hosted remotely. This can help + you to quickly get acquainted with using commands on + network-hosted repositories. + + + A few things to keep in mind + + Because it provides unauthenticated read access to all + clients, you should only use hg + serve in an environment where you either don't + care, or have complete control over, who can access your + network and pull data from your repository. + + The hg serve command + knows nothing about any firewall software you might have + installed on your system or network. It cannot detect or + control your firewall software. If other people are unable to + talk to a running hg serve + instance, the second thing you should do + (after you make sure that they're using + the correct URL) is check your firewall configuration. + + By default, hg serve + listens for incoming connections on port 8000. If another + process is already listening on the port you want to use, you + can specify a different port to listen on using the option. + + Normally, when hg serve + starts, it prints no output, which can be a bit unnerving. If + you'd like to confirm that it is indeed running correctly, and + find out what URL you should send to your collaborators, start + it with the + option. + + + + + Using the Secure Shell (ssh) protocol + + You can pull and push changes securely over a network + connection using the Secure Shell (ssh) + protocol. To use this successfully, you may have to do a little + bit of configuration on the client or server sides. + + If you're not familiar with ssh, it's the name of + both a command and a network protocol that let you securely + communicate with another computer. To use it with Mercurial, + you'll be setting up one or more user accounts on a server so + that remote users can log in and execute commands. + + (If you are familiar with ssh, you'll + probably find some of the material that follows to be elementary + in nature.) + + + How to read and write ssh URLs + + An ssh URL tends to look like this: + ssh://bos@hg.serpentine.com:22/hg/hgbook + + The ssh:// + part tells Mercurial to use the ssh protocol. + + The bos@ + component indicates what username to log into the server + as. You can leave this out if the remote username is the + same as your local username. + + The + hg.serpentine.com gives + the hostname of the server to log into. + + The :22 identifies the port + number to connect to the server on. The default port is + 22, so you only need to specify a colon and port number if + you're not using port 22. + + The remainder of the URL is the local path to + the repository on the server. + + + There's plenty of scope for confusion with the path + component of ssh URLs, as there is no standard way for tools + to interpret it. Some programs behave differently than others + when dealing with these paths. This isn't an ideal situation, + but it's unlikely to change. Please read the following + paragraphs carefully. + + Mercurial treats the path to a repository on the server as + relative to the remote user's home directory. For example, if + user foo on the server has a home directory + of /home/foo, then an + ssh URL that contains a path component of bar really + refers to the directory /home/foo/bar. + + If you want to specify a path relative to another user's + home directory, you can use a path that starts with a tilde + character followed by the user's name (let's call them + otheruser), like this. + ssh://server/~otheruser/hg/repo + + And if you really want to specify an + absolute path on the server, begin the + path component with two slashes, as in this example. + ssh://server//absolute/path + + + + Finding an ssh client for your system + + Almost every Unix-like system comes with OpenSSH + preinstalled. If you're using such a system, run + which ssh to find out if the + ssh command is installed (it's usually in + /usr/bin). In the + unlikely event that it isn't present, take a look at your + system documentation to figure out how to install it. + + On Windows, the TortoiseHg package is bundled + with a version of Simon Tatham's excellent + plink command, and you should not need to + do any further configuration. + + + + Generating a key pair + + To avoid the need to repetitively type a + password every time you need to use your ssh client, I + recommend generating a key pair. + + + Key pairs are not mandatory + + Mercurial knows nothing about ssh authentication or key + pairs. You can, if you like, safely ignore this section and + the one that follows until you grow tired of repeatedly + typing ssh passwords. + + + + + On a Unix-like system, the + ssh-keygen command will do the + trick. + On Windows, if you're using TortoiseHg, you may need + to download a command named puttygen + from the + PuTTY web site to generate a key pair. See + the + puttygen documentation for + details of how use the command. + + + + When you generate a key pair, it's usually + highly advisable to protect it with a + passphrase. (The only time that you might not want to do this + is when you're using the ssh protocol for automated tasks on a + secure network.) + + Simply generating a key pair isn't enough, however. + You'll need to add the public key to the set of authorised + keys for whatever user you're logging in remotely as. For + servers using OpenSSH (the vast majority), this will mean + adding the public key to a list in a file called authorized_keys in their .ssh + directory. + + On a Unix-like system, your public key will have a + .pub extension. If you're using + puttygen on Windows, you can save the + public key to a file of your choosing, or paste it from the + window it's displayed in straight into the authorized_keys file. + + + Using an authentication agent + + An authentication agent is a daemon that stores + passphrases in memory (so it will forget passphrases if you + log out and log back in again). An ssh client will notice if + it's running, and query it for a passphrase. If there's no + authentication agent running, or the agent doesn't store the + necessary passphrase, you'll have to type your passphrase + every time Mercurial tries to communicate with a server on + your behalf (e.g. whenever you pull or push changes). + + The downside of storing passphrases in an agent is that + it's possible for a well-prepared attacker to recover the + plain text of your passphrases, in some cases even if your + system has been power-cycled. You should make your own + judgment as to whether this is an acceptable risk. It + certainly saves a lot of repeated typing. + + + + On Unix-like systems, the agent is called + ssh-agent, and it's often run + automatically for you when you log in. You'll need to use + the ssh-add command to add passphrases + to the agent's store. + + + On Windows, if you're using TortoiseHg, the + pageant command acts as the agent. As + with puttygen, you'll need to download + pageant from the PuTTY web + site and read its + documentation. The pageant + command adds an icon to your system tray that will let you + manage stored passphrases. + + + + + + Configuring the server side properly + + Because ssh can be fiddly to set up if you're new to it, + a variety of things can go wrong. Add Mercurial + on top, and there's plenty more scope for head-scratching. + Most of these potential problems occur on the server side, not + the client side. The good news is that once you've gotten a + configuration working, it will usually continue to work + indefinitely. + + Before you try using Mercurial to talk to an ssh server, + it's best to make sure that you can use the normal + ssh or putty command to + talk to the server first. If you run into problems with using + these commands directly, Mercurial surely won't work. Worse, + it will obscure the underlying problem. Any time you want to + debug ssh-related Mercurial problems, you should drop back to + making sure that plain ssh client commands work first, + before you worry about whether there's a + problem with Mercurial. + + The first thing to be sure of on the server side is that + you can actually log in from another machine at all. If you + can't use ssh or putty + to log in, the error message you get may give you a few hints + as to what's wrong. The most common problems are as + follows. + + If you get a connection refused + error, either there isn't an SSH daemon running on the + server at all, or it's inaccessible due to firewall + configuration. + + If you get a no route to host + error, you either have an incorrect address for the server + or a seriously locked down firewall that won't admit its + existence at all. + + If you get a permission denied + error, you may have mistyped the username on the server, + or you could have mistyped your key's passphrase or the + remote user's password. + + In summary, if you're having trouble talking to the + server's ssh daemon, first make sure that one is running at + all. On many systems it will be installed, but disabled, by + default. Once you're done with this step, you should then + check that the server's firewall is configured to allow + incoming connections on the port the ssh daemon is listening + on (usually 22). Don't worry about more exotic possibilities + for misconfiguration until you've checked these two + first. + + If you're using an authentication agent on the client side + to store passphrases for your keys, you ought to be able to + log into the server without being prompted for a passphrase or + a password. If you're prompted for a passphrase, there are a + few possible culprits. + + You might have forgotten to use + ssh-add or pageant + to store the passphrase. + + You might have stored the passphrase for the + wrong key. + + If you're being prompted for the remote user's password, + there are another few possible problems to check. + + Either the user's home directory or their + .ssh + directory might have excessively liberal permissions. As + a result, the ssh daemon will not trust or read their + authorized_keys file. + For example, a group-writable home or .ssh + directory will often cause this symptom. + + The user's authorized_keys file may have + a problem. If anyone other than the user owns or can write + to that file, the ssh daemon will not trust or read + it. + + + In the ideal world, you should be able to run the + following command successfully, and it should print exactly + one line of output, the current date and time. + ssh myserver date + + If, on your server, you have login scripts that print + banners or other junk even when running non-interactive + commands like this, you should fix them before you continue, + so that they only print output if they're run interactively. + Otherwise these banners will at least clutter up Mercurial's + output. Worse, they could potentially cause problems with + running Mercurial commands remotely. Mercurial tries to + detect and ignore banners in non-interactive + ssh sessions, but it is not foolproof. (If + you're editing your login scripts on your server, the usual + way to see if a login script is running in an interactive + shell is to check the return code from the command + tty -s.) + + Once you've verified that plain old ssh is working with + your server, the next step is to ensure that Mercurial runs on + the server. The following command should run + successfully: + + ssh myserver hg version + + If you see an error message instead of normal hg version output, this is usually + because you haven't installed Mercurial to /usr/bin. Don't worry if this + is the case; you don't need to do that. But you should check + for a few possible problems. + + Is Mercurial really installed on the server at + all? I know this sounds trivial, but it's worth + checking! + + Maybe your shell's search path (usually set + via the PATH environment variable) is + simply misconfigured. + + Perhaps your PATH environment + variable is only being set to point to the location of the + hg executable if the login session is + interactive. This can happen if you're setting the path + in the wrong shell login script. See your shell's + documentation for details. + + The PYTHONPATH environment + variable may need to contain the path to the Mercurial + Python modules. It might not be set at all; it could be + incorrect; or it may be set only if the login is + interactive. + + + If you can run hg version + over an ssh connection, well done! You've got the server and + client sorted out. You should now be able to use Mercurial to + access repositories hosted by that username on that server. + If you run into problems with Mercurial and ssh at this point, + try using the + option to get a clearer picture of what's going on. + + + Using compression with ssh + + Mercurial does not compress data when it uses the ssh + protocol, because the ssh protocol can transparently compress + data. However, the default behavior of ssh clients is + not to request compression. + + Over any network other than a fast LAN (even a wireless + network), using compression is likely to significantly speed + up Mercurial's network operations. For example, over a WAN, + someone measured compression as reducing the amount of time + required to clone a particularly large repository from 51 + minutes to 17 minutes. + + Both ssh and plink + accept a option which + turns on compression. You can easily edit your ~/.hgrc to enable compression for + all of Mercurial's uses of the ssh protocol. Here is how to + do so for regular ssh on Unix-like systems, + for example. + [ui] +ssh = ssh -C + + If you use ssh on a + Unix-like system, you can configure it to always use + compression when talking to your server. To do this, edit + your .ssh/config file + (which may not yet exist), as follows. + + Host hg + Compression yes + HostName hg.example.com + + This defines a hostname alias, + hg. When you use that hostname on the + ssh command line or in a Mercurial + ssh-protocol URL, it will cause + ssh to connect to + hg.example.com and use compression. This + gives you both a shorter name to type and compression, each of + which is a good thing in its own right. + + + + + Serving over HTTP using CGI + + The simplest way to host one or more repositories in a + permanent way is to use a web server and Mercurial's CGI + support. + + Depending on how ambitious you are, configuring Mercurial's + CGI interface can take anything from a few moments to several + hours. + + We'll begin with the simplest of examples, and work our way + towards a more complex configuration. Even for the most basic + case, you're almost certainly going to need to read and modify + your web server's configuration. + + + High pain tolerance required + + Configuring a web server is a complex, fiddly, + and highly system-dependent activity. I can't possibly give + you instructions that will cover anything like all of the + cases you will encounter. Please use your discretion and + judgment in following the sections below. Be prepared to make + plenty of mistakes, and to spend a lot of time reading your + server's error logs. + + If you don't have a strong stomach for tweaking + configurations over and over, or a compelling need to host + your own services, you might want to try one of the public + hosting services that I mentioned earlier. + + + + Web server configuration checklist + + Before you continue, do take a few moments to check a few + aspects of your system's setup. + + + Do you have a web server installed + at all? Mac OS X and some Linux distributions ship with + Apache, but many other systems may not have a web server + installed. + + If you have a web server installed, is it + actually running? On most systems, even if one is + present, it will be disabled by default. + + Is your server configured to allow you to run + CGI programs in the directory where you plan to do so? + Most servers default to explicitly disabling the ability + to run CGI programs. + + + If you don't have a web server installed, and don't have + substantial experience configuring Apache, you should consider + using the lighttpd web server instead of + Apache. Apache has a well-deserved reputation for baroque and + confusing configuration. While lighttpd is + less capable in some ways than Apache, most of these + capabilities are not relevant to serving Mercurial + repositories. And lighttpd is undeniably + much easier to get started with than + Apache. + + + + Basic CGI configuration + + On Unix-like systems, it's common for users to have a + subdirectory named something like public_html in their home + directory, from which they can serve up web pages. A file + named foo in this directory will be + accessible at a URL of the form + http://www.example.com/username/foo. + + To get started, find the hgweb.cgi script that should be + present in your Mercurial installation. If you can't quickly + find a local copy on your system, simply download one from the + master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgweb.cgi. + + You'll need to copy this script into your public_html directory, and + ensure that it's executable. + cp .../hgweb.cgi ~/public_html +chmod 755 ~/public_html/hgweb.cgi + The 755 argument to + chmod is a little more general than just + making the script executable: it ensures that the script is + executable by anyone, and that group and + other write permissions are + not set. If you were to leave those + write permissions enabled, Apache's suexec + subsystem would likely refuse to execute the script. In fact, + suexec also insists that the + directory in which the script resides + must not be writable by others. + chmod 755 ~/public_html + + + What could <emphasis>possibly</emphasis> go + wrong? + + Once you've copied the CGI script into place, + go into a web browser, and try to open the URL + http://myhostname/~myuser/hgweb.cgi, + but brace yourself for instant failure. + There's a high probability that trying to visit this URL + will fail, and there are many possible reasons for this. In + fact, you're likely to stumble over almost every one of the + possible errors below, so please read carefully. The + following are all of the problems I ran into on a system + running Fedora 7, with a fresh installation of Apache, and a + user account that I created specially to perform this + exercise. + + Your web server may have per-user directories disabled. + If you're using Apache, search your config file for a + UserDir directive. If there's none + present, per-user directories will be disabled. If one + exists, but its value is disabled, then + per-user directories will be disabled. Otherwise, the + string after UserDir gives the name of + the subdirectory that Apache will look in under your home + directory, for example public_html. + + Your file access permissions may be too restrictive. + The web server must be able to traverse your home directory + and directories under your public_html directory, and + read files under the latter too. Here's a quick recipe to + help you to make your permissions more appropriate. + chmod 755 ~ +find ~/public_html -type d -print0 | xargs -0r chmod 755 +find ~/public_html -type f -print0 | xargs -0r chmod 644 + + The other possibility with permissions is that you might + get a completely empty window when you try to load the + script. In this case, it's likely that your access + permissions are too permissive. Apache's + suexec subsystem won't execute a script + that's group- or world-writable, for example. + + Your web server may be configured to disallow execution + of CGI programs in your per-user web directory. Here's + Apache's default per-user configuration from my Fedora + system. + + +<Directory /home/*/public_html> + AllowOverride FileInfo AuthConfig Limit + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + <Limit GET POST OPTIONS> + Order allow,deny + Allow from all + </Limit> + <LimitExcept GET POST OPTIONS> + Order deny,allow Deny from all + </LimitExcept> +</Directory> + + + + If you find a similar-looking + Directory group in your Apache + configuration, the directive to look at inside it is + Options. Add ExecCGI + to the end of this list if it's missing, and restart the web + server. + + If you find that Apache serves you the text of the CGI + script instead of executing it, you may need to either + uncomment (if already present) or add a directive like + this. + AddHandler cgi-script .cgi + + The next possibility is that you might be served with a + colourful Python backtrace claiming that it can't import a + mercurial-related module. This is + actually progress! The server is now capable of executing + your CGI script. This error is only likely to occur if + you're running a private installation of Mercurial, instead + of a system-wide version. Remember that the web server runs + the CGI program without any of the environment variables + that you take for granted in an interactive session. If + this error happens to you, edit your copy of hgweb.cgi and follow the + directions inside it to correctly set your + PYTHONPATH environment variable. + + Finally, you are certain to be + served with another colourful Python backtrace: this one + will complain that it can't find /path/to/repository. Edit + your hgweb.cgi script + and replace the /path/to/repository string + with the complete path to the repository you want to serve + up. + + At this point, when you try to reload the page, you + should be presented with a nice HTML view of your + repository's history. Whew! + + + + Configuring lighttpd + + To be exhaustive in my experiments, I tried configuring + the increasingly popular lighttpd web + server to serve the same repository as I described with + Apache above. I had already overcome all of the problems I + outlined with Apache, many of which are not server-specific. + As a result, I was fairly sure that my file and directory + permissions were good, and that my hgweb.cgi script was properly + edited. + + Once I had Apache running, getting + lighttpd to serve the repository was a + snap (in other words, even if you're trying to use + lighttpd, you should read the Apache + section). I first had to edit the + mod_access section of its config file to + enable mod_cgi and + mod_userdir, both of which were disabled + by default on my system. I then added a few lines to the + end of the config file, to configure these modules. + userdir.path = "public_html" +cgi.assign = (".cgi" => "" ) + With this done, lighttpd ran + immediately for me. If I had configured + lighttpd before Apache, I'd almost + certainly have run into many of the same system-level + configuration problems as I did with Apache. However, I + found lighttpd to be noticeably easier to + configure than Apache, even though I've used Apache for over + a decade, and this was my first exposure to + lighttpd. + + + + + Sharing multiple repositories with one CGI script + + The hgweb.cgi script + only lets you publish a single repository, which is an + annoying restriction. If you want to publish more than one + without wracking yourself with multiple copies of the same + script, each with different names, a better choice is to use + the hgwebdir.cgi + script. + + The procedure to configure hgwebdir.cgi is only a little more + involved than for hgweb.cgi. First, you must obtain + a copy of the script. If you don't have one handy, you can + download a copy from the master Mercurial repository at http://www.selenic.com/repo/hg/raw-file/tip/hgwebdir.cgi. + + You'll need to copy this script into your public_html directory, and + ensure that it's executable. + + cp .../hgwebdir.cgi ~/public_html +chmod 755 ~/public_html ~/public_html/hgwebdir.cgi + + With basic configuration out of the way, try to + visit http://myhostname/~myuser/hgwebdir.cgi + in your browser. It should + display an empty list of repositories. If you get a blank + window or error message, try walking through the list of + potential problems in . + + The hgwebdir.cgi + script relies on an external configuration file. By default, + it searches for a file named hgweb.config in the same directory + as itself. You'll need to create this file, and make it + world-readable. The format of the file is similar to a + Windows ini file, as understood by Python's + ConfigParser + web:configparser module. + + The easiest way to configure hgwebdir.cgi is with a section + named collections. This will automatically + publish every repository under the + directories you name. The section should look like + this: + [collections] +/my/root = /my/root + Mercurial interprets this by looking at the directory name + on the right hand side of the + = sign; finding repositories + in that directory hierarchy; and using the text on the + left to strip off matching text from the + names it will actually list in the web interface. The + remaining component of a path after this stripping has + occurred is called a virtual path. + + Given the example above, if we have a + repository whose local path is /my/root/this/repo, the CGI + script will strip the leading /my/root from the name, and + publish the repository with a virtual path of this/repo. If the base URL for + our CGI script is + http://myhostname/~myuser/hgwebdir.cgi, the + complete URL for that repository will be + http://myhostname/~myuser/hgwebdir.cgi/this/repo. + + If we replace /my/root on the left hand side + of this example with /my, then hgwebdir.cgi will only strip off + /my from the repository + name, and will give us a virtual path of root/this/repo instead of + this/repo. + + The hgwebdir.cgi + script will recursively search each directory listed in the + collections section of its configuration + file, but it will not recurse into the + repositories it finds. + + The collections mechanism makes it easy + to publish many repositories in a fire and + forget manner. You only need to set up the CGI + script and configuration file one time. Afterwards, you can + publish or unpublish a repository at any time by simply moving + it into, or out of, the directory hierarchy in which you've + configured hgwebdir.cgi to + look. + + + Explicitly specifying which repositories to + publish + + In addition to the collections + mechanism, the hgwebdir.cgi script allows you + to publish a specific list of repositories. To do so, + create a paths section, with contents of + the following form. + [paths] +repo1 = /my/path/to/some/repo +repo2 = /some/path/to/another + In this case, the virtual path (the component that will + appear in a URL) is on the left hand side of each + definition, while the path to the repository is on the + right. Notice that there does not need to be any + relationship between the virtual path you choose and the + location of a repository in your filesystem. + + If you wish, you can use both the + collections and paths + mechanisms simultaneously in a single configuration + file. + + + Beware duplicate virtual paths + + If several repositories have the same + virtual path, hgwebdir.cgi will not report + an error. Instead, it will behave unpredictably. + + + + + + Downloading source archives + + Mercurial's web interface lets users download an archive + of any revision. This archive will contain a snapshot of the + working directory as of that revision, but it will not contain + a copy of the repository data. + + By default, this feature is not enabled. To enable it, + you'll need to add an allow_archive item to the + web section of your ~/.hgrc; see below for details. + + + Web configuration options + + Mercurial's web interfaces (the hg + serve command, and the hgweb.cgi and hgwebdir.cgi scripts) have a + number of configuration options that you can set. These + belong in a section named web. + + allow_archive: Determines + which (if any) archive download mechanisms Mercurial + supports. If you enable this feature, users of the web + interface will be able to download an archive of whatever + revision of a repository they are viewing. To enable the + archive feature, this item must take the form of a + sequence of words drawn from the list below. + + bz2: A + tar archive, compressed using + bzip2 compression. This has the + best compression ratio, but uses the most CPU time on + the server. + + gz: A + tar archive, compressed using + gzip compression. + + zip: A + zip archive, compressed using LZW + compression. This format has the worst compression + ratio, but is widely used in the Windows world. + + + If you provide an empty list, or don't have an + allow_archive entry at + all, this feature will be disabled. Here is an example of + how to enable all three supported formats. + [web] +allow_archive = bz2 gz zip + + allowpull: + Boolean. Determines whether the web interface allows + remote users to hg pull + and hg clone this + repository over HTTP. If set to no or + false, only the + human-oriented portion of the web interface + is available. + + contact: + String. A free-form (but preferably brief) string + identifying the person or group in charge of the + repository. This often contains the name and email + address of a person or mailing list. It often makes sense + to place this entry in a repository's own .hg/hgrc file, but it can make + sense to use in a global ~/.hgrc if every repository + has a single maintainer. + + maxchanges: + Integer. The default maximum number of changesets to + display in a single page of output. + + maxfiles: + Integer. The default maximum number of modified files to + display in a single page of output. + + stripes: + Integer. If the web interface displays alternating + stripes to make it easier to visually align + rows when you are looking at a table, this number controls + the number of rows in each stripe. + + style: Controls the template + Mercurial uses to display the web interface. Mercurial + ships with several web templates. + + + coal is monochromatic. + + + gitweb emulates the visual + style of git's web interface. + + + monoblue uses solid blues and + greys. + + + paper is the default. + + + spartan was the default for a + long time. + + + You can + also specify a custom template of your own; see + for details. Here, you can + see how to enable the gitweb + style. + [web] +style = gitweb + + templates: + Path. The directory in which to search for template + files. By default, Mercurial searches in the directory in + which it was installed. + + If you are using hgwebdir.cgi, you can place a few + configuration items in a web + section of the hgweb.config file instead of a + ~/.hgrc file, for + convenience. These items are motd and style. + + + Options specific to an individual repository + + A few web configuration + items ought to be placed in a repository's local .hg/hgrc, rather than a user's + or global ~/.hgrc. + + description: String. A + free-form (but preferably brief) string that describes + the contents or purpose of the repository. + + name: + String. The name to use for the repository in the web + interface. This overrides the default name, which is + the last component of the repository's path. + + + + + Options specific to the <command role="hg-cmd" moreinfo="none">hg + serve</command> command + + Some of the items in the web section of a ~/.hgrc file are only for use + with the hg serve + command. + + accesslog: + Path. The name of a file into which to write an access + log. By default, the hg + serve command writes this information to + standard output, not to a file. Log entries are written + in the standard combined file format used + by almost all web servers. + + address: + String. The local address on which the server should + listen for incoming connections. By default, the server + listens on all addresses. + + errorlog: + Path. The name of a file into which to write an error + log. By default, the hg + serve command writes this information to + standard error, not to a file. + + ipv6: + Boolean. Whether to use the IPv6 protocol. By default, + IPv6 is not used. + + port: + Integer. The TCP port number on which the server should + listen. The default port number used is 8000. + + + + + Choosing the right <filename role="special" moreinfo="none">~/.hgrc</filename> file to add <literal role="rc-web" moreinfo="none">web</literal> items to + + It is important to remember that a web server like + Apache or lighttpd will run under a user + ID that is different to yours. CGI scripts run by your + server, such as hgweb.cgi, will usually also run + under that user ID. + + If you add web items to + your own personal ~/.hgrc file, CGI scripts won't read that + ~/.hgrc file. Those + settings will thus only affect the behavior of the hg serve command when you run it. + To cause CGI scripts to see your settings, either create a + ~/.hgrc file in the + home directory of the user ID that runs your web server, or + add those settings to a system-wide hgrc file. + + + + + + System-wide configuration + + On Unix-like systems shared by multiple users (such as a + server to which people publish changes), it often makes sense to + set up some global default behaviors, such as what theme to use + in web interfaces. + + If a file named /etc/mercurial/hgrc + exists, Mercurial will read it at startup time and apply any + configuration settings it finds in that file. It will also look + for files ending in a .rc extension in a + directory named /etc/mercurial/hgrc.d, and + apply any configuration settings it finds in each of those + files. + + + Making Mercurial more trusting + + One situation in which a global hgrc + can be useful is if users are pulling changes owned by other + users. By default, Mercurial will not trust most of the + configuration items in a .hg/hgrc file + inside a repository that is owned by a different user. If we + clone or pull changes from such a repository, Mercurial will + print a warning stating that it does not trust their + .hg/hgrc. + + If everyone in a particular Unix group is on the same team + and should trust each other's + configuration settings, or we want to trust particular users, + we can override Mercurial's skeptical defaults by creating a + system-wide hgrc file such as the + following: + + # Save this as e.g. /etc/mercurial/hgrc.d/trust.rc +[trusted] +# Trust all entries in any hgrc file owned by the "editors" or +# "www-data" groups. +groups = editors, www-data + +# Trust entries in hgrc files owned by the following users. +users = apache, bobo + + + +
+ + + + + + + + + File names and pattern matching + + Mercurial provides mechanisms that let you work with file + names in a consistent and expressive way. + + + Simple file naming + + Mercurial uses a unified piece of machinery under the + hood to handle file names. Every command behaves + uniformly with respect to file names. The way in which commands + work with file names is as follows. + + If you explicitly name real files on the command line, + Mercurial works with exactly those files, as you would expect. + +$ hg add COPYING README examples/simple.py + + + + + When you provide a directory name, Mercurial will interpret + this as operate on every file in this directory and its + subdirectories. Mercurial traverses the files and + subdirectories in a directory in alphabetical order. When it + encounters a subdirectory, it will traverse that subdirectory + before continuing with the current directory. + + +$ hg status src +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt + + + + + + + Running commands without any file names + + Mercurial's commands that work with file names have useful + default behaviors when you invoke them without providing any + file names or patterns. What kind of behavior you should + expect depends on what the command does. Here are a few rules + of thumb you can use to predict what a command is likely to do + if you don't give it any names to work with. + + Most commands will operate on the entire working + directory. This is what the hg + add command does, for example. + + If the command has effects that are difficult or + impossible to reverse, it will force you to explicitly + provide at least one name or pattern (see below). This + protects you from accidentally deleting files by running + hg remove with no + arguments, for example. + + + It's easy to work around these default behaviors if they + don't suit you. If a command normally operates on the whole + working directory, you can invoke it on just the current + directory and its subdirectories by giving it the name + .. + + +$ cd src +$ hg add -n +adding ../MANIFEST.in +adding ../examples/performant.py +adding ../setup.py +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt +$ hg add -n . +adding main.py +adding watcher/_watcher.c +adding watcher/watcher.py +adding xyzzy.txt + + + + + Along the same lines, some commands normally print file + names relative to the root of the repository, even if you're + invoking them from a subdirectory. Such a command will print + file names relative to your subdirectory if you give it explicit + names. Here, we're going to run hg + status from a subdirectory, and get it to operate on + the entire working directory while printing file names relative + to our subdirectory, by passing it the output of the hg root command. + + +$ hg status +A COPYING +A README +A examples/simple.py +? MANIFEST.in +? examples/performant.py +? setup.py +? src/main.py +? src/watcher/_watcher.c +? src/watcher/watcher.py +? src/xyzzy.txt +$ hg status `hg root` +A ../COPYING +A ../README +A ../examples/simple.py +? ../MANIFEST.in +? ../examples/performant.py +? ../setup.py +? main.py +? watcher/_watcher.c +? watcher/watcher.py +? xyzzy.txt + + + + + + + Telling you what's going on + + The hg add example in the + preceding section illustrates something else that's helpful + about Mercurial commands. If a command operates on a file that + you didn't name explicitly on the command line, it will usually + print the name of the file, so that you will not be surprised + what's going on. + + The principle here is of least + surprise. If you've exactly named a file on the + command line, there's no point in repeating it back at you. If + Mercurial is acting on a file implicitly, e.g. + because you provided no names, or a directory, or a pattern (see + below), it is safest to tell you what files it's operating on. + + For commands that behave this way, you can silence them + using the option. You + can also get them to print the name of every file, even those + you've named explicitly, using the option. + + + + Using patterns to identify files + + In addition to working with file and directory names, + Mercurial lets you use patterns to identify + files. Mercurial's pattern handling is expressive. + + On Unix-like systems (Linux, MacOS, etc.), the job of + matching file names to patterns normally falls to the shell. On + these systems, you must explicitly tell Mercurial that a name is + a pattern. On Windows, the shell does not expand patterns, so + Mercurial will automatically identify names that are patterns, + and expand them for you. + + To provide a pattern in place of a regular name on the + command line, the mechanism is simple: + syntax:patternbody + That is, a pattern is identified by a short text string that + says what kind of pattern this is, followed by a colon, followed + by the actual pattern. + + Mercurial supports two kinds of pattern syntax. The most + frequently used is called glob; this is the + same kind of pattern matching used by the Unix shell, and should + be familiar to Windows command prompt users, too. + + When Mercurial does automatic pattern matching on Windows, + it uses glob syntax. You can thus omit the + glob: prefix on Windows, but + it's safe to use it, too. + + The re syntax is more powerful; it lets + you specify patterns using regular expressions, also known as + regexps. + + By the way, in the examples that follow, notice that I'm + careful to wrap all of my patterns in quote characters, so that + they won't get expanded by the shell before Mercurial sees + them. + + + Shell-style <literal moreinfo="none">glob</literal> patterns + + This is an overview of the kinds of patterns you can use + when you're matching on glob patterns. + + The * character matches + any string, within a single directory. + + +$ hg add 'glob:*.py' +adding main.py + + + + + The ** pattern matches + any string, and crosses directory boundaries. It's not a + standard Unix glob token, but it's accepted by several popular + Unix shells, and is very useful. + + +$ cd .. +$ hg status 'glob:**.py' +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py + + + + + The ? pattern matches + any single character. + + +$ hg status 'glob:**.?' +? src/watcher/_watcher.c + + + + + The [ character begins a + character class. This matches any single + character within the class. The class ends with a + ] character. A class may + contain multiple ranges of the form + a-f, which is shorthand for + abcdef. + + +$ hg status 'glob:**[nr-t]' +? MANIFEST.in +? src/xyzzy.txt + + + + + If the first character after the + [ in a character class is a + !, it + negates the class, making it match any + single character not in the class. + + A { begins a group of + subpatterns, where the whole group matches if any subpattern + in the group matches. The , + character separates subpatterns, and + } ends the group. + + +$ hg status 'glob:*.{in,py}' +? MANIFEST.in +? setup.py + + + + + + Watch out! + + Don't forget that if you want to match a pattern in any + directory, you should not be using the + * match-any token, as this + will only match within one directory. Instead, use the + ** token. This small + example illustrates the difference between the two. + + +$ hg status 'glob:*.py' +? setup.py +$ hg status 'glob:**.py' +A examples/simple.py +A src/main.py +? examples/performant.py +? setup.py +? src/watcher/watcher.py + + + + + + + + Regular expression matching with <literal moreinfo="none">re</literal> + patterns + + Mercurial accepts the same regular expression syntax as + the Python programming language (it uses Python's regexp + engine internally). This is based on the Perl language's + regexp syntax, which is the most popular dialect in use (it's + also used in Java, for example). + + I won't discuss Mercurial's regexp dialect in any detail + here, as regexps are not often used. Perl-style regexps are + in any case already exhaustively documented on a multitude of + web sites, and in many books. Instead, I will focus here on a + few things you should know if you find yourself needing to use + regexps with Mercurial. + + A regexp is matched against an entire file name, relative + to the root of the repository. In other words, even if you're + already in subbdirectory foo, if you want to match files + under this directory, your pattern must start with + foo/. + + One thing to note, if you're familiar with Perl-style + regexps, is that Mercurial's are rooted. + That is, a regexp starts matching against the beginning of a + string; it doesn't look for a match anywhere within the + string. To match anywhere in a string, start your pattern + with .*. + + + + + Filtering files + + Not only does Mercurial give you a variety of ways to + specify files; it lets you further winnow those files using + filters. Commands that work with file + names accept two filtering options. + + , or + , lets you + specify a pattern that file names must match in order to be + processed. + + , or + , gives you a + way to avoid processing files, if they + match this pattern. + + You can provide multiple and options on the command line, + and intermix them as you please. Mercurial interprets the + patterns you provide using glob syntax by default (but you can + use regexps if you need to). + + You can read a + filter as process only the files that match this + filter. + + +$ hg status -I '*.in' +? MANIFEST.in + + + + + The filter is best + read as process only the files that don't match this + pattern. + + +$ hg status -X '**.py' src +? src/watcher/_watcher.c +? src/xyzzy.txt + + + + + + + Permanently ignoring unwanted files and directories + + When you create a new repository, the chances are + that over time it will grow to contain files that ought to + not be managed by Mercurial, but which you + don't want to see listed every time you run hg + status. For instance, build products + are files that are created as part of a build but which should + not be managed by a revision control system. The most common + build products are output files produced by software tools such + as compilers. As another example, many text editors litter a + directory with lock files, temporary working files, and backup + files, which it also makes no sense to manage. + + To have Mercurial permanently ignore such files, create a + file named .hgignore in the root of your + repository. You should hg + add this file so that it gets tracked with the rest of + your repository contents, since your collaborators will probably + find it useful too. + + By default, the .hgignore file should + contain a list of regular expressions, one per line. Empty + lines are skipped. Most people prefer to describe the files they + want to ignore using the glob syntax that we + described above, so a typical .hgignore + file will start with this directive: + + syntax: glob + + This tells Mercurial to interpret the lines that follow as + glob patterns, not regular expressions. + + Here is a typical-looking .hgignore + file. + + syntax: glob +# This line is a comment, and will be skipped. +# Empty lines are skipped too. + +# Backup files left behind by the Emacs editor. +*~ + +# Lock files used by the Emacs editor. +# Notice that the "#" character is quoted with a backslash. +# This prevents it from being interpreted as starting a comment. +.\#* + +# Temporary files used by the vim editor. +.*.swp + +# A hidden file created by the Mac OS X Finder. +.DS_Store + + + + + Case sensitivity + + If you're working in a mixed development environment that + contains both Linux (or other Unix) systems and Macs or Windows + systems, you should keep in the back of your mind the knowledge + that they treat the case (N versus + n) of file names in incompatible ways. This is + not very likely to affect you, and it's easy to deal with if it + does, but it could surprise you if you don't know about + it. + + Operating systems and filesystems differ in the way they + handle the case of characters in file and + directory names. There are three common ways to handle case in + names. + + Completely case insensitive. Uppercase and + lowercase versions of a letter are treated as identical, + both when creating a file and during subsequent accesses. + This is common on older DOS-based systems. + + Case preserving, but insensitive. When a file + or directory is created, the case of its name is stored, and + can be retrieved and displayed by the operating system. + When an existing file is being looked up, its case is + ignored. This is the standard arrangement on Windows and + MacOS. The names foo and + FoO identify the same file. This + treatment of uppercase and lowercase letters as + interchangeable is also referred to as case + folding. + + Case sensitive. The case of a name + is significant at all times. The names + foo and FoO + identify different files. This is the way Linux and Unix + systems normally work. + + + On Unix-like systems, it is possible to have any or all of + the above ways of handling case in action at once. For example, + if you use a USB thumb drive formatted with a FAT32 filesystem + on a Linux system, Linux will handle names on that filesystem in + a case preserving, but insensitive, way. + + + Safe, portable repository storage + + Mercurial's repository storage mechanism is case + safe. It translates file names so that they can + be safely stored on both case sensitive and case insensitive + filesystems. This means that you can use normal file copying + tools to transfer a Mercurial repository onto, for example, a + USB thumb drive, and safely move that drive and repository + back and forth between a Mac, a PC running Windows, and a + Linux box. + + + + Detecting case conflicts + + When operating in the working directory, Mercurial honours + the naming policy of the filesystem where the working + directory is located. If the filesystem is case preserving, + but insensitive, Mercurial will treat names that differ only + in case as the same. + + An important aspect of this approach is that it is + possible to commit a changeset on a case sensitive (typically + Linux or Unix) filesystem that will cause trouble for users on + case insensitive (usually Windows and MacOS) users. If a + Linux user commits changes to two files, one named + myfile.c and the other named + MyFile.C, they will be stored correctly + in the repository. And in the working directories of other + Linux users, they will be correctly represented as separate + files. + + If a Windows or Mac user pulls this change, they will not + initially have a problem, because Mercurial's repository + storage mechanism is case safe. However, once they try to + hg update the working + directory to that changeset, or hg + merge with that changeset, Mercurial will spot the + conflict between the two file names that the filesystem would + treat as the same, and forbid the update or merge from + occurring. + + + + Fixing a case conflict + + If you are using Windows or a Mac in a mixed environment + where some of your collaborators are using Linux or Unix, and + Mercurial reports a case folding conflict when you try to + hg update or hg merge, the procedure to fix the + problem is simple. + + Just find a nearby Linux or Unix box, clone the problem + repository onto it, and use Mercurial's hg rename command to change the + names of any offending files or directories so that they will + no longer cause case folding conflicts. Commit this change, + hg pull or hg push it across to your Windows or + MacOS system, and hg update + to the revision with the non-conflicting names. + + The changeset with case-conflicting names will remain in + your project's history, and you still won't be able to + hg update your working + directory to that changeset on a Windows or MacOS system, but + you can continue development unimpeded. + + + + + + + + + + + + Managing releases and branchy development + + Mercurial provides several mechanisms for you to manage a + project that is making progress on multiple fronts at once. To + understand these mechanisms, let's first take a brief look at a + fairly normal software project structure. + + Many software projects issue periodic major + releases that contain substantial new features. In parallel, they + may issue minor releases. These are usually + identical to the major releases off which they're based, but with + a few bugs fixed. + + In this chapter, we'll start by talking about how to keep + records of project milestones such as releases. We'll then + continue on to talk about the flow of work between different + phases of a project, and how Mercurial can help you to isolate and + manage this work. + + + Giving a persistent name to a revision + + Once you decide that you'd like to call a particular + revision a release, it's a good idea to record + the identity of that revision. This will let you reproduce that + release at a later date, for whatever purpose you might need at + the time (reproducing a bug, porting to a new platform, etc). + +$ hg init mytag +$ cd mytag +$ echo hello > myfile +$ hg commit -A -m 'Initial commit' +adding myfile + + + + + Mercurial lets you give a permanent name to any revision + using the hg tag command. Not + surprisingly, these names are called tags. + + +$ hg tag v1.0 + + + + + A tag is nothing more than a symbolic name + for a revision. Tags exist purely for your convenience, so that + you have a handy permanent way to refer to a revision; Mercurial + doesn't interpret the tag names you use in any way. Neither + does Mercurial place any restrictions on the name of a tag, + beyond a few that are necessary to ensure that a tag can be + parsed unambiguously. A tag name cannot contain any of the + following characters: + + Colon (ASCII 58, + :) + + Carriage return (ASCII 13, + \r) + + Newline (ASCII 10, + \n) + + + You can use the hg tags + command to display the tags present in your repository. In the + output, each tagged revision is identified first by its name, + then by revision number, and finally by the unique hash of the + revision. + + +$ hg tags +tip 1:f283c2669b38 +v1.0 0:0c957785065f + + + + + Notice that tip is listed in the output + of hg tags. The + tip tag is a special floating + tag, which always identifies the newest revision in the + repository. + + In the output of the hg + tags command, tags are listed in reverse order, by + revision number. This usually means that recent tags are listed + before older tags. It also means that tip is + always going to be the first tag listed in the output of + hg tags. + + When you run hg log, if it + displays a revision that has tags associated with it, it will + print those tags. + + +$ hg log +changeset: 1:f283c2669b38 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:16 2009 +0000 +summary: Added tag v1.0 for changeset 0c957785065f + +changeset: 0:0c957785065f +tag: v1.0 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:15 2009 +0000 +summary: Initial commit + + + + + + Any time you need to provide a revision ID to a Mercurial + command, the command will accept a tag name in its place. + Internally, Mercurial will translate your tag name into the + corresponding revision ID, then use that. + + +$ echo goodbye > myfile2 +$ hg commit -A -m 'Second commit' +adding myfile2 +$ hg log -r v1.0 +changeset: 0:0c957785065f +tag: v1.0 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:15 2009 +0000 +summary: Initial commit + + + + + + There's no limit on the number of tags you can have in a + repository, or on the number of tags that a single revision can + have. As a practical matter, it's not a great idea to have + too many (a number which will vary from project + to project), simply because tags are supposed to help you to + find revisions. If you have lots of tags, the ease of using + them to identify revisions diminishes rapidly. + + For example, if your project has milestones as frequent as + every few days, it's perfectly reasonable to tag each one of + those. But if you have a continuous build system that makes + sure every revision can be built cleanly, you'd be introducing a + lot of noise if you were to tag every clean build. Instead, you + could tag failed builds (on the assumption that they're rare!), + or simply not use tags to track buildability. + + If you want to remove a tag that you no longer want, use + hg tag --remove. + + +$ hg tag --remove v1.0 +$ hg tags +tip 3:0f446f1d1f7f + + + + + You can also modify a tag at any time, so that it identifies + a different revision, by simply issuing a new hg tag command. You'll have to use the + option to tell Mercurial + that you really want to update the + tag. + + +$ hg tag -r 1 v1.1 +$ hg tags +tip 4:12fc7bf92915 +v1.1 1:f283c2669b38 +$ hg tag -r 2 v1.1 +abort: tag 'v1.1' already exists (use -f to force) +$ hg tag -f -r 2 v1.1 +$ hg tags +tip 5:17e25cf010af +v1.1 2:737882b3cc76 + + + + + There will still be a permanent record of the previous + identity of the tag, but Mercurial will no longer use it. + There's thus no penalty to tagging the wrong revision; all you + have to do is turn around and tag the correct revision once you + discover your error. + + Mercurial stores tags in a normal revision-controlled file + in your repository. If you've created any tags, you'll find + them in a file in the root of your repository named .hgtags. When you run the hg tag command, Mercurial modifies + this file, then automatically commits the change to it. This + means that every time you run hg + tag, you'll see a corresponding changeset in the + output of hg log. + + +$ hg tip +changeset: 5:17e25cf010af +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:16 2009 +0000 +summary: Added tag v1.1 for changeset 737882b3cc76 + + + + + + + Handling tag conflicts during a merge + + You won't often need to care about the .hgtags file, but it sometimes + makes its presence known during a merge. The format of the + file is simple: it consists of a series of lines. Each line + starts with a changeset hash, followed by a space, followed by + the name of a tag. + + If you're resolving a conflict in the .hgtags file during a merge, + there's one twist to modifying the .hgtags file: when Mercurial is + parsing the tags in a repository, it + never reads the working copy of the + .hgtags file. Instead, it + reads the most recently committed + revision of the file. + + An unfortunate consequence of this design is that you + can't actually verify that your merged .hgtags file is correct until + after you've committed a change. So if + you find yourself resolving a conflict on .hgtags during a merge, be sure to + run hg tags after you commit. + If it finds an error in the .hgtags file, it will report the + location of the error, which you can then fix and commit. You + should then run hg tags + again, just to be sure that your fix is correct. + + + + Tags and cloning + + You may have noticed that the hg + clone command has a option that lets you clone + an exact copy of the repository as of a particular changeset. + The new clone will not contain any project history that comes + after the revision you specified. This has an interaction + with tags that can surprise the unwary. + + Recall that a tag is stored as a revision to + the .hgtags file. When you + create a tag, the changeset in which its recorded refers to an + older changeset. When you run hg clone + -r foo to clone a repository as of tag + foo, the new clone will not + contain any revision newer than the one the tag refers to, + including the revision where the tag was created. + The result is that you'll get exactly the right subset of the + project's history in the new repository, but + not the tag you might have + expected. + + + + When permanent tags are too much + + Since Mercurial's tags are revision controlled and carried + around with a project's history, everyone you work with will + see the tags you create. But giving names to revisions has + uses beyond simply noting that revision + 4237e45506ee is really + v2.0.2. If you're trying to track down a + subtle bug, you might want a tag to remind you of something + like Anne saw the symptoms with this + revision. + + For cases like this, what you might want to use are + local tags. You can create a local tag + with the option to the + hg tag command. This will + store the tag in a file called .hg/localtags. Unlike .hgtags, .hg/localtags is not revision + controlled. Any tags you create using remain strictly local to the + repository you're currently working in. + + + + + The flow of changes—big picture vs. little + + To return to the outline I sketched at the + beginning of the chapter, let's think about a project that has + multiple concurrent pieces of work under development at + once. + + There might be a push for a new main release; + a new minor bugfix release to the last main release; and an + unexpected hot fix to an old release that is now + in maintenance mode. + + The usual way people refer to these different concurrent + directions of development is as branches. + However, we've already seen numerous times that Mercurial treats + all of history as a series of branches and + merges. Really, what we have here is two ideas that are + peripherally related, but which happen to share a name. + + Big picture branches represent + the sweep of a project's evolution; people give them names, + and talk about them in conversation. + + Little picture branches are + artefacts of the day-to-day activity of developing and + merging changes. They expose the narrative of how the code + was developed. + + + + + Managing big-picture branches in repositories + + The easiest way to isolate a big picture + branch in Mercurial is in a dedicated repository. If you have + an existing shared repository—let's call it + myproject—that reaches a + 1.0 milestone, you can start to prepare for + future maintenance releases on top of version 1.0 by tagging the + revision from which you prepared the 1.0 release. + + +$ cd myproject +$ hg tag v1.0 + + + + + You can then clone a new shared + myproject-1.0.1 repository as of that + tag. + + +$ cd .. +$ hg clone myproject myproject-1.0.1 +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Afterwards, if someone needs to work on a bug fix that ought + to go into an upcoming 1.0.1 minor release, they clone the + myproject-1.0.1 repository, make their + changes, and push them back. + + +$ hg clone myproject-1.0.1 my-1.0.1-bugfix +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd my-1.0.1-bugfix +$ echo 'I fixed a bug using only echo!' >> myfile +$ hg commit -m 'Important fix for 1.0.1' +$ hg push +pushing to /tmp/branch-repo3rVLLS/myproject-1.0.1 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files + + + + + Meanwhile, development for + the next major release can continue, isolated and unabated, in + the myproject repository. + + +$ cd .. +$ hg clone myproject my-feature +updating working directory +2 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd my-feature +$ echo 'This sure is an exciting new feature!' > mynewfile +$ hg commit -A -m 'New feature' +adding mynewfile +$ hg push +pushing to /tmp/branch-repo3rVLLS/myproject +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 1 changes to 1 files + + + + + + + Don't repeat yourself: merging across branches + + In many cases, if you have a bug to fix on a maintenance + branch, the chances are good that the bug exists on your + project's main branch (and possibly other maintenance branches, + too). It's a rare developer who wants to fix the same bug + multiple times, so let's look at a few ways that Mercurial can + help you to manage these bugfixes without duplicating your + work. + + In the simplest instance, all you need to do is pull changes + from your maintenance branch into your local clone of the target + branch. + + +$ cd .. +$ hg clone myproject myproject-merge +updating working directory +3 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd myproject-merge +$ hg pull ../myproject-1.0.1 +pulling from ../myproject-1.0.1 +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) + + + + + You'll then need to merge the heads of the two branches, and + push back to the main branch. + + +$ hg merge +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ hg commit -m 'Merge bugfix from 1.0.1 branch' +$ hg push +pushing to /tmp/branch-repo3rVLLS/myproject +searching for changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 1 changes to 1 files + + + + + + + Naming branches within one repository + + In most instances, isolating branches in repositories is the + right approach. Its simplicity makes it easy to understand; and + so it's hard to make mistakes. There's a one-to-one + relationship between branches you're working in and directories + on your system. This lets you use normal (non-Mercurial-aware) + tools to work on files within a branch/repository. + + If you're more in the power user category + (and your collaborators are too), there is + an alternative way of handling branches that you can consider. + I've already mentioned the human-level distinction between + small picture and big picture + branches. While Mercurial works with multiple small + picture branches in a repository all the time (for + example after you pull changes in, but before you merge them), + it can also work with multiple big + picture branches. + + The key to working this way is that Mercurial lets you + assign a persistent name to a branch. + There always exists a branch named default. + Even before you start naming branches yourself, you can find + traces of the default branch if you look for + them. + + As an example, when you run the hg + commit command, and it pops up your editor so that + you can enter a commit message, look for a line that contains + the text HG: branch default at + the bottom. This is telling you that your commit will occur on + the branch named default. + + To start working with named branches, use the hg branches command. This command + lists the named branches already present in your repository, + telling you which changeset is the tip of each. + + +$ hg tip +changeset: 0:90897f9e54e3 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Initial commit + +$ hg branches +default 0:90897f9e54e3 + + + + + Since you haven't created any named branches yet, the only + one that exists is default. + + To find out what the current branch is, run + the hg branch command, giving + it no arguments. This tells you what branch the parent of the + current changeset is on. + + +$ hg branch +default + + + + + To create a new branch, run the hg + branch command again. This time, give it one + argument: the name of the branch you want to create. + + +$ hg branch foo +marked working directory as branch foo +$ hg branch +foo + + + + + After you've created a branch, you might wonder what effect + the hg branch command has had. + What do the hg status and + hg tip commands report? + + +$ hg status +$ hg tip +changeset: 0:90897f9e54e3 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Initial commit + + + + + + Nothing has changed in the + working directory, and there's been no new history created. As + this suggests, running the hg + branch command has no permanent effect; it only + tells Mercurial what branch name to use the + next time you commit a changeset. + + When you commit a change, Mercurial records the name of the + branch on which you committed. Once you've switched from the + default branch to another and committed, + you'll see the name of the new branch show up in the output of + hg log, hg tip, and other commands that + display the same kind of output. + + +$ echo 'hello again' >> myfile +$ hg commit -m 'Second commit' +$ hg tip +changeset: 1:5656f8ffdd49 +branch: foo +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Second commit + + + + + + The hg log-like commands + will print the branch name of every changeset that's not on the + default branch. As a result, if you never + use named branches, you'll never see this information. + + Once you've named a branch and committed a change with that + name, every subsequent commit that descends from that change + will inherit the same branch name. You can change the name of a + branch at any time, using the hg + branch command. + + +$ hg branch +foo +$ hg branch bar +marked working directory as branch bar +$ echo new file > newfile +$ hg commit -A -m 'Third commit' +adding newfile +$ hg tip +changeset: 2:c59d680fc2ec +branch: bar +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Third commit + + + + + + In practice, this is something you won't do very often, as + branch names tend to have fairly long lifetimes. (This isn't a + rule, just an observation.) + + + + Dealing with multiple named branches in a + repository + + If you have more than one named branch in a repository, + Mercurial will remember the branch that your working directory + is on when you start a command like hg + update or hg pull + -u. It will update the working directory to the tip + of this branch, no matter what the repo-wide tip + is. To update to a revision that's on a different named branch, + you may need to use the + option to hg update. + + This behavior is a little subtle, so let's see it in + action. First, let's remind ourselves what branch we're + currently on, and what branches are in our repository. + + +$ hg parents +changeset: 2:c59d680fc2ec +branch: bar +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Third commit + +$ hg branches +bar 2:c59d680fc2ec +foo 1:5656f8ffdd49 (inactive) +default 0:90897f9e54e3 (inactive) + + + + + We're on the bar branch, but there also + exists an older hg foo + branch. + + We can hg update back and + forth between the tips of the foo and + bar branches without needing to use the + option, because this + only involves going backwards and forwards linearly through our + change history. + + +$ hg update foo +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ hg parents +changeset: 1:5656f8ffdd49 +branch: foo +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Second commit + +$ hg update bar +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ hg parents +changeset: 2:c59d680fc2ec +branch: bar +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Third commit + + + + + + If we go back to the foo branch and then + run hg update, it will keep us + on foo, not move us to the tip of + bar. + + +$ hg update foo +0 files updated, 0 files merged, 1 files removed, 0 files unresolved +$ hg update +0 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Committing a new change on the foo branch + introduces a new head. + + +$ echo something > somefile +$ hg commit -A -m 'New file' +adding somefile +created new head +$ hg heads +changeset: 3:4dd2f7a37288 +branch: foo +tag: tip +parent: 1:5656f8ffdd49 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:43 2009 +0000 +summary: New file + +changeset: 2:c59d680fc2ec +branch: bar +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:42 2009 +0000 +summary: Third commit + + + + + + + + Branch names and merging + + As you've probably noticed, merges in Mercurial are not + symmetrical. Let's say our repository has two heads, 17 and 23. + If I hg update to 17 and then + hg merge with 23, Mercurial + records 17 as the first parent of the merge, and 23 as the + second. Whereas if I hg update + to 23 and then hg merge with + 17, it records 23 as the first parent, and 17 as the + second. + + This affects Mercurial's choice of branch name when you + merge. After a merge, Mercurial will retain the branch name of + the first parent when you commit the result of the merge. If + your first parent's branch name is foo, and + you merge with bar, the branch name will + still be foo after you merge. + + It's not unusual for a repository to contain multiple heads, + each with the same branch name. Let's say I'm working on the + foo branch, and so are you. We commit + different changes; I pull your changes; I now have two heads, + each claiming to be on the foo branch. The + result of a merge will be a single head on the + foo branch, as you might hope. + + But if I'm working on the bar branch, and + I merge work from the foo branch, the result + will remain on the bar branch. + + +$ hg branch +bar +$ hg merge foo +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) +$ hg commit -m 'Merge' +$ hg tip +changeset: 4:9f05d4ef3efe +branch: bar +tag: tip +parent: 2:c59d680fc2ec +parent: 3:4dd2f7a37288 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:44 2009 +0000 +summary: Merge + + + + + + To give a more concrete example, if I'm working on the + bleeding-edge branch, and I want to bring in + the latest fixes from the stable branch, + Mercurial will choose the right + (bleeding-edge) branch name when I pull and + merge from stable. + + + + Branch naming is generally useful + + You shouldn't think of named branches as applicable only to + situations where you have multiple long-lived branches + cohabiting in a single repository. They're very useful even in + the one-branch-per-repository case. + + In the simplest case, giving a name to each branch gives you + a permanent record of which branch a changeset originated on. + This gives you more context when you're trying to follow the + history of a long-lived branchy project. + + If you're working with shared repositories, you can set up a + pretxnchangegroup hook on each + that will block incoming changes that have the + wrong branch name. This provides a simple, but + effective, defence against people accidentally pushing changes + from a bleeding edge branch to a + stable branch. Such a hook might look like this + inside the shared repo's + /.hgrc. + [hooks] +pretxnchangegroup.branch = hg heads --template '{branches} ' | grep mybranch + + + + + + + + + + + Finding and fixing mistakes + + To err might be human, but to really handle the consequences + well takes a top-notch revision control system. In this chapter, + we'll discuss some of the techniques you can use when you find + that a problem has crept into your project. Mercurial has some + highly capable features that will help you to isolate the sources + of problems, and to handle them appropriately. + + + Erasing local history + + + The accidental commit + + I have the occasional but persistent problem of typing + rather more quickly than I can think, which sometimes results + in me committing a changeset that is either incomplete or + plain wrong. In my case, the usual kind of incomplete + changeset is one in which I've created a new source file, but + forgotten to hg add it. A + plain wrong changeset is not as common, but no + less annoying. + + + + Rolling back a transaction + + In , I + mentioned that Mercurial treats each modification of a + repository as a transaction. Every time + you commit a changeset or pull changes from another + repository, Mercurial remembers what you did. You can undo, + or roll back, exactly one of these + actions using the hg rollback + command. (See + for an important caveat about the use of this command.) + + Here's a mistake that I often find myself making: + committing a change in which I've created a new file, but + forgotten to hg add + it. + + +$ hg status +M a +$ echo b > b +$ hg commit -m 'Add file b' + + + + + Looking at the output of hg + status after the commit immediately confirms the + error. + + +$ hg status +? b +$ hg tip +changeset: 1:246e2aada1c5 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:14 2009 +0000 +summary: Add file b + + + + + + The commit captured the changes to the file + a, but not the new file + b. If I were to push this changeset to a + repository that I shared with a colleague, the chances are + high that something in a would refer to + b, which would not be present in their + repository when they pulled my changes. I would thus become + the object of some indignation. + + However, luck is with me—I've caught my error + before I pushed the changeset. I use the hg rollback command, and Mercurial + makes that last changeset vanish. + + +$ hg rollback +rolling back last transaction +$ hg tip +changeset: 0:c37ce4157509 +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:14 2009 +0000 +summary: First commit + +$ hg status +M a +? b + + + + + Notice that the changeset is no longer present in the + repository's history, and the working directory once again + thinks that the file a is modified. The + commit and rollback have left the working directory exactly as + it was prior to the commit; the changeset has been completely + erased. I can now safely hg + add the file b, and rerun my + commit. + + +$ hg add b +$ hg commit -m 'Add file b, this time for real' + + + + + + + The erroneous pull + + It's common practice with Mercurial to maintain separate + development branches of a project in different repositories. + Your development team might have one shared repository for + your project's 0.9 release, and another, + containing different changes, for the 1.0 + release. + + Given this, you can imagine that the consequences could be + messy if you had a local 0.9 repository, and + accidentally pulled changes from the shared 1.0 + repository into it. At worst, you could be paying + insufficient attention, and push those changes into the shared + 0.9 tree, confusing your entire team (but don't + worry, we'll return to this horror scenario later). However, + it's more likely that you'll notice immediately, because + Mercurial will display the URL it's pulling from, or you will + see it pull a suspiciously large number of changes into the + repository. + + The hg rollback command + will work nicely to expunge all of the changesets that you + just pulled. Mercurial groups all changes from one hg pull into a single transaction, + so one hg rollback is all you + need to undo this mistake. + + + + Rolling back is useless once you've pushed + + The value of the hg + rollback command drops to zero once you've pushed + your changes to another repository. Rolling back a change + makes it disappear entirely, but only in + the repository in which you perform the hg rollback. Because a rollback + eliminates history, there's no way for the disappearance of a + change to propagate between repositories. + + If you've pushed a change to another + repository—particularly if it's a shared + repository—it has essentially escaped into the + wild, and you'll have to recover from your mistake + in a different way. If you push a changeset somewhere, then + roll it back, then pull from the repository you pushed to, the + changeset you thought you'd gotten rid of will simply reappear + in your repository. + + (If you absolutely know for sure that the change + you want to roll back is the most recent change in the + repository that you pushed to, and you + know that nobody else could have pulled it from that + repository, you can roll back the changeset there, too, but + you really should not expect this to work reliably. Sooner or + later a change really will make it into a repository that you + don't directly control (or have forgotten about), and come + back to bite you.) + + + + You can only roll back once + + Mercurial stores exactly one transaction in its + transaction log; that transaction is the most recent one that + occurred in the repository. This means that you can only roll + back one transaction. If you expect to be able to roll back + one transaction, then its predecessor, this is not the + behavior you will get. + + +$ hg rollback +rolling back last transaction +$ hg rollback +no rollback information available + + + + + Once you've rolled back one transaction in a repository, + you can't roll back again in that repository until you perform + another commit or pull. + + + + + Reverting the mistaken change + + If you make a modification to a file, and decide that you + really didn't want to change the file at all, and you haven't + yet committed your changes, the hg + revert command is the one you'll need. It looks at + the changeset that's the parent of the working directory, and + restores the contents of the file to their state as of that + changeset. (That's a long-winded way of saying that, in the + normal case, it undoes your modifications.) + + Let's illustrate how the hg + revert command works with yet another small example. + We'll begin by modifying a file that Mercurial is already + tracking. + + +$ cat file +original content +$ echo unwanted change >> file +$ hg diff file +diff -r 2eacf948d309 file +--- a/file Sun Aug 16 14:05:00 2009 +0000 ++++ b/file Sun Aug 16 14:05:00 2009 +0000 +@@ -1,1 +1,2 @@ + original content ++unwanted change + + + + + If we don't + want that change, we can simply hg + revert the file. + + +$ hg status +M file +$ hg revert file +$ cat file +original content + + + + + The hg revert command + provides us with an extra degree of safety by saving our + modified file with a .orig + extension. + + +$ hg status +? file.orig +$ cat file.orig +original content +unwanted change + + + + + + Be careful with <filename moreinfo="none">.orig</filename> files + + It's extremely unlikely that you are either using + Mercurial to manage files with .orig + extensions or that you even care about the contents of such + files. Just in case, though, it's useful to remember that + hg revert will + unconditionally overwrite an existing file with a + .orig extension. For instance, if you + already have a file named foo.orig when + you revert foo, the contents of + foo.orig will be clobbered. + + + Here is a summary of the cases that the hg revert command can deal with. We + will describe each of these in more detail in the section that + follows. + + If you modify a file, it will restore the file + to its unmodified state. + + If you hg add a + file, it will undo the added state of the + file, but leave the file itself untouched. + + If you delete a file without telling Mercurial, + it will restore the file to its unmodified contents. + + If you use the hg + remove command to remove a file, it will undo + the removed state of the file, and restore + the file to its unmodified contents. + + + + File management errors + + The hg revert command is + useful for more than just modified files. It lets you reverse + the results of all of Mercurial's file management + commands—hg add, + hg remove, and so on. + + If you hg add a file, + then decide that in fact you don't want Mercurial to track it, + use hg revert to undo the + add. Don't worry; Mercurial will not modify the file in any + way. It will just unmark the file. + + +$ echo oops > oops +$ hg add oops +$ hg status oops +A oops +$ hg revert oops +$ hg status +? oops + + + + + Similarly, if you ask Mercurial to hg remove a file, you can use + hg revert to restore it to + the contents it had as of the parent of the working directory. + +$ hg remove file +$ hg status +R file +$ hg revert file +$ hg status +$ ls file +file + + + This works just as + well for a file that you deleted by hand, without telling + Mercurial (recall that in Mercurial terminology, this kind of + file is called missing). + + +$ rm file +$ hg status +! file +$ hg revert file +$ ls file +file + + + + + If you revert a hg copy, + the copied-to file remains in your working directory + afterwards, untracked. Since a copy doesn't affect the + copied-from file in any way, Mercurial doesn't do anything + with the copied-from file. + + +$ hg copy file new-file +$ hg revert new-file +$ hg status +? new-file + + + + + + + + Dealing with committed changes + + Consider a case where you have committed a change + a, and another change + b on top of it; you then realise that + change a was incorrect. Mercurial lets you + back out an entire changeset automatically, and + building blocks that let you reverse part of a changeset by + hand. + + Before you read this section, here's something to + keep in mind: the hg backout + command undoes the effect of a change by + adding to your repository's history, not by + modifying or erasing it. It's the right tool to use if you're + fixing bugs, but not if you're trying to undo some change that + has catastrophic consequences. To deal with those, see + . + + + Backing out a changeset + + The hg backout command + lets you undo the effects of an entire + changeset in an automated fashion. Because Mercurial's + history is immutable, this command does + not get rid of the changeset you want to undo. + Instead, it creates a new changeset that + reverses the effect of the to-be-undone + changeset. + + The operation of the hg + backout command is a little intricate, so let's + illustrate it with some examples. First, we'll create a + repository with some simple changes. + + +$ hg init myrepo +$ cd myrepo +$ echo first change >> myfile +$ hg add myfile +$ hg commit -m 'first change' +$ echo second change >> myfile +$ hg commit -m 'second change' + + + + + The hg backout command + takes a single changeset ID as its argument; this is the + changeset to back out. Normally, hg + backout will drop you into a text editor to write + a commit message, so you can record why you're backing the + change out. In this example, we provide a commit message on + the command line using the option. + + + + Backing out the tip changeset + + We're going to start by backing out the last changeset we + committed. + + +$ hg backout -m 'back out second change' tip +reverting myfile +changeset 2:611a0cae251c backs out changeset 1:43700a9b3ec8 +$ cat myfile +first change + + + + + You can see that the second line from + myfile is no longer present. Taking a + look at the output of hg log + gives us an idea of what the hg + backout command has done. + +$ hg log --style compact +2[tip] 611a0cae251c 2009-08-16 14:04 +0000 bos + back out second change + +1 43700a9b3ec8 2009-08-16 14:04 +0000 bos + second change + +0 f2ef23d503fd 2009-08-16 14:04 +0000 bos + first change + + + + Notice that the new changeset + that hg backout has created + is a child of the changeset we backed out. It's easier to see + this in , which presents a + graphical view of the change history. As you can see, the + history is nice and linear. + +
+ Backing out a change using the <command role="hg-cmd" moreinfo="none">hg backout</command> command + + + XXX add text + +
+ +
+ + Backing out a non-tip change + + If you want to back out a change other than the last one + you committed, pass the option to the + hg backout command. + + +$ cd .. +$ hg clone -r1 myrepo non-tip-repo +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd non-tip-repo + + + + + This makes backing out any changeset a + one-shot operation that's usually simple and + fast. + + +$ echo third change >> myfile +$ hg commit -m 'third change' +$ hg backout --merge -m 'back out second change' 1 +reverting myfile +created new head +changeset 3:611a0cae251c backs out changeset 1:43700a9b3ec8 +merging with changeset 3:611a0cae251c +merging myfile +0 files updated, 1 files merged, 0 files removed, 0 files unresolved +(branch merge, don't forget to commit) + + + + + If you take a look at the contents of + myfile after the backout finishes, you'll + see that the first and third changes are present, but not the + second. + + +$ cat myfile +first change +third change + + + + + As the graphical history in illustrates, Mercurial + still commits one change in this kind of situation (the + box-shaped node is the ones that Mercurial commits + automatically), but the revision graph now looks different. + Before Mercurial begins the backout process, it first + remembers what the current parent of the working directory is. + It then backs out the target changeset, and commits that as a + changeset. Finally, it merges back to the previous parent of + the working directory, but notice that it does not + commit the result of the merge. The repository + now contains two heads, and the working directory is in a + merge state. + +
+ Automated backout of a non-tip change using the + <command role="hg-cmd" moreinfo="none">hg backout</command> command + + + XXX add text + +
+ + The result is that you end up back where you + were, only with some extra history that undoes the + effect of the changeset you wanted to back out. + + You might wonder why Mercurial does not commit the result + of the merge that it performed. The reason lies in Mercurial + behaving conservatively: a merge naturally has more scope for + error than simply undoing the effect of the tip changeset, + so your work will be safest if you first inspect (and test!) + the result of the merge, then commit + it. + + + Always use the <option role="hg-opt-backout">--merge</option> option + + In fact, since the option will do the + right thing whether or not the changeset + you're backing out is the tip (i.e. it won't try to merge if + it's backing out the tip, since there's no need), you should + always use this option when you run the + hg backout command. + + +
+ + Gaining more control of the backout process + + While I've recommended that you always use the option when backing + out a change, the hg backout + command lets you decide how to merge a backout changeset. + Taking control of the backout process by hand is something you + will rarely need to do, but it can be useful to understand + what the hg backout command + is doing for you automatically. To illustrate this, let's + clone our first repository, but omit the backout change that + it contains. + + +$ cd .. +$ hg clone -r1 myrepo newrepo +requesting all changes +adding changesets +adding manifests +adding file changes +added 2 changesets with 2 changes to 1 files +updating working directory +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd newrepo + + + + + As with our + earlier example, We'll commit a third changeset, then back out + its parent, and see what happens. + + +$ echo third change >> myfile +$ hg commit -m 'third change' +$ hg backout -m 'back out second change' 1 +reverting myfile +created new head +changeset 3:bf906ee0baae backs out changeset 1:43700a9b3ec8 +the backout changeset is a new head - do not forget to merge +(use "backout --merge" if you want to auto-merge) + + + + + Our new changeset is again a descendant of the changeset + we backout out; it's thus a new head, not + a descendant of the changeset that was the tip. The hg backout command was quite + explicit in telling us this. + + +$ hg log --style compact +3[tip]:1 bf906ee0baae 2009-08-16 14:04 +0000 bos + back out second change + +2 2521379001ad 2009-08-16 14:04 +0000 bos + third change + +1 43700a9b3ec8 2009-08-16 14:04 +0000 bos + second change + +0 f2ef23d503fd 2009-08-16 14:04 +0000 bos + first change + + + + + + Again, it's easier to see what has happened by looking at + a graph of the revision history, in . This makes it clear + that when we use hg backout + to back out a change other than the tip, Mercurial adds a new + head to the repository (the change it committed is + box-shaped). + +
+ Backing out a change using the <command role="hg-cmd" moreinfo="none">hg backout</command> command + + + XXX add text + +
+ + After the hg backout + command has completed, it leaves the new + backout changeset as the parent of the working + directory. + + +$ hg parents +changeset: 2:2521379001ad +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:37 2009 +0000 +summary: third change + + + + + + Now we have two isolated sets of changes. + + +$ hg heads +changeset: 3:bf906ee0baae +tag: tip +parent: 1:43700a9b3ec8 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:37 2009 +0000 +summary: back out second change + +changeset: 2:2521379001ad +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:37 2009 +0000 +summary: third change + + + + + + Let's think about what we expect to see as the contents of + myfile now. The first change should be + present, because we've never backed it out. The second change + should be missing, as that's the change we backed out. Since + the history graph shows the third change as a separate head, + we don't expect to see the third change + present in myfile. + + +$ cat myfile +first change + + + + + To get the third change back into the file, we just do a + normal merge of our two heads. + + +$ hg merge +abort: outstanding uncommitted changes +$ hg commit -m 'merged backout with previous tip' +$ cat myfile +first change + + + + + Afterwards, the graphical history of our + repository looks like + . + +
+ Manually merging a backout change + + + XXX add text + +
+ +
+ + Why <command role="hg-cmd" moreinfo="none">hg backout</command> works as + it does + + Here's a brief description of how the hg backout command works. + + It ensures that the working directory is + clean, i.e. that the output of hg status would be empty. + + It remembers the current parent of the working + directory. Let's call this changeset + orig. + + It does the equivalent of a hg update to sync the working + directory to the changeset you want to back out. Let's + call this changeset backout. + + It finds the parent of that changeset. Let's + call that changeset parent. + + For each file that the + backout changeset affected, it does the + equivalent of a hg revert -r + parent on that file, to restore it to the + contents it had before that changeset was + committed. + + It commits the result as a new changeset. + This changeset has backout as its + parent. + + If you specify on the command + line, it merges with orig, and commits + the result of the merge. + + + An alternative way to implement the hg backout command would be to + hg export the + to-be-backed-out changeset as a diff, then use the option to the + patch command to reverse the effect of the + change without fiddling with the working directory. This + sounds much simpler, but it would not work nearly as + well. + + The reason that hg + backout does an update, a commit, a merge, and + another commit is to give the merge machinery the best chance + to do a good job when dealing with all the changes + between the change you're backing out and + the current tip. + + If you're backing out a changeset that's 100 revisions + back in your project's history, the chances that the + patch command will be able to apply a + reverse diff cleanly are not good, because intervening changes + are likely to have broken the context that + patch uses to determine whether it can + apply a patch (if this sounds like gibberish, see for a + discussion of the patch command). Also, + Mercurial's merge machinery will handle files and directories + being renamed, permission changes, and modifications to binary + files, none of which patch can deal + with. + + +
+ + Changes that should never have been + + Most of the time, the hg + backout command is exactly what you need if you want + to undo the effects of a change. It leaves a permanent record + of exactly what you did, both when committing the original + changeset and when you cleaned up after it. + + On rare occasions, though, you may find that you've + committed a change that really should not be present in the + repository at all. For example, it would be very unusual, and + usually considered a mistake, to commit a software project's + object files as well as its source files. Object files have + almost no intrinsic value, and they're big, + so they increase the size of the repository and the amount of + time it takes to clone or pull changes. + + Before I discuss the options that you have if you commit a + brown paper bag change (the kind that's so bad + that you want to pull a brown paper bag over your head), let me + first discuss some approaches that probably won't work. + + Since Mercurial treats history as + accumulative—every change builds on top of all changes + that preceded it—you generally can't just make disastrous + changes disappear. The one exception is when you've just + committed a change, and it hasn't been pushed or pulled into + another repository. That's when you can safely use the hg rollback command, as I detailed in + . + + After you've pushed a bad change to another repository, you + could still use hg + rollback to make your local copy of the change + disappear, but it won't have the consequences you want. The + change will still be present in the remote repository, so it + will reappear in your local repository the next time you + pull. + + If a situation like this arises, and you know which + repositories your bad change has propagated into, you can + try to get rid of the change from + every one of those repositories. This is, + of course, not a satisfactory solution: if you miss even a + single repository while you're expunging, the change is still + in the wild, and could propagate further. + + If you've committed one or more changes + after the change that you'd like to see + disappear, your options are further reduced. Mercurial doesn't + provide a way to punch a hole in history, leaving + changesets intact. + + + Backing out a merge + + Since merges are often complicated, it is not unheard of + for a merge to be mangled badly, but committed erroneously. + Mercurial provides an important safeguard against bad merges + by refusing to commit unresolved files, but human ingenuity + guarantees that it is still possible to mess a merge up and + commit it. + + Given a bad merge that has been committed, usually the + best way to approach it is to simply try to repair the damage + by hand. A complete disaster that cannot be easily fixed up + by hand ought to be very rare, but the hg backout command may help in + making the cleanup easier. It offers a option, which lets + you specify which parent to revert to when backing out a + merge. + +
+ A bad merge + + + XXX add text + +
+ + Suppose we have a revision graph like that in . What we'd like is to + redo the merge of revisions 2 and + 3. + + One way to do so would be as follows. + + + + Call hg backout --rev=4 + --parent=2. This tells hg backout to back out revision + 4, which is the bad merge, and to when deciding which + revision to prefer, to choose parent 2, one of the parents + of the merge. The effect can be seen in . +
+ Backing out the merge, favoring one parent + + + XXX add text + +
+
+ + + Call hg backout --rev=4 + --parent=3. This tells hg backout to back out revision + 4 again, but this time to choose parent 3, the other + parent of the merge. The result is visible in , in which the repository + now contains three heads. +
+ Backing out the merge, favoring the other + parent + + + XXX add text + +
+
+ + + Redo the bad merge by merging the two backout heads, + which reduces the number of heads in the repository to + two, as can be seen in . +
+ Merging the backouts + + + XXX add text + +
+
+ + + Merge with the commit that was made after the bad + merge, as shown in . +
+ Merging the backouts + + + XXX add text + +
+
+
+
+ + + Protect yourself from <quote>escaped</quote> + changes + + If you've committed some changes to your local repository + and they've been pushed or pulled somewhere else, this isn't + necessarily a disaster. You can protect yourself ahead of + time against some classes of bad changeset. This is + particularly easy if your team usually pulls changes from a + central repository. + + By configuring some hooks on that repository to validate + incoming changesets (see chapter ), + you can + automatically prevent some kinds of bad changeset from being + pushed to the central repository at all. With such a + configuration in place, some kinds of bad changeset will + naturally tend to die out because they can't + propagate into the central repository. Better yet, this + happens without any need for explicit intervention. + + For instance, an incoming change hook that + verifies that a changeset will actually compile can prevent + people from inadvertently breaking the + build. + + + + What to do about sensitive changes that escape + + Even a carefully run project can suffer an unfortunate + event such as the committing and uncontrolled propagation of a + file that contains important passwords. + + If something like this happens to you, and the information + that gets accidentally propagated is truly sensitive, your + first step should be to mitigate the effect of the leak + without trying to control the leak itself. If you are not 100% + certain that you know exactly who could have seen the changes, + you should immediately change passwords, cancel credit cards, + or find some other way to make sure that the information that + has leaked is no longer useful. In other words, assume that + the change has propagated far and wide, and that there's + nothing more you can do. + + You might hope that there would be mechanisms you could + use to either figure out who has seen a change or to erase the + change permanently everywhere, but there are good reasons why + these are not possible. + + Mercurial does not provide an audit trail of who has + pulled changes from a repository, because it is usually either + impossible to record such information or trivial to spoof it. + In a multi-user or networked environment, you should thus be + extremely skeptical of yourself if you think that you have + identified every place that a sensitive changeset has + propagated to. Don't forget that people can and will send + bundles by email, have their backup software save data + offsite, carry repositories on USB sticks, and find other + completely innocent ways to confound your attempts to track + down every copy of a problematic change. + + Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is + no way to enforce its disappearance; someone could easily + modify their copy of Mercurial to ignore such directives. In + addition, even if Mercurial provided such a capability, + someone who simply hadn't pulled a make this file + disappear changeset wouldn't be affected by it, nor + would web crawlers visiting at the wrong time, disk backups, + or other mechanisms. Indeed, no distributed revision control + system can make data reliably vanish. Providing the illusion + of such control could easily give a false sense of security, + and be worse than not providing it at all. + +
+ + + Finding the source of a bug + + While it's all very well to be able to back out a changeset + that introduced a bug, this requires that you know which + changeset to back out. Mercurial provides an invaluable + command, called hg bisect, that + helps you to automate this process and accomplish it very + efficiently. + + The idea behind the hg + bisect command is that a changeset has introduced + some change of behavior that you can identify with a simple + pass/fail test. You don't know which piece of code introduced the + change, but you know how to test for the presence of the bug. + The hg bisect command uses your + test to direct its search for the changeset that introduced the + code that caused the bug. + + Here are a few scenarios to help you understand how you + might apply this command. + + The most recent version of your software has a + bug that you remember wasn't present a few weeks ago, but + you don't know when it was introduced. Here, your binary + test checks for the presence of that bug. + + You fixed a bug in a rush, and now it's time to + close the entry in your team's bug database. The bug + database requires a changeset ID when you close an entry, + but you don't remember which changeset you fixed the bug in. + Once again, your binary test checks for the presence of the + bug. + + Your software works correctly, but runs 15% + slower than the last time you measured it. You want to know + which changeset introduced the performance regression. In + this case, your binary test measures the performance of your + software, to see whether it's fast or + slow. + + The sizes of the components of your project that + you ship exploded recently, and you suspect that something + changed in the way you build your project. + + + From these examples, it should be clear that the hg bisect command is not useful only + for finding the sources of bugs. You can use it to find any + emergent property of a repository (anything that + you can't find from a simple text search of the files in the + tree) for which you can write a binary test. + + We'll introduce a little bit of terminology here, just to + make it clear which parts of the search process are your + responsibility, and which are Mercurial's. A + test is something that + you run when hg + bisect chooses a changeset. A + probe is what hg + bisect runs to tell whether a revision is good. + Finally, we'll use the word bisect, as both a + noun and a verb, to stand in for the phrase search using + the hg bisect + command. + + One simple way to automate the searching process would be + simply to probe every changeset. However, this scales poorly. + If it took ten minutes to test a single changeset, and you had + 10,000 changesets in your repository, the exhaustive approach + would take on average 35 days to find the + changeset that introduced a bug. Even if you knew that the bug + was introduced by one of the last 500 changesets, and limited + your search to those, you'd still be looking at over 40 hours to + find the changeset that introduced your bug. + + What the hg bisect command + does is use its knowledge of the shape of your + project's revision history to perform a search in time + proportional to the logarithm of the number + of changesets to check (the kind of search it performs is called + a dichotomic search). With this approach, searching through + 10,000 changesets will take less than three hours, even at ten + minutes per test (the search will require about 14 tests). + Limit your search to the last hundred changesets, and it will + take only about an hour (roughly seven tests). + + The hg bisect command is + aware of the branchy nature of a Mercurial + project's revision history, so it has no problems dealing with + branches, merges, or multiple heads in a repository. It can + prune entire branches of history with a single probe, which is + how it operates so efficiently. + + + Using the <command role="hg-cmd" moreinfo="none">hg bisect</command> + command + + Here's an example of hg + bisect in action. + + + In versions 0.9.5 and earlier of Mercurial, hg bisect was not a core command: + it was distributed with Mercurial as an extension. This + section describes the built-in command, not the old + extension. + + + Now let's create a repository, so that we can try out the + hg bisect command in + isolation. + + +$ hg init mybug +$ cd mybug + + + + + We'll simulate a project that has a bug in it in a + simple-minded way: create trivial changes in a loop, and + nominate one specific change that will have the + bug. This loop creates 35 changesets, each + adding a single file to the repository. We'll represent our + bug with a file that contains the text i + have a gub. + + +$ buggy_change=22 +$ for (( i = 0; i < 35; i++ )); do +> if [[ $i = $buggy_change ]]; then +> echo 'i have a gub' > myfile$i +> hg commit -q -A -m 'buggy changeset' +> else +> echo 'nothing to see here, move along' > myfile$i +> hg commit -q -A -m 'normal changeset' +> fi +> done + + + + + The next thing that we'd like to do is figure out how to + use the hg bisect command. + We can use Mercurial's normal built-in help mechanism for + this. + + +$ hg help bisect +hg bisect [-gbsr] [-c CMD] [REV] + +subdivision search of changesets + + This command helps to find changesets which introduce problems. + To use, mark the earliest changeset you know exhibits the problem + as bad, then mark the latest changeset which is free from the + problem as good. Bisect will update your working directory to a + revision for testing (unless the --noupdate option is specified). + Once you have performed tests, mark the working directory as bad + or good and bisect will either update to another candidate changeset + or announce that it has found the bad revision. + + As a shortcut, you can also use the revision argument to mark a + revision as good or bad without checking it out first. + + If you supply a command it will be used for automatic bisection. Its exit + status will be used as flag to mark revision as bad or good. In case exit + status is 0 the revision is marked as good, 125 - skipped, 127 (command not + found) - bisection will be aborted; any other status bigger than 0 will + mark revision as bad. + +options: + + -r --reset reset bisect state + -g --good mark changeset good + -b --bad mark changeset bad + -s --skip skip testing changeset + -c --command use command to check changeset state + -U --noupdate do not update to target + +use "hg -v help bisect" to show global options + + + + + The hg bisect command + works in steps. Each step proceeds as follows. + + You run your binary test. + + If the test succeeded, you tell hg bisect by running the + hg bisect --good + command. + + If it failed, run the hg bisect --bad + command. + + The command uses your information to decide + which changeset to test next. + + It updates the working directory to that + changeset, and the process begins again. + + The process ends when hg + bisect identifies a unique changeset that marks + the point where your test transitioned from + succeeding to failing. + + To start the search, we must run the hg bisect --reset command. + + +$ hg bisect --reset + + + + + In our case, the binary test we use is simple: we check to + see if any file in the repository contains the string i + have a gub. If it does, this changeset contains the + change that caused the bug. By convention, a + changeset that has the property we're searching for is + bad, while one that doesn't is + good. + + Most of the time, the revision to which the working + directory is synced (usually the tip) already exhibits the + problem introduced by the buggy change, so we'll mark it as + bad. + + +$ hg bisect --bad + + + + + Our next task is to nominate a changeset that we know + doesn't have the bug; the hg bisect command will + bracket its search between the first pair of + good and bad changesets. In our case, we know that revision + 10 didn't have the bug. (I'll have more words about choosing + the first good changeset later.) + + +$ hg bisect --good 10 +Testing changeset 22:69f52b967ab8 (24 changesets remaining, ~4 tests) +0 files updated, 0 files merged, 12 files removed, 0 files unresolved + + + + + Notice that this command printed some output. + + It told us how many changesets it must + consider before it can identify the one that introduced + the bug, and how many tests that will require. + + It updated the working directory to the next + changeset to test, and told us which changeset it's + testing. + + + We now run our test in the working directory. We use the + grep command to see if our + bad file is present in the working directory. + If it is, this revision is bad; if not, this revision is good. + +$ if grep -q 'i have a gub' * +> then +> result=bad +> else +> result=good +> fi +$ echo this revision is $result +this revision is bad +$ hg bisect --$result +Testing changeset 16:f1dd8bc690ae (12 changesets remaining, ~3 tests) +0 files updated, 0 files merged, 6 files removed, 0 files unresolved + + + + + This test looks like a perfect candidate for automation, + so let's turn it into a shell function. + +$ mytest() { +> if grep -q 'i have a gub' * +> then +> result=bad +> else +> result=good +> fi +> echo this revision is $result +> hg bisect --$result +> } + + + + + We can now run an entire test step with a single command, + mytest. + + +$ mytest +this revision is good +Testing changeset 19:88d99d97058a (6 changesets remaining, ~2 tests) +3 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + A few more invocations of our canned test step command, + and we're done. + + +$ mytest +this revision is good +Testing changeset 20:32a195a31d51 (3 changesets remaining, ~1 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ mytest +this revision is good +Testing changeset 21:a2efe8e4f624 (2 changesets remaining, ~1 tests) +1 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ mytest +this revision is good +The first bad revision is: +changeset: 22:69f52b967ab8 +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:04:39 2009 +0000 +summary: buggy changeset + + + + + + Even though we had 40 changesets to search through, the + hg bisect command let us find + the changeset that introduced our bug with only + five tests. Because the number of tests that the hg bisect command performs grows + logarithmically with the number of changesets to search, the + advantage that it has over the brute force + search approach increases with every changeset you add. + + + + Cleaning up after your search + + When you're finished using the hg + bisect command in a repository, you can use the + hg bisect --reset command to + drop the information it was using to drive your search. The + command doesn't use much space, so it doesn't matter if you + forget to run this command. However, hg bisect won't let you start a new + search in that repository until you do a hg bisect --reset. + + +$ hg bisect --reset + + + + + + + + Tips for finding bugs effectively + + + Give consistent input + + The hg bisect command + requires that you correctly report the result of every test + you perform. If you tell it that a test failed when it really + succeeded, it might be able to detect the + inconsistency. If it can identify an inconsistency in your + reports, it will tell you that a particular changeset is both + good and bad. However, it can't do this perfectly; it's about + as likely to report the wrong changeset as the source of the + bug. + + + + Automate as much as possible + + When I started using the hg + bisect command, I tried a few times to run my + tests by hand, on the command line. This is an approach that + I, at least, am not suited to. After a few tries, I found + that I was making enough mistakes that I was having to restart + my searches several times before finally getting correct + results. + + My initial problems with driving the hg bisect command by hand occurred + even with simple searches on small repositories; if the + problem you're looking for is more subtle, or the number of + tests that hg bisect must + perform increases, the likelihood of operator error ruining + the search is much higher. Once I started automating my + tests, I had much better results. + + The key to automated testing is twofold: + + always test for the same symptom, and + + always feed consistent input to the hg bisect command. + + In my tutorial example above, the grep + command tests for the symptom, and the if + statement takes the result of this check and ensures that we + always feed the same input to the hg + bisect command. The mytest + function marries these together in a reproducible way, so that + every test is uniform and consistent. + + + + Check your results + + Because the output of a hg + bisect search is only as good as the input you + give it, don't take the changeset it reports as the absolute + truth. A simple way to cross-check its report is to manually + run your test at each of the following changesets: + + The changeset that it reports as the first bad + revision. Your test should still report this as + bad. + + The parent of that changeset (either parent, + if it's a merge). Your test should report this changeset + as good. + + A child of that changeset. Your test should + report this changeset as bad. + + + + + Beware interference between bugs + + It's possible that your search for one bug could be + disrupted by the presence of another. For example, let's say + your software crashes at revision 100, and worked correctly at + revision 50. Unknown to you, someone else introduced a + different crashing bug at revision 60, and fixed it at + revision 80. This could distort your results in one of + several ways. + + It is possible that this other bug completely + masks yours, which is to say that it occurs + before your bug has a chance to manifest itself. If you can't + avoid that other bug (for example, it prevents your project + from building), and so can't tell whether your bug is present + in a particular changeset, the hg + bisect command cannot help you directly. Instead, + you can mark a changeset as untested by running hg bisect --skip. + + A different problem could arise if your test for a bug's + presence is not specific enough. If you check for my + program crashes, then both your crashing bug and an + unrelated crashing bug that masks it will look like the same + thing, and mislead hg + bisect. + + Another useful situation in which to use hg bisect --skip is if you can't + test a revision because your project was in a broken and hence + untestable state at that revision, perhaps because someone + checked in a change that prevented the project from + building. + + + + Bracket your search lazily + + Choosing the first good and + bad changesets that will mark the end points of + your search is often easy, but it bears a little discussion + nevertheless. From the perspective of hg bisect, the newest + changeset is conventionally bad, and the older + changeset is good. + + If you're having trouble remembering when a suitable + good change was, so that you can tell hg bisect, you could do worse than + testing changesets at random. Just remember to eliminate + contenders that can't possibly exhibit the bug (perhaps + because the feature with the bug isn't present yet) and those + where another problem masks the bug (as I discussed + above). + + Even if you end up early by thousands of + changesets or months of history, you will only add a handful + of tests to the total number that hg + bisect must perform, thanks to its logarithmic + behavior. + + + +
+ + + + + + + + + Handling repository events with hooks + + Mercurial offers a powerful mechanism to let you perform + automated actions in response to events that occur in a + repository. In some cases, you can even control Mercurial's + response to those events. + + The name Mercurial uses for one of these actions is a + hook. Hooks are called + triggers in some revision control systems, but the + two names refer to the same idea. + + + An overview of hooks in Mercurial + + Here is a brief list of the hooks that Mercurial + supports. We will revisit each of these hooks in more detail + later, in . + + Each of the hooks whose description begins with the word + Controlling has the ability to determine whether + an activity can proceed. If the hook succeeds, the activity may + proceed; if it fails, the activity is either not permitted or + undone, depending on the hook. + + + changegroup: This + is run after a group of changesets has been brought into the + repository from elsewhere. + + commit: This is + run after a new changeset has been created in the local + repository. + + incoming: This is + run once for each new changeset that is brought into the + repository from elsewhere. Notice the difference from + changegroup, which is run + once per group of changesets brought + in. + + outgoing: This is + run after a group of changesets has been transmitted from + this repository. + + prechangegroup: + This is run before starting to bring a group of changesets + into the repository. + + + precommit: + Controlling. This is run before starting a commit. + + + preoutgoing: + Controlling. This is run before starting to transmit a group + of changesets from this repository. + + + pretag: + Controlling. This is run before creating a tag. + + + pretxnchangegroup: Controlling. This + is run after a group of changesets has been brought into the + local repository from another, but before the transaction + completes that will make the changes permanent in the + repository. + + + pretxncommit: + Controlling. This is run after a new changeset has been + created in the local repository, but before the transaction + completes that will make it permanent. + + + preupdate: + Controlling. This is run before starting an update or merge + of the working directory. + + + tag: This is run + after a tag is created. + + + update: This is + run after an update or merge of the working directory has + finished. + + + + + + Hooks and security + + + Hooks are run with your privileges + + When you run a Mercurial command in a repository, and the + command causes a hook to run, that hook runs on + your system, under + your user account, with + your privilege level. Since hooks are + arbitrary pieces of executable code, you should treat them + with an appropriate level of suspicion. Do not install a hook + unless you are confident that you know who created it and what + it does. + + + In some cases, you may be exposed to hooks that you did + not install yourself. If you work with Mercurial on an + unfamiliar system, Mercurial will run hooks defined in that + system's global ~/.hgrc + file. + + + If you are working with a repository owned by another + user, Mercurial can run hooks defined in that user's + repository, but it will still run them as you. + For example, if you hg pull + from that repository, and its .hg/hgrc defines a local outgoing hook, that hook will run + under your user account, even though you don't own that + repository. + + + + This only applies if you are pulling from a repository + on a local or network filesystem. If you're pulling over + http or ssh, any outgoing + hook will run under whatever account is executing the server + process, on the server. + + + + To see what hooks are defined in a repository, + use the hg showconfig hooks + command. If you are working in one repository, but talking to + another that you do not own (e.g. using hg pull or hg + incoming), remember that it is the other + repository's hooks you should be checking, not your own. + + + + + Hooks do not propagate + + In Mercurial, hooks are not revision controlled, and do + not propagate when you clone, or pull from, a repository. The + reason for this is simple: a hook is a completely arbitrary + piece of executable code. It runs under your user identity, + with your privilege level, on your machine. + + + It would be extremely reckless for any distributed + revision control system to implement revision-controlled + hooks, as this would offer an easily exploitable way to + subvert the accounts of users of the revision control system. + + + Since Mercurial does not propagate hooks, if you are + collaborating with other people on a common project, you + should not assume that they are using the same Mercurial hooks + as you are, or that theirs are correctly configured. You + should document the hooks you expect people to use. + + + In a corporate intranet, this is somewhat easier to + control, as you can for example provide a + standard installation of Mercurial on an NFS + filesystem, and use a site-wide ~/.hgrc file to define hooks that all users will + see. However, this too has its limits; see below. + + + + + Hooks can be overridden + + Mercurial allows you to override a hook definition by + redefining the hook. You can disable it by setting its value + to the empty string, or change its behavior as you wish. + + + If you deploy a system- or site-wide ~/.hgrc file that defines some + hooks, you should thus understand that your users can disable + or override those hooks. + + + + + Ensuring that critical hooks are run + + Sometimes you may want to enforce a policy that you do not + want others to be able to work around. For example, you may + have a requirement that every changeset must pass a rigorous + set of tests. Defining this requirement via a hook in a + site-wide ~/.hgrc won't + work for remote users on laptops, and of course local users + can subvert it at will by overriding the hook. + + + Instead, you can set up your policies for use of Mercurial + so that people are expected to propagate changes through a + well-known canonical server that you have + locked down and configured appropriately. + + + One way to do this is via a combination of social + engineering and technology. Set up a restricted-access + account; users can push changes over the network to + repositories managed by this account, but they cannot log into + the account and run normal shell commands. In this scenario, + a user can commit a changeset that contains any old garbage + they want. + + + When someone pushes a changeset to the server that + everyone pulls from, the server will test the changeset before + it accepts it as permanent, and reject it if it fails to pass + the test suite. If people only pull changes from this + filtering server, it will serve to ensure that all changes + that people pull have been automatically vetted. + + + + + + + A short tutorial on using hooks + + It is easy to write a Mercurial hook. Let's start with a + hook that runs when you finish a hg + commit, and simply prints the hash of the changeset + you just created. The hook is called commit. + + + All hooks follow the pattern in this example. + + +$ hg init hook-test +$ cd hook-test +$ echo '[hooks]' >> .hg/hgrc +$ echo 'commit = echo committed $HG_NODE' >> .hg/hgrc +$ cat .hg/hgrc +[hooks] +commit = echo committed $HG_NODE +$ echo a > a +$ hg add a +$ hg commit -m 'testing commit hook' +committed 13a334d1e5ca83fea465aa779110eec3c5ddd6b1 + + + + + You add an entry to the hooks section of your ~/.hgrc. On the left is the name of + the event to trigger on; on the right is the action to take. As + you can see, you can run an arbitrary shell command in a hook. + Mercurial passes extra information to the hook using environment + variables (look for HG_NODE in the example). + + + + Performing multiple actions per event + + Quite often, you will want to define more than one hook + for a particular kind of event, as shown below. + + +$ echo 'commit.when = echo -n "date of commit: "; date' >> .hg/hgrc +$ echo a >> a +$ hg commit -m 'i have two hooks' +committed 3be6e2778fb853cbc7e5138d0b9c29386504670b +date of commit: Sun Aug 16 14:05:05 GMT 2009 + + + + + Mercurial lets you do this by adding an + extension to the end of a hook's name. + You extend a hook's name by giving the name of the hook, + followed by a full stop (the + . character), followed by + some more text of your choosing. For example, Mercurial will + run both commit.foo and + commit.bar when the + commit event occurs. + + + To give a well-defined order of execution when there are + multiple hooks defined for an event, Mercurial sorts hooks by + extension, and executes the hook commands in this sorted + order. In the above example, it will execute + commit.bar before + commit.foo, and commit + before both. + + + It is a good idea to use a somewhat descriptive + extension when you define a new hook. This will help you to + remember what the hook was for. If the hook fails, you'll get + an error message that contains the hook name and extension, so + using a descriptive extension could give you an immediate hint + as to why the hook failed (see for an example). + + + + + Controlling whether an activity can proceed + + In our earlier examples, we used the commit hook, which is run after a + commit has completed. This is one of several Mercurial hooks + that run after an activity finishes. Such hooks have no way + of influencing the activity itself. + + + Mercurial defines a number of events that occur before an + activity starts; or after it starts, but before it finishes. + Hooks that trigger on these events have the added ability to + choose whether the activity can continue, or will abort. + + + The pretxncommit hook runs + after a commit has all but completed. In other words, the + metadata representing the changeset has been written out to + disk, but the transaction has not yet been allowed to + complete. The pretxncommit + hook has the ability to decide whether the transaction can + complete, or must be rolled back. + + + If the pretxncommit hook + exits with a status code of zero, the transaction is allowed + to complete; the commit finishes; and the commit hook is run. If the pretxncommit hook exits with a + non-zero status code, the transaction is rolled back; the + metadata representing the changeset is erased; and the + commit hook is not run. + + + +$ cat check_bug_id +#!/bin/sh +# check that a commit comment mentions a numeric bug id +hg log -r $1 --template {desc} | grep -q "\<bug *[0-9]" +$ echo 'pretxncommit.bug_id_required = ./check_bug_id $HG_NODE' >> .hg/hgrc +$ echo a >> a +$ hg commit -m 'i am not mentioning a bug id' +transaction abort! +rollback completed +abort: pretxncommit.bug_id_required hook exited with status 1 +$ hg commit -m 'i refer you to bug 666' +committed 1a52be73a1ca4fa05e269f99003ed00912e8e836 +date of commit: Sun Aug 16 14:05:05 GMT 2009 + + + + + The hook in the example above checks that a commit comment + contains a bug ID. If it does, the commit can complete. If + not, the commit is rolled back. + + + + + + Writing your own hooks + + When you are writing a hook, you might find it useful to run + Mercurial either with the option, or the verbose config item set to + true. When you do so, Mercurial will print a + message before it calls each hook. + + + + Choosing how your hook should run + + You can write a hook either as a normal + program—typically a shell script—or as a Python + function that is executed within the Mercurial process. + + + Writing a hook as an external program has the advantage + that it requires no knowledge of Mercurial's internals. You + can call normal Mercurial commands to get any added + information you need. The trade-off is that external hooks + are slower than in-process hooks. + + + An in-process Python hook has complete access to the + Mercurial API, and does not shell out to + another process, so it is inherently faster than an external + hook. It is also easier to obtain much of the information + that a hook requires by using the Mercurial API than by + running Mercurial commands. + + + If you are comfortable with Python, or require high + performance, writing your hooks in Python may be a good + choice. However, when you have a straightforward hook to + write and you don't need to care about performance (probably + the majority of hooks), a shell script is perfectly fine. + + + + + Hook parameters + + Mercurial calls each hook with a set of well-defined + parameters. In Python, a parameter is passed as a keyword + argument to your hook function. For an external program, a + parameter is passed as an environment variable. + + + Whether your hook is written in Python or as a shell + script, the hook-specific parameter names and values will be + the same. A boolean parameter will be represented as a + boolean value in Python, but as the number 1 (for + true) or 0 (for false) as an + environment variable for an external hook. If a hook + parameter is named foo, the keyword + argument for a Python hook will also be named + foo, while the environment variable for an + external hook will be named HG_FOO. + + + + + Hook return values and activity control + + A hook that executes successfully must exit with a status + of zero if external, or return boolean false if + in-process. Failure is indicated with a non-zero exit status + from an external hook, or an in-process hook returning boolean + true. If an in-process hook raises an + exception, the hook is considered to have failed. + + + For a hook that controls whether an activity can proceed, + zero/false means allow, while + non-zero/true/exception means deny. + + + + + Writing an external hook + + When you define an external hook in your ~/.hgrc and the hook is run, its + value is passed to your shell, which interprets it. This + means that you can use normal shell constructs in the body of + the hook. + + + An executable hook is always run with its current + directory set to a repository's root directory. + + + Each hook parameter is passed in as an environment + variable; the name is upper-cased, and prefixed with the + string HG_. + + + With the exception of hook parameters, Mercurial does not + set or modify any environment variables when running a hook. + This is useful to remember if you are writing a site-wide hook + that may be run by a number of different users with differing + environment variables set. In multi-user situations, you + should not rely on environment variables being set to the + values you have in your environment when testing the hook. + + + + + Telling Mercurial to use an in-process hook + + The ~/.hgrc syntax + for defining an in-process hook is slightly different than for + an executable hook. The value of the hook must start with the + text python:, and continue + with the fully-qualified name of a callable object to use as + the hook's value. + + + The module in which a hook lives is automatically imported + when a hook is run. So long as you have the module name and + PYTHONPATH right, it should just + work. + + + The following ~/.hgrc + example snippet illustrates the syntax and meaning of the + notions we just described. + + [hooks] +commit.example = python:mymodule.submodule.myhook + When Mercurial runs the commit.example + hook, it imports mymodule.submodule, looks + for the callable object named myhook, and + calls it. + + + + + Writing an in-process hook + + The simplest in-process hook does nothing, but illustrates + the basic shape of the hook API: + + def myhook(ui, repo, **kwargs): + pass + The first argument to a Python hook is always a ui object. The second + is a repository object; at the moment, it is always an + instance of localrepository. + Following these two arguments are other keyword arguments. + Which ones are passed in depends on the hook being called, but + a hook can ignore arguments it doesn't care about by dropping + them into a keyword argument dict, as with + **kwargs above. + + + + + + Some hook examples + + + Writing meaningful commit messages + + It's hard to imagine a useful commit message being very + short. The simple pretxncommit + hook of the example below will prevent you from committing a + changeset with a message that is less than ten bytes long. + + + +$ cat .hg/hgrc +[hooks] +pretxncommit.msglen = test `hg tip --template {desc} | wc -c` -ge 10 +$ echo a > a +$ hg add a +$ hg commit -A -m 'too short' +transaction abort! +rollback completed +abort: pretxncommit.msglen hook exited with status 1 +$ hg commit -A -m 'long enough' + + + + + + + Checking for trailing whitespace + + An interesting use of a commit-related hook is to help you + to write cleaner code. A simple example of cleaner + code is the dictum that a change should not add any + new lines of text that contain trailing + whitespace. Trailing whitespace is a series of + space and tab characters at the end of a line of text. In + most cases, trailing whitespace is unnecessary, invisible + noise, but it is occasionally problematic, and people often + prefer to get rid of it. + + + You can use either the precommit or pretxncommit hook to tell whether you + have a trailing whitespace problem. If you use the precommit hook, the hook will not know + which files you are committing, so it will have to check every + modified file in the repository for trailing white space. If + you want to commit a change to just the file + foo, but the file + bar contains trailing whitespace, doing a + check in the precommit hook + will prevent you from committing foo due + to the problem with bar. This doesn't + seem right. + + + Should you choose the pretxncommit hook, the check won't + occur until just before the transaction for the commit + completes. This will allow you to check for problems only the + exact files that are being committed. However, if you entered + the commit message interactively and the hook fails, the + transaction will roll back; you'll have to re-enter the commit + message after you fix the trailing whitespace and run hg commit again. + + + +$ cat .hg/hgrc +[hooks] +pretxncommit.whitespace = hg export tip | (! egrep -q '^\+.*[ \t]$') +$ echo 'a ' > a +$ hg commit -A -m 'test with trailing whitespace' +adding a +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ echo 'a' > a +$ hg commit -A -m 'drop trailing whitespace and try again' + + + + + In this example, we introduce a simple pretxncommit hook that checks for + trailing whitespace. This hook is short, but not very + helpful. It exits with an error status if a change adds a + line with trailing whitespace to any file, but does not print + any information that might help us to identify the offending + file or line. It also has the nice property of not paying + attention to unmodified lines; only lines that introduce new + trailing whitespace cause problems. + + + +#!/usr/bin/env python +# +# save as .hg/check_whitespace.py and make executable + +import re + +def trailing_whitespace(difflines): + # + linenum, header = 0, False + + for line in difflines: + if header: + # remember the name of the file that this diff affects + m = re.match(r'(?:---|\+\+\+) ([^\t]+)', line) + if m and m.group(1) != '/dev/null': + filename = m.group(1).split('/', 1)[-1] + if line.startswith('+++ '): + header = False + continue + if line.startswith('diff '): + header = True + continue + # hunk header - save the line number + m = re.match(r'@@ -\d+,\d+ \+(\d+),', line) + if m: + linenum = int(m.group(1)) + continue + # hunk body - check for an added line with trailing whitespace + m = re.match(r'\+.*\s$', line) + if m: + yield filename, linenum + if line and line[0] in ' +': + linenum += 1 + +if __name__ == '__main__': + import os, sys + + added = 0 + for filename, linenum in trailing_whitespace(os.popen('hg export tip')): + print >> sys.stderr, ('%s, line %d: trailing whitespace added' % + (filename, linenum)) + added += 1 + if added: + # save the commit message so we don't need to retype it + os.system('hg tip --template "{desc}" > .hg/commit.save') + print >> sys.stderr, 'commit message saved to .hg/commit.save' + sys.exit(1) + + + + The above version is much more complex, but also more + useful. It parses a unified diff to see if any lines add + trailing whitespace, and prints the name of the file and the + line number of each such occurrence. Even better, if the + change adds trailing whitespace, this hook saves the commit + comment and prints the name of the save file before exiting + and telling Mercurial to roll the transaction back, so you can + use the + option to hg commit to reuse + the saved commit message once you've corrected the problem. + + + +$ cat .hg/hgrc +[hooks] +pretxncommit.whitespace = .hg/check_whitespace.py +$ echo 'a ' >> a +$ hg commit -A -m 'add new line with trailing whitespace' +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 +$ sed -i 's, *$,,' a +$ hg commit -A -m 'trimmed trailing whitespace' +a, line 2: trailing whitespace added +commit message saved to .hg/commit.save +transaction abort! +rollback completed +abort: pretxncommit.whitespace hook exited with status 1 + + + + + As a final aside, note in the example above the + use of sed's in-place editing feature to + get rid of trailing whitespace from a file. This is concise + and useful enough that I will reproduce it here (using + perl for good measure). + perl -pi -e 's,\s+$,,' filename + + + + + Bundled hooks + + Mercurial ships with several bundled hooks. You can find + them in the hgext + directory of a Mercurial source tree. If you are using a + Mercurial binary package, the hooks will be located in the + hgext directory of + wherever your package installer put Mercurial. + + + + <literal role="hg-ext" moreinfo="none">acl</literal>—access + control for parts of a repository + + The acl extension lets + you control which remote users are allowed to push changesets + to a networked server. You can protect any portion of a + repository (including the entire repo), so that a specific + remote user can push changes that do not affect the protected + portion. + + + This extension implements access control based on the + identity of the user performing a push, + not on who committed the changesets + they're pushing. It makes sense to use this hook only if you + have a locked-down server environment that authenticates + remote users, and you want to be sure that only specific users + are allowed to push changes to that server. + + + + Configuring the <literal role="hook" moreinfo="none">acl</literal> + hook + + In order to manage incoming changesets, the acl hook must be used as a + pretxnchangegroup hook. This + lets it see which files are modified by each incoming + changeset, and roll back a group of changesets if they + modify forbidden files. Example: + + [hooks] +pretxnchangegroup.acl = python:hgext.acl.hook + + The acl extension is + configured using three sections. + + + The acl section has + only one entry, sources, + which lists the sources of incoming changesets that the hook + should pay attention to. You don't normally need to + configure this section. + + + serve: + Control incoming changesets that are arriving from a + remote repository over http or ssh. This is the default + value of sources, and + usually the only setting you'll need for this + configuration item. + + + pull: + Control incoming changesets that are arriving via a pull + from a local repository. + + + push: + Control incoming changesets that are arriving via a push + from a local repository. + + + bundle: + Control incoming changesets that are arriving from + another repository via a bundle. + + + + The acl.allow + section controls the users that are allowed to add + changesets to the repository. If this section is not + present, all users that are not explicitly denied are + allowed. If this section is present, all users that are not + explicitly allowed are denied (so an empty section means + that all users are denied). + + + The acl.deny + section determines which users are denied from adding + changesets to the repository. If this section is not + present or is empty, no users are denied. + + + The syntaxes for the acl.allow and acl.deny sections are + identical. On the left of each entry is a glob pattern that + matches files or directories, relative to the root of the + repository; on the right, a user name. + + + In the following example, the user + docwriter can only push changes to the + docs subtree of the + repository, while intern can push changes + to any file or directory except source/sensitive. + + [acl.allow] +docs/** = docwriter +[acl.deny] +source/sensitive/** = intern + + + + Testing and troubleshooting + + If you want to test the acl hook, run it with Mercurial's + debugging output enabled. Since you'll probably be running + it on a server where it's not convenient (or sometimes + possible) to pass in the option, don't forget + that you can enable debugging output in your ~/.hgrc: + + [ui] +debug = true + With this enabled, the acl hook will print enough + information to let you figure out why it is allowing or + forbidding pushes from specific users. + + + + + + <literal role="hg-ext" moreinfo="none">bugzilla</literal>—integration with + Bugzilla + + The bugzilla extension + adds a comment to a Bugzilla bug whenever it finds a reference + to that bug ID in a commit comment. You can install this hook + on a shared server, so that any time a remote user pushes + changes to this server, the hook gets run. + + + It adds a comment to the bug that looks like this (you can + configure the contents of the comment—see below): + + Changeset aad8b264143a, made by Joe User + <joe.user@domain.com> in the frobnitz repository, refers + to this bug. For complete details, see + http://hg.domain.com/frobnitz?cmd=changeset;node=aad8b264143a + Changeset description: Fix bug 10483 by guarding against some + NULL pointers + The value of this hook is that it automates the process of + updating a bug any time a changeset refers to it. If you + configure the hook properly, it makes it easy for people to + browse straight from a Bugzilla bug to a changeset that refers + to that bug. + + + You can use the code in this hook as a starting point for + some more exotic Bugzilla integration recipes. Here are a few + possibilities: + + + Require that every changeset pushed to the + server have a valid bug ID in its commit comment. In this + case, you'd want to configure the hook as a pretxncommit hook. This would + allow the hook to reject changes that didn't contain bug + IDs. + + + Allow incoming changesets to automatically + modify the state of a bug, as well as + simply adding a comment. For example, the hook could + recognise the string fixed bug 31337 as + indicating that it should update the state of bug 31337 to + requires testing. + + + + + Configuring the <literal role="hook" moreinfo="none">bugzilla</literal> + hook + + You should configure this hook in your server's + ~/.hgrc as an incoming hook, for example as + follows: + + [hooks] +incoming.bugzilla = python:hgext.bugzilla.hook + + Because of the specialised nature of this hook, and + because Bugzilla was not written with this kind of + integration in mind, configuring this hook is a somewhat + involved process. + + + Before you begin, you must install the MySQL bindings + for Python on the host(s) where you'll be running the hook. + If this is not available as a binary package for your + system, you can download it from + web:mysql-python. + + + Configuration information for this hook lives in the + bugzilla section of + your ~/.hgrc. + + + version: The version + of Bugzilla installed on the server. The database + schema that Bugzilla uses changes occasionally, so this + hook has to know exactly which schema to use. + + host: + The hostname of the MySQL server that stores your + Bugzilla data. The database must be configured to allow + connections from whatever host you are running the + bugzilla hook on. + + + user: + The username with which to connect to the MySQL server. + The database must be configured to allow this user to + connect from whatever host you are running the bugzilla hook on. This user + must be able to access and modify Bugzilla tables. The + default value of this item is bugs, + which is the standard name of the Bugzilla user in a + MySQL database. + + + password: The MySQL + password for the user you configured above. This is + stored as plain text, so you should make sure that + unauthorised users cannot read the ~/.hgrc file where you + store this information. + + + db: + The name of the Bugzilla database on the MySQL server. + The default value of this item is + bugs, which is the standard name of + the MySQL database where Bugzilla stores its data. + + + notify: If you want + Bugzilla to send out a notification email to subscribers + after this hook has added a comment to a bug, you will + need this hook to run a command whenever it updates the + database. The command to run depends on where you have + installed Bugzilla, but it will typically look something + like this, if you have Bugzilla installed in /var/www/html/bugzilla: + + cd /var/www/html/bugzilla && + ./processmail %s nobody@nowhere.com + + The Bugzilla + processmail program expects to be + given a bug ID (the hook replaces + %s with the bug ID) + and an email address. It also expects to be able to + write to some files in the directory that it runs in. + If Bugzilla and this hook are not installed on the same + machine, you will need to find a way to run + processmail on the server where + Bugzilla is installed. + + + + + + Mapping committer names to Bugzilla user names + + By default, the bugzilla hook tries to use the + email address of a changeset's committer as the Bugzilla + user name with which to update a bug. If this does not suit + your needs, you can map committer email addresses to + Bugzilla user names using a usermap section. + + + Each item in the usermap section contains an + email address on the left, and a Bugzilla user name on the + right. + + [usermap] +jane.user@example.com = jane + You can either keep the usermap data in a normal + ~/.hgrc, or tell the + bugzilla hook to read the + information from an external usermap + file. In the latter case, you can store + usermap data by itself in (for example) + a user-modifiable repository. This makes it possible to let + your users maintain their own usermap entries. The main + ~/.hgrc file might look + like this: + + # regular hgrc file refers to external usermap file +[bugzilla] +usermap = /home/hg/repos/userdata/bugzilla-usermap.conf + While the usermap file that it + refers to might look like this: + + # bugzilla-usermap.conf - inside a hg repository +[usermap] stephanie@example.com = steph + + + + Configuring the text that gets added to a bug + + You can configure the text that this hook adds as a + comment; you specify it in the form of a Mercurial template. + Several ~/.hgrc entries + (still in the bugzilla + section) control this behavior. + + + strip: The number of + leading path elements to strip from a repository's path + name to construct a partial path for a URL. For example, + if the repositories on your server live under /home/hg/repos, and you + have a repository whose path is /home/hg/repos/app/tests, + then setting strip to + 4 will give a partial path of + app/tests. The + hook will make this partial path available when + expanding a template, as webroot. + + + template: The text of the + template to use. In addition to the usual + changeset-related variables, this template can use + hgweb (the value of the + hgweb configuration item above) and + webroot (the path constructed using + strip above). + + + + In addition, you can add a baseurl item to the web section of your ~/.hgrc. The bugzilla hook will make this + available when expanding a template, as the base string to + use when constructing a URL that will let users browse from + a Bugzilla comment to view a changeset. Example: + + [web] +baseurl = http://hg.domain.com/ + + Here is an example set of bugzilla hook config information. + + + +[bugzilla] +host = bugzilla.example.com +password = mypassword version = 2.16 +# server-side repos live in /home/hg/repos, so strip 4 leading +# separators +strip = 4 +hgweb = http://hg.example.com/ +usermap = /home/hg/repos/notify/bugzilla.conf +template = Changeset {node|short}, made by {author} in the {webroot} + repo, refers to this bug.\n + For complete details, see + {hgweb}{webroot}?cmd=changeset;node={node|short}\n + Changeset description:\n + \t{desc|tabindent} + + + + + + Testing and troubleshooting + + The most common problems with configuring the bugzilla hook relate to running + Bugzilla's processmail script and + mapping committer names to user names. + + + Recall from above that the user + that runs the Mercurial process on the server is also the + one that will run the processmail + script. The processmail script + sometimes causes Bugzilla to write to files in its + configuration directory, and Bugzilla's configuration files + are usually owned by the user that your web server runs + under. + + + You can cause processmail to be run + with the suitable user's identity using the + sudo command. Here is an example entry + for a sudoers file. + + hg_user = (httpd_user) +NOPASSWD: /var/www/html/bugzilla/processmail-wrapper %s + This allows the hg_user user to run a + processmail-wrapper program under the + identity of httpd_user. + + + This indirection through a wrapper script is necessary, + because processmail expects to be run + with its current directory set to wherever you installed + Bugzilla; you can't specify that kind of constraint in a + sudoers file. The contents of the + wrapper script are simple: + + #!/bin/sh +cd `dirname $0` && ./processmail "$1" nobody@example.com + It doesn't seem to matter what email address you pass to + processmail. + + + If your usermap is + not set up correctly, users will see an error message from + the bugzilla hook when they + push changes to the server. The error message will look + like this: + + cannot find bugzilla user id for john.q.public@example.com + What this means is that the committer's address, + john.q.public@example.com, is not a valid + Bugzilla user name, nor does it have an entry in your + usermap that maps it to + a valid Bugzilla user name. + + + + + + <literal role="hg-ext" moreinfo="none">notify</literal>—send email + notifications + + Although Mercurial's built-in web server provides RSS + feeds of changes in every repository, many people prefer to + receive change notifications via email. The notify hook lets you send out + notifications to a set of email addresses whenever changesets + arrive that those subscribers are interested in. + + + As with the bugzilla + hook, the notify hook is + template-driven, so you can customise the contents of the + notification messages that it sends. + + + By default, the notify + hook includes a diff of every changeset that it sends out; you + can limit the size of the diff, or turn this feature off + entirely. It is useful for letting subscribers review changes + immediately, rather than clicking to follow a URL. + + + + Configuring the <literal role="hg-ext" moreinfo="none">notify</literal> + hook + + You can set up the notify hook to send one email + message per incoming changeset, or one per incoming group of + changesets (all those that arrived in a single pull or + push). + + [hooks] +# send one email per group of changes +changegroup.notify = python:hgext.notify.hook +# send one email per change +incoming.notify = python:hgext.notify.hook + + Configuration information for this hook lives in the + notify section of a + ~/.hgrc file. + + + test: + By default, this hook does not send out email at all; + instead, it prints the message that it + would send. Set this item to + false to allow email to be sent. The + reason that sending of email is turned off by default is + that it takes several tries to configure this extension + exactly as you would like, and it would be bad form to + spam subscribers with a number of broken + notifications while you debug your configuration. + + + config: + The path to a configuration file that contains + subscription information. This is kept separate from + the main ~/.hgrc so + that you can maintain it in a repository of its own. + People can then clone that repository, update their + subscriptions, and push the changes back to your server. + + + strip: + The number of leading path separator characters to strip + from a repository's path, when deciding whether a + repository has subscribers. For example, if the + repositories on your server live in /home/hg/repos, and + notify is considering a + repository named /home/hg/repos/shared/test, + setting strip to + 4 will cause notify to trim the path it + considers down to shared/test, and it will + match subscribers against that. + + + template: The template + text to use when sending messages. This specifies both + the contents of the message header and its body. + + + maxdiff: The maximum + number of lines of diff data to append to the end of a + message. If a diff is longer than this, it is + truncated. By default, this is set to 300. Set this to + 0 to omit diffs from notification + emails. + + + sources: A list of + sources of changesets to consider. This lets you limit + notify to only sending + out email about changes that remote users pushed into + this repository via a server, for example. See + for the sources you + can specify here. + + + + If you set the baseurl + item in the web section, + you can use it in a template; it will be available as + webroot. + + + Here is an example set of notify configuration information. + + + +[notify] +# really send email +test = false +# subscriber data lives in the notify repo +config = /home/hg/repos/notify/notify.conf +# repos live in /home/hg/repos on server, so strip 4 "/" chars +strip = 4 +template = X-Hg-Repo: {webroot}\n + Subject: {webroot}: {desc|firstline|strip}\n + From: {author} + \n\n + changeset {node|short} in {root} + \n\ndetails: + {baseurl}{webroot}?cmd=changeset;node={node|short} + description: {desc|tabindent|strip} + +[web] +baseurl = +http://hg.example.com/ + + + + This will produce a message that looks like the + following: + + + +X-Hg-Repo: tests/slave +Subject: tests/slave: Handle error case when slave has no buffers +Date: Wed, 2 Aug 2006 15:25:46 -0700 (PDT) + +changeset 3cba9bfe74b5 in /home/hg/repos/tests/slave + +details: +http://hg.example.com/tests/slave?cmd=changeset;node=3cba9bfe74b5 + +description: Handle error case when slave has no buffers + +diffs (54 lines): +diff -r 9d95df7cf2ad -r 3cba9bfe74b5 include/tests.h +--- a/include/tests.h Wed Aug 02 15:19:52 2006 -0700 ++++ b/include/tests.h Wed Aug 02 15:25:26 2006 -0700 +@@ -212,6 +212,15 @@ static __inline__ +void test_headers(void *h) +[...snip...] + + + + + + Testing and troubleshooting + + Do not forget that by default, the notify extension will not + send any mail until you explicitly configure it to do so, + by setting test to + false. Until you do that, it simply + prints the message it would send. + + + + + + + Information for writers of hooks + + + In-process hook execution + + An in-process hook is called with arguments of the + following form: + + def myhook(ui, repo, **kwargs): pass + The ui parameter is a ui object. The + repo parameter is a localrepository + object. The names and values of the + **kwargs parameters depend on the hook + being invoked, with the following common features: + + + If a parameter is named + node or parentN, it + will contain a hexadecimal changeset ID. The empty string + is used to represent null changeset ID + instead of a string of zeroes. + + + If a parameter is named + url, it will contain the URL of a + remote repository, if that can be determined. + + + Boolean-valued parameters are represented as + Python bool objects. + + + + An in-process hook is called without a change to the + process's working directory (unlike external hooks, which are + run in the root of the repository). It must not change the + process's working directory, or it will cause any calls it + makes into the Mercurial API to fail. + + + If a hook returns a boolean false value, it + is considered to have succeeded. If it returns a boolean + true value or raises an exception, it is + considered to have failed. A useful way to think of the + calling convention is tell me if you fail. + + + Note that changeset IDs are passed into Python hooks as + hexadecimal strings, not the binary hashes that Mercurial's + APIs normally use. To convert a hash from hex to binary, use + the bin function. + + + + + External hook execution + + An external hook is passed to the shell of the user + running Mercurial. Features of that shell, such as variable + substitution and command redirection, are available. The hook + is run in the root directory of the repository (unlike + in-process hooks, which are run in the same directory that + Mercurial was run in). + + + Hook parameters are passed to the hook as environment + variables. Each environment variable's name is converted in + upper case and prefixed with the string + HG_. For example, if the + name of a parameter is node, + the name of the environment variable representing that + parameter will be HG_NODE. + + + A boolean parameter is represented as the string + 1 for true, + 0 for false. + If an environment variable is named HG_NODE, + HG_PARENT1 or HG_PARENT2, it + contains a changeset ID represented as a hexadecimal string. + The empty string is used to represent null changeset + ID instead of a string of zeroes. If an environment + variable is named HG_URL, it will contain the + URL of a remote repository, if that can be determined. + + + If a hook exits with a status of zero, it is considered to + have succeeded. If it exits with a non-zero status, it is + considered to have failed. + + + + + Finding out where changesets come from + + A hook that involves the transfer of changesets between a + local repository and another may be able to find out + information about the far side. Mercurial + knows how changes are being transferred, + and in many cases where they are being + transferred to or from. + + + + Sources of changesets + + Mercurial will tell a hook what means are, or were, used + to transfer changesets between repositories. This is + provided by Mercurial in a Python parameter named + source, or an environment variable named + HG_SOURCE. + + + + serve: Changesets are + transferred to or from a remote repository over http or + ssh. + + + pull: Changesets are + being transferred via a pull from one repository into + another. + + + push: Changesets are + being transferred via a push from one repository into + another. + + + bundle: Changesets are + being transferred to or from a bundle. + + + + + + Where changes are going—remote repository + URLs + + When possible, Mercurial will tell a hook the location + of the far side of an activity that transfers + changeset data between repositories. This is provided by + Mercurial in a Python parameter named + url, or an environment variable named + HG_URL. + + + This information is not always known. If a hook is + invoked in a repository that is being served via http or + ssh, Mercurial cannot tell where the remote repository is, + but it may know where the client is connecting from. In + such cases, the URL will take one of the following forms: + + + remote:ssh:1.2.3.4—remote + ssh client, at the IP address + 1.2.3.4. + + + remote:http:1.2.3.4—remote + http client, at the IP address + 1.2.3.4. If the client is using SSL, + this will be of the form + remote:https:1.2.3.4. + + + Empty—no information could be + discovered about the remote client. + + + + + + + Hook reference + + + <literal role="hook" moreinfo="none">changegroup</literal>—after + remote changesets added + + This hook is run after a group of pre-existing changesets + has been added to the repository, for example via a hg pull or hg + unbundle. This hook is run once per operation + that added one or more changesets. This is in contrast to the + incoming hook, which is run + once per changeset, regardless of whether the changesets + arrive in a group. + + + Some possible uses for this hook include kicking off an + automated build or test of the added changesets, updating a + bug database, or notifying subscribers that a repository + contains new changes. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + tip, inclusive, were added by a single + hg pull, hg push or hg unbundle. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: incoming (), prechangegroup (), pretxnchangegroup () + + + + + <literal role="hook" moreinfo="none">commit</literal>—after a new + changeset is created + + This hook is run after a new changeset has been created. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the newly committed changeset. + + + parent1: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + + + parent2: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + + + + See also: precommit (), pretxncommit () + + + + + <literal role="hook" moreinfo="none">incoming</literal>—after one + remote changeset is added + + This hook is run after a pre-existing changeset has been + added to the repository, for example via a hg push. If a group of changesets + was added in a single operation, this hook is called once for + each added changeset. + + + You can use this hook for the same purposes as + the changegroup hook (); it's simply more + convenient sometimes to run a hook once per group of + changesets, while other times it's handier once per changeset. + + + Parameters to this hook: + + + node: A changeset ID. The + ID of the newly added changeset. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup () prechangegroup (), pretxnchangegroup () + + + + + <literal role="hook" moreinfo="none">outgoing</literal>—after + changesets are propagated + + This hook is run after a group of changesets has been + propagated out of this repository, for example by a hg push or hg + bundle command. + + + One possible use for this hook is to notify administrators + that changes have been pulled. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset of the group that was + sent. + + + source: A string. The + source of the of the operation (see ). If a remote + client pulled changes from this repository, + source will be + serve. If the client that obtained + changes from this repository was local, + source will be + bundle, pull, or + push, depending on the operation the + client performed. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: preoutgoing () + + + + + <literal role="hook" moreinfo="none">prechangegroup</literal>—before starting + to add remote changesets + + This controlling hook is run before Mercurial begins to + add a group of changesets from another repository. + + + This hook does not have any information about the + changesets to be added, because it is run before transmission + of those changesets is allowed to begin. If this hook fails, + the changesets will not be transmitted. + + + One use for this hook is to prevent external changes from + being added to a repository. For example, you could use this + to freeze a server-hosted branch temporarily or + permanently so that users cannot push to it, while still + allowing a local administrator to modify the repository. + + + Parameters to this hook: + + + source: A string. The + source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup (), incoming (), pretxnchangegroup () + + + + + <literal role="hook" moreinfo="none">precommit</literal>—before + starting to commit a changeset + + This hook is run before Mercurial begins to commit a new + changeset. It is run before Mercurial has any of the metadata + for the commit, such as the files to be committed, the commit + message, or the commit date. + + + One use for this hook is to disable the ability to commit + new changesets, while still allowing incoming changesets. + Another is to run a build or test, and only allow the commit + to begin if the build or test succeeds. + + + Parameters to this hook: + + + parent1: A changeset ID. + The changeset ID of the first parent of the working + directory. + + + parent2: A changeset ID. + The changeset ID of the second parent of the working + directory. + + + If the commit proceeds, the parents of the working + directory will become the parents of the new changeset. + + + See also: commit + (), pretxncommit () + + + + + <literal role="hook" moreinfo="none">preoutgoing</literal>—before + starting to propagate changesets + + This hook is invoked before Mercurial knows the identities + of the changesets to be transmitted. + + + One use for this hook is to prevent changes from being + transmitted to another repository. + + + Parameters to this hook: + + + source: A + string. The source of the operation that is attempting to + obtain changes from this repository (see ). See the documentation + for the source parameter to the + outgoing hook, in + , for possible values + of this parameter. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: outgoing () + + + + + <literal role="hook" moreinfo="none">pretag</literal>—before + tagging a changeset + + This controlling hook is run before a tag is created. If + the hook succeeds, creation of the tag proceeds. If the hook + fails, the tag is not created. + + + Parameters to this hook: + + + local: A boolean. Whether + the tag is local to this repository instance (i.e. stored + in .hg/localtags) or + managed by Mercurial (stored in .hgtags). + + + node: A changeset ID. The + ID of the changeset to be tagged. + + + tag: A string. The name of + the tag to be created. + + + + If the tag to be created is + revision-controlled, the precommit and pretxncommit hooks ( and ) will also be run. + + + See also: tag + () + + + + + <literal role="hook" moreinfo="none">pretxnchangegroup</literal>—before + completing addition of remote changesets + + This controlling hook is run before a + transaction—that manages the addition of a group of new + changesets from outside the repository—completes. If + the hook succeeds, the transaction completes, and all of the + changesets become permanent within this repository. If the + hook fails, the transaction is rolled back, and the data for + the changesets is erased. + + + This hook can access the metadata associated with the + almost-added changesets, but it should not do anything + permanent with this data. It must also not modify the working + directory. + + + While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-added changesets as if they are permanent. This may + lead to race conditions if you do not take steps to avoid + them. + + + This hook can be used to automatically vet a group of + changesets. If the hook fails, all of the changesets are + rejected when the transaction rolls back. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the first changeset in the group that was + added. All changesets between this and + tip, + inclusive, were added by a single hg pull, hg push or hg unbundle. + + + source: A + string. The source of these changes. See for details. + + + url: A URL. The + location of the remote repository, if known. See for more information. + + + + See also: changegroup (), incoming (), prechangegroup () + + + + + <literal role="hook" moreinfo="none">pretxncommit</literal>—before + completing commit of new changeset + + This controlling hook is run before a + transaction—that manages a new commit—completes. + If the hook succeeds, the transaction completes and the + changeset becomes permanent within this repository. If the + hook fails, the transaction is rolled back, and the commit + data is erased. + + + This hook can access the metadata associated with the + almost-new changeset, but it should not do anything permanent + with this data. It must also not modify the working + directory. + + + While this hook is running, if other Mercurial processes + access this repository, they will be able to see the + almost-new changeset as if it is permanent. This may lead to + race conditions if you do not take steps to avoid them. + + + Parameters to this hook: + + + node: A changeset ID. The + changeset ID of the newly committed changeset. + + + parent1: A changeset ID. + The changeset ID of the first parent of the newly + committed changeset. + + + parent2: A changeset ID. + The changeset ID of the second parent of the newly + committed changeset. + + + + See also: precommit () + + + + + <literal role="hook" moreinfo="none">preupdate</literal>—before + updating or merging working directory + + This controlling hook is run before an update + or merge of the working directory begins. It is run only if + Mercurial's normal pre-update checks determine that the update + or merge can proceed. If the hook succeeds, the update or + merge may proceed; if it fails, the update or merge does not + start. + + + Parameters to this hook: + + + parent1: A + changeset ID. The ID of the parent that the working + directory is to be updated to. If the working directory + is being merged, it will not change this parent. + + + parent2: A + changeset ID. Only set if the working directory is being + merged. The ID of the revision that the working directory + is being merged with. + + + + See also: update + () + + + + <literal role="hook" moreinfo="none">tag</literal>—after tagging a + changeset + + This hook is run after a tag has been created. + + + Parameters to this hook: + + + local: A boolean. Whether + the new tag is local to this repository instance (i.e. + stored in .hg/localtags) or managed by + Mercurial (stored in .hgtags). + + + node: A changeset ID. The + ID of the changeset that was tagged. + + + tag: A string. The name of + the tag that was created. + + + + If the created tag is revision-controlled, the commit hook (section ) is run before this hook. + + + See also: pretag + () + + + + + <literal role="hook" moreinfo="none">update</literal>—after + updating or merging working directory + + This hook is run after an update or merge of the working + directory completes. Since a merge can fail (if the external + hgmerge command fails to resolve conflicts + in a file), this hook communicates whether the update or merge + completed cleanly. + + + + error: A boolean. + Indicates whether the update or merge completed + successfully. + + + parent1: A changeset ID. + The ID of the parent that the working directory was + updated to. If the working directory was merged, it will + not have changed this parent. + + + parent2: A changeset ID. + Only set if the working directory was merged. The ID of + the revision that the working directory was merged with. + + + + See also: preupdate + () + + + + + + + + + + + + + + Customizing the output of Mercurial + + Mercurial provides a powerful mechanism to let you control how + it displays information. The mechanism is based on templates. + You can use templates to generate specific output for a single + command, or to customize the entire appearance of the built-in web + interface. + + + Using precanned output styles + + Packaged with Mercurial are some output styles that you can + use immediately. A style is simply a precanned template that + someone wrote and installed somewhere that Mercurial can + find. + + Before we take a look at Mercurial's bundled styles, let's + review its normal output. + + +$ hg log -r1 +changeset: 1:e3d2468ca47c +tag: mytag +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:17 2009 +0000 +summary: added line to end of <<hello>> file. + + + + + + This is somewhat informative, but it takes up a lot of + space—five lines of output per changeset. The + compact style reduces this to three lines, + presented in a sparse manner. + + +$ hg log --style compact +3[tip] d3cc7424d32c 2009-08-16 14:05 +0000 bos + Added tag v0.1 for changeset a5dd5392119b + +2[v0.1] a5dd5392119b 2009-08-16 14:05 +0000 bos + Added tag mytag for changeset e3d2468ca47c + +1[mytag] e3d2468ca47c 2009-08-16 14:05 +0000 bos + added line to end of <<hello>> file. + +0 1cf727e9fc61 2009-08-16 14:05 +0000 bos + added hello + + + + + + The changelog style hints at the + expressive power of Mercurial's templating engine. This style + attempts to follow the GNU Project's changelog + guidelinesweb:changelog. + + +$ hg log --style changelog +2009-08-16 Bryan O'Sullivan <bos@serpentine.com> + + * .hgtags: + Added tag v0.1 for changeset a5dd5392119b + [d3cc7424d32c] [tip] + + * .hgtags: + Added tag mytag for changeset e3d2468ca47c + [a5dd5392119b] [v0.1] + + * goodbye, hello: + added line to end of <<hello>> file. + + in addition, added a file with the helpful name (at least i hope + that some might consider it so) of goodbye. + [e3d2468ca47c] [mytag] + + * hello: + added hello + [1cf727e9fc61] + + + + + + You will not be shocked to learn that Mercurial's default + output style is named default. + + + Setting a default style + + You can modify the output style that Mercurial will use + for every command by editing your ~/.hgrc file, naming the style + you would prefer to use. + + [ui] +style = compact + + If you write a style of your own, you can use it by either + providing the path to your style file, or copying your style + file into a location where Mercurial can find it (typically + the templates subdirectory of your + Mercurial install directory). + + + + + Commands that support styles and templates + + All of Mercurial's + log-like commands let you use + styles and templates: hg + incoming, hg log, + hg outgoing, and hg tip. + + As I write this manual, these are so far the only commands + that support styles and templates. Since these are the most + important commands that need customizable output, there has been + little pressure from the Mercurial user community to add style + and template support to other commands. + + + + The basics of templating + + At its simplest, a Mercurial template is a piece of text. + Some of the text never changes, while other parts are + expanded, or replaced with new text, when + necessary. + + Before we continue, let's look again at a simple example of + Mercurial's normal output. + + +$ hg log -r1 +changeset: 1:e3d2468ca47c +tag: mytag +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:17 2009 +0000 +summary: added line to end of <<hello>> file. + + + + + + Now, let's run the same command, but using a template to + change its output. + + +$ hg log -r1 --template 'i saw a changeset\n' +i saw a changeset + + + + + The example above illustrates the simplest possible + template; it's just a piece of static text, printed once for + each changeset. The option to the hg log command tells Mercurial to use + the given text as the template when printing each + changeset. + + Notice that the template string above ends with the text + \n. This is an + escape sequence, telling Mercurial to print + a newline at the end of each template item. If you omit this + newline, Mercurial will run each piece of output together. See + for more details + of escape sequences. + + A template that prints a fixed string of text all the time + isn't very useful; let's try something a bit more + complex. + + +$ hg log --template 'i saw a changeset: {desc}\n' +i saw a changeset: Added tag v0.1 for changeset a5dd5392119b +i saw a changeset: Added tag mytag for changeset e3d2468ca47c +i saw a changeset: added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye. +i saw a changeset: added hello + + + + + As you can see, the string + {desc} in the template has + been replaced in the output with the description of each + changeset. Every time Mercurial finds text enclosed in curly + braces ({ and + }), it will try to replace the + braces and text with the expansion of whatever is inside. To + print a literal curly brace, you must escape it, as described in + . + + + + Common template keywords + + You can start writing simple templates immediately using the + keywords below. + + + author: String. The + unmodified author of the changeset. + + branches: String. The + name of the branch on which the changeset was committed. + Will be empty if the branch name was + default. + + date: + Date information. The date when the changeset was + committed. This is not human-readable; + you must pass it through a filter that will render it + appropriately. See for more information + on filters. The date is expressed as a pair of numbers. The + first number is a Unix UTC timestamp (seconds since January + 1, 1970); the second is the offset of the committer's + timezone from UTC, in seconds. + + desc: + String. The text of the changeset description. + + files: List of strings. + All files modified, added, or removed by this + changeset. + + file_adds: List of + strings. Files added by this changeset. + + file_dels: List of + strings. Files removed by this changeset. + + node: + String. The changeset identification hash, as a + 40-character hexadecimal string. + + parents: List of + strings. The parents of the changeset. + + rev: + Integer. The repository-local changeset revision + number. + + tags: + List of strings. Any tags associated with the + changeset. + + + + A few simple experiments will show us what to expect when we + use these keywords; you can see the results below. + + +$ hg log -r1 --template 'author: {author}\n' +author: Bryan O'Sullivan <bos@serpentine.com> +$ hg log -r1 --template 'desc:\n{desc}\n' +desc: +added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope that some might consider it so) of goodbye. +$ hg log -r1 --template 'files: {files}\n' +files: goodbye hello +$ hg log -r1 --template 'file_adds: {file_adds}\n' +file_adds: goodbye +$ hg log -r1 --template 'file_dels: {file_dels}\n' +file_dels: +$ hg log -r1 --template 'node: {node}\n' +node: e3d2468ca47c10bdfbbb41b367a0c84509862197 +$ hg log -r1 --template 'parents: {parents}\n' +parents: +$ hg log -r1 --template 'rev: {rev}\n' +rev: 1 +$ hg log -r1 --template 'tags: {tags}\n' +tags: mytag + + + + + As we noted above, the date keyword does not produce + human-readable output, so we must treat it specially. This + involves using a filter, about which more + in . + + +$ hg log -r1 --template 'date: {date}\n' +date: 1250431517.00 +$ hg log -r1 --template 'date: {date|isodate}\n' +date: 2009-08-16 14:05 +0000 + + + + + + + Escape sequences + + Mercurial's templating engine recognises the most commonly + used escape sequences in strings. When it sees a backslash + (\) character, it looks at the + following character and substitutes the two characters with a + single replacement, as described below. + + + \: + Backslash, \, ASCII + 134. + + \n: Newline, + ASCII 12. + + \r: Carriage + return, ASCII 15. + + \t: Tab, ASCII + 11. + + \v: Vertical + tab, ASCII 13. + + \{: Open curly + brace, {, ASCII + 173. + + \}: Close curly + brace, }, ASCII + 175. + + + As indicated above, if you want the expansion of a template + to contain a literal \, + {, or + { character, you must escape + it. + + + + Filtering keywords to change their results + + Some of the results of template expansion are not + immediately easy to use. Mercurial lets you specify an optional + chain of filters to modify the result of + expanding a keyword. You have already seen a common filter, + isodate, in + action above, to make a date readable. + + Below is a list of the most commonly used filters that + Mercurial supports. While some filters can be applied to any + text, others can only be used in specific circumstances. The + name of each filter is followed first by an indication of where + it can be used, then a description of its effect. + + + addbreaks: Any text. Add + an XHTML <br/> tag + before the end of every line except the last. For example, + foo\nbar becomes + foo<br/>\nbar. + + age: date keyword. Render + the age of the date, relative to the current time. Yields a + string like 10 + minutes. + + basename: Any text, but + most useful for the files keyword and its + relatives. Treat the text as a path, and return the + basename. For example, + foo/bar/baz becomes + baz. + + date: date keyword. Render a + date in a similar format to the Unix date command, but with + timezone included. Yields a string like Mon + Sep 04 15:13:13 2006 -0700. + + domain: Any text, + but most useful for the author keyword. Finds + the first string that looks like an email address, and + extract just the domain component. For example, + Bryan O'Sullivan + <bos@serpentine.com> becomes + serpentine.com. + + email: Any text, + but most useful for the author keyword. Extract + the first string that looks like an email address. For + example, Bryan O'Sullivan + <bos@serpentine.com> becomes + bos@serpentine.com. + + escape: Any text. + Replace the special XML/XHTML characters + &, + < and + > with XML + entities. + + fill68: Any text. Wrap + the text to fit in 68 columns. This is useful before you + pass text through the tabindent filter, and + still want it to fit in an 80-column fixed-font + window. + + fill76: Any text. Wrap + the text to fit in 76 columns. + + firstline: Any text. + Yield the first line of text, without any trailing + newlines. + + hgdate: date keyword. Render + the date as a pair of readable numbers. Yields a string + like 1157407993 + 25200. + + isodate: date keyword. Render + the date as a text string in ISO 8601 format. Yields a + string like 2006-09-04 15:13:13 + -0700. + + obfuscate: Any text, but + most useful for the author keyword. Yield + the input text rendered as a sequence of XML entities. This + helps to defeat some particularly stupid screen-scraping + email harvesting spambots. + + person: Any text, + but most useful for the author keyword. Yield + the text before an email address. For example, + Bryan O'Sullivan + <bos@serpentine.com> becomes + Bryan O'Sullivan. + + rfc822date: + date keyword. + Render a date using the same format used in email headers. + Yields a string like Mon, 04 Sep 2006 + 15:13:13 -0700. + + short: Changeset + hash. Yield the short form of a changeset hash, i.e. a + 12-character hexadecimal string. + + shortdate: date keyword. Render + the year, month, and day of the date. Yields a string like + 2006-09-04. + + strip: + Any text. Strip all leading and trailing whitespace from + the string. + + tabindent: Any text. + Yield the text, with every line except the first starting + with a tab character. + + urlescape: Any text. + Escape all characters that are considered + special by URL parsers. For example, + foo bar becomes + foo%20bar. + + user: Any text, + but most useful for the author keyword. Return + the user portion of an email address. For + example, Bryan O'Sullivan + <bos@serpentine.com> becomes + bos. + + + + +$ hg log -r1 --template '{author}\n' +Bryan O'Sullivan <bos@serpentine.com> +$ hg log -r1 --template '{author|domain}\n' +serpentine.com +$ hg log -r1 --template '{author|email}\n' +bos@serpentine.com +$ hg log -r1 --template '{author|obfuscate}\n' | cut -c-76 +&#66;&#114;&#121;&#97;&#110;&#32;&#79;&#39;&#83;&#117;&#108;&#108;&#105;&#11 +$ hg log -r1 --template '{author|person}\n' +Bryan O'Sullivan +$ hg log -r1 --template '{author|user}\n' +bos +$ hg log -r1 --template 'looks almost right, but actually garbage: {date}\n' +looks almost right, but actually garbage: 1250431517.00 +$ hg log -r1 --template '{date|age}\n' +3 seconds +$ hg log -r1 --template '{date|date}\n' +Sun Aug 16 14:05:17 2009 +0000 +$ hg log -r1 --template '{date|hgdate}\n' +1250431517 0 +$ hg log -r1 --template '{date|isodate}\n' +2009-08-16 14:05 +0000 +$ hg log -r1 --template '{date|rfc822date}\n' +Sun, 16 Aug 2009 14:05:17 +0000 +$ hg log -r1 --template '{date|shortdate}\n' +2009-08-16 +$ hg log -r1 --template '{desc}\n' | cut -c-76 +added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope that some m +$ hg log -r1 --template '{desc|addbreaks}\n' | cut -c-76 +added line to end of <<hello>> file.<br/> +<br/> +in addition, added a file with the helpful name (at least i hope that some m +$ hg log -r1 --template '{desc|escape}\n' | cut -c-76 +added line to end of &lt;&lt;hello&gt;&gt; file. + +in addition, added a file with the helpful name (at least i hope that some m +$ hg log -r1 --template '{desc|fill68}\n' +added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope +that some might consider it so) of goodbye. +$ hg log -r1 --template '{desc|fill76}\n' +added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope that some +might consider it so) of goodbye. +$ hg log -r1 --template '{desc|firstline}\n' +added line to end of <<hello>> file. +$ hg log -r1 --template '{desc|strip}\n' | cut -c-76 +added line to end of <<hello>> file. + +in addition, added a file with the helpful name (at least i hope that some m +$ hg log -r1 --template '{desc|tabindent}\n' | expand | cut -c-76 +added line to end of <<hello>> file. + + in addition, added a file with the helpful name (at least i hope tha +$ hg log -r1 --template '{node}\n' +e3d2468ca47c10bdfbbb41b367a0c84509862197 +$ hg log -r1 --template '{node|short}\n' +e3d2468ca47c + + + + + + If you try to apply a filter to a piece of data that it + cannot process, Mercurial will fail and print a Python + exception. For example, trying to run the output of the + desc keyword into + the isodate + filter is not a good idea. + + + + Combining filters + + It is easy to combine filters to yield output in the form + you would like. The following chain of filters tidies up a + description, then makes sure that it fits cleanly into 68 + columns, then indents it by a further 8 characters (at least + on Unix-like systems, where a tab is conventionally 8 + characters wide). + + +$ hg log -r1 --template 'description:\n\t{desc|strip|fill68|tabindent}\n' +description: + added line to end of <<hello>> file. + + in addition, added a file with the helpful name (at least i hope + that some might consider it so) of goodbye. + + + + + Note the use of \t (a + tab character) in the template to force the first line to be + indented; this is necessary since tabindent indents all + lines except the first. + + Keep in mind that the order of filters in a chain is + significant. The first filter is applied to the result of the + keyword; the second to the result of the first filter; and so + on. For example, using fill68|tabindent + gives very different results from + tabindent|fill68. + + + + + From templates to styles + + A command line template provides a quick and simple way to + format some output. Templates can become verbose, though, and + it's useful to be able to give a template a name. A style file + is a template with a name, stored in a file. + + More than that, using a style file unlocks the power of + Mercurial's templating engine in ways that are not possible + using the command line option. + + + The simplest of style files + + Our simple style file contains just one line: + + +$ echo 'changeset = "rev: {rev}\n"' > rev +$ hg log -l1 --style ./rev +rev: 3 + + + + + This tells Mercurial, if you're printing a + changeset, use the text on the right as the + template. + + + + Style file syntax + + The syntax rules for a style file are simple. + + + The file is processed one line at a + time. + + Leading and trailing white space are + ignored. + + Empty lines are skipped. + + If a line starts with either of the characters + # or + ;, the entire line is + treated as a comment, and skipped as if empty. + + A line starts with a keyword. This must start + with an alphabetic character or underscore, and can + subsequently contain any alphanumeric character or + underscore. (In regexp notation, a keyword must match + [A-Za-z_][A-Za-z0-9_]*.) + + The next element must be an + = character, which can + be preceded or followed by an arbitrary amount of white + space. + + If the rest of the line starts and ends with + matching quote characters (either single or double quote), + it is treated as a template body. + + If the rest of the line does + not start with a quote character, it is + treated as the name of a file; the contents of this file + will be read and used as a template body. + + + + + + Style files by example + + To illustrate how to write a style file, we will construct a + few by example. Rather than provide a complete style file and + walk through it, we'll mirror the usual process of developing a + style file by starting with something very simple, and walking + through a series of successively more complete examples. + + + Identifying mistakes in style files + + If Mercurial encounters a problem in a style file you are + working on, it prints a terse error message that, once you + figure out what it means, is actually quite useful. + + +$ cat broken.style +changeset = + + + + + Notice that broken.style attempts to + define a changeset keyword, but forgets to + give any content for it. When instructed to use this style + file, Mercurial promptly complains. + + +$ hg log -r1 --style broken.style +abort: broken.style:1: parse error + + + + + This error message looks intimidating, but it is not too + hard to follow. + + + The first component is simply Mercurial's way + of saying I am giving up. + ___abort___: broken.style:1: parse error + + Next comes the name of the style file that + contains the error. + abort: ___broken.style___:1: parse error + + Following the file name is the line number + where the error was encountered. + abort: broken.style:___1___: parse error + + Finally, a description of what went + wrong. + abort: broken.style:1: ___parse error___ + + The description of the problem is not always + clear (as in this case), but even when it is cryptic, it + is almost always trivial to visually inspect the offending + line in the style file and see what is wrong. + + + + + + Uniquely identifying a repository + + If you would like to be able to identify a Mercurial + repository fairly uniquely using a short string + as an identifier, you can use the first revision in the + repository. + + +$ hg log -r0 --template '{node}' +02b4f9d8a52a6da645e20fa7df0accc8aa33b650 + + + + This is likely to be unique, and so it is + useful in many cases. There are a few caveats. + + It will not work in a completely empty + repository, because such a repository does not have a + revision zero. + + Neither will it work in the (extremely rare) + case where a repository is a merge of two or more formerly + independent repositories, and you still have those + repositories around. + + Here are some uses to which you could put this + identifier: + + As a key into a table for a database that + manages repositories on a server. + + As half of a {repository + ID, revision ID} tuple. + Save this information away when you run an automated build + or other activity, so that you can replay + the build later if necessary. + + + + + + Listing files on multiple lines + + Suppose we want to list the files changed by a changeset, + one per line, with a little indentation before each file + name. + + +$ cat > multiline << EOF +> changeset = "Changed in {node|short}:\n{files}" +> file = " {file}\n" +> EOF +$ hg log --style multiline +Changed in badb58085712: + .bashrc + .hgrc + test.c + + + + + + + Mimicking Subversion's output + + Let's try to emulate the default output format used by + another revision control tool, Subversion. + + +$ svn log -r9653 +------------------------------------------------------------------------ +r9653 | sean.hefty | 2006-09-27 14:39:55 -0700 (Wed, 27 Sep 2006) | 5 lines + +On reporting a route error, also include the status for the error, +rather than indicating a status of 0 when an error has occurred. + +Signed-off-by: Sean Hefty <sean.hefty@intel.com> + +------------------------------------------------------------------------ + + + + + Since Subversion's output style is fairly simple, it is + easy to copy-and-paste a hunk of its output into a file, and + replace the text produced above by Subversion with the + template values we'd like to see expanded. + + +$ cat svn.template +r{rev} | {author|user} | {date|isodate} ({date|rfc822date}) + +{desc|strip|fill76} + +------------------------------------------------------------------------ + + + + + There are a few small ways in which this template deviates + from the output produced by Subversion. + + Subversion prints a readable + date (the Wed, 27 Sep 2006 in the + example output above) in parentheses. Mercurial's + templating engine does not provide a way to display a date + in this format without also printing the time and time + zone. + + We emulate Subversion's printing of + separator lines full of + - characters by ending + the template with such a line. We use the templating + engine's header + keyword to print a separator line as the first line of + output (see below), thus achieving similar output to + Subversion. + + Subversion's output includes a count in the + header of the number of lines in the commit message. We + cannot replicate this in Mercurial; the templating engine + does not currently provide a filter that counts the number + of lines the template generates. + + It took me no more than a minute or two of work to replace + literal text from an example of Subversion's output with some + keywords and filters to give the template above. The style + file simply refers to the template. + + +$ cat svn.style +header = '------------------------------------------------------------------------\n\n' +changeset = svn.template + + + + + We could have included the text of the template file + directly in the style file by enclosing it in quotes and + replacing the newlines with + \n sequences, but it would + have made the style file too difficult to read. Readability + is a good guide when you're trying to decide whether some text + belongs in a style file, or in a template file that the style + file points to. If the style file will look too big or + cluttered if you insert a literal piece of text, drop it into + a template instead. + + + + + + + + + + + + Managing change with Mercurial Queues + + + The patch management problem + + Here is a common scenario: you need to install a software + package from source, but you find a bug that you must fix in the + source before you can start using the package. You make your + changes, forget about the package for a while, and a few months + later you need to upgrade to a newer version of the package. If + the newer version of the package still has the bug, you must + extract your fix from the older source tree and apply it against + the newer version. This is a tedious task, and it's easy to + make mistakes. + + This is a simple case of the patch management + problem. You have an upstream source tree that + you can't change; you need to make some local changes on top of + the upstream tree; and you'd like to be able to keep those + changes separate, so that you can apply them to newer versions + of the upstream source. + + The patch management problem arises in many situations. + Probably the most visible is that a user of an open source + software project will contribute a bug fix or new feature to the + project's maintainers in the form of a patch. + + Distributors of operating systems that include open source + software often need to make changes to the packages they + distribute so that they will build properly in their + environments. + + When you have few changes to maintain, it is easy to manage + a single patch using the standard diff and + patch programs (see for a discussion of these + tools). Once the number of changes grows, it starts to make + sense to maintain patches as discrete chunks of + work, so that for example a single patch will contain + only one bug fix (the patch might modify several files, but it's + doing only one thing), and you may have a number + of such patches for different bugs you need fixed and local + changes you require. In this situation, if you submit a bug fix + patch to the upstream maintainers of a package and they include + your fix in a subsequent release, you can simply drop that + single patch when you're updating to the newer release. + + Maintaining a single patch against an upstream tree is a + little tedious and error-prone, but not difficult. However, the + complexity of the problem grows rapidly as the number of patches + you have to maintain increases. With more than a tiny number of + patches in hand, understanding which ones you have applied and + maintaining them moves from messy to overwhelming. + + Fortunately, Mercurial includes a powerful extension, + Mercurial Queues (or simply MQ), that massively + simplifies the patch management problem. + + + + The prehistory of Mercurial Queues + + During the late 1990s, several Linux kernel developers + started to maintain patch series that modified + the behavior of the Linux kernel. Some of these series were + focused on stability, some on feature coverage, and others were + more speculative. + + The sizes of these patch series grew rapidly. In 2002, + Andrew Morton published some shell scripts he had been using to + automate the task of managing his patch queues. Andrew was + successfully using these scripts to manage hundreds (sometimes + thousands) of patches on top of the Linux kernel. + + + A patchwork quilt + + In early 2003, Andreas Gruenbacher and Martin Quinson + borrowed the approach of Andrew's scripts and published a tool + called patchwork quilt + web:quilt, or simply quilt + (see gruenbacher:2005 for a paper + describing it). Because quilt substantially automated patch + management, it rapidly gained a large following among open + source software developers. + + Quilt manages a stack of patches on + top of a directory tree. To begin, you tell quilt to manage a + directory tree, and tell it which files you want to manage; it + stores away the names and contents of those files. To fix a + bug, you create a new patch (using a single command), edit the + files you need to fix, then refresh the + patch. + + The refresh step causes quilt to scan the directory tree; + it updates the patch with all of the changes you have made. + You can create another patch on top of the first, which will + track the changes required to modify the tree from tree + with one patch applied to tree with two + patches applied. + + You can change which patches are + applied to the tree. If you pop a patch, the + changes made by that patch will vanish from the directory + tree. Quilt remembers which patches you have popped, though, + so you can push a popped patch again, and the + directory tree will be restored to contain the modifications + in the patch. Most importantly, you can run the + refresh command at any time, and the topmost + applied patch will be updated. This means that you can, at + any time, change both which patches are applied and what + modifications those patches make. + + Quilt knows nothing about revision control tools, so it + works equally well on top of an unpacked tarball or a + Subversion working copy. + + + + From patchwork quilt to Mercurial Queues + + In mid-2005, Chris Mason took the features of quilt and + wrote an extension that he called Mercurial Queues, which + added quilt-like behavior to Mercurial. + + The key difference between quilt and MQ is that quilt + knows nothing about revision control systems, while MQ is + integrated into Mercurial. Each patch + that you push is represented as a Mercurial changeset. Pop a + patch, and the changeset goes away. + + Because quilt does not care about revision control tools, + it is still a tremendously useful piece of software to know + about for situations where you cannot use Mercurial and + MQ. + + + + + The huge advantage of MQ + + I cannot overstate the value that MQ offers through the + unification of patches and revision control. + + A major reason that patches have persisted in the free + software and open source world—in spite of the + availability of increasingly capable revision control tools over + the years—is the agility they + offer. + + Traditional revision control tools make a permanent, + irreversible record of everything that you do. While this has + great value, it's also somewhat stifling. If you want to + perform a wild-eyed experiment, you have to be careful in how + you go about it, or you risk leaving unneeded—or worse, + misleading or destabilising—traces of your missteps and + errors in the permanent revision record. + + By contrast, MQ's marriage of distributed revision control + with patches makes it much easier to isolate your work. Your + patches live on top of normal revision history, and you can make + them disappear or reappear at will. If you don't like a patch, + you can drop it. If a patch isn't quite as you want it to be, + simply fix it—as many times as you need to, until you + have refined it into the form you desire. + + As an example, the integration of patches with revision + control makes understanding patches and debugging their + effects—and their interplay with the code they're based + on—enormously easier. Since every + applied patch has an associated changeset, you can give hg log a file name to see which + changesets and patches affected the file. You can use the + hg bisect command to + binary-search through all changesets and applied patches to see + where a bug got introduced or fixed. You can use the hg annotate command to see which + changeset or patch modified a particular line of a source file. + And so on. + + + + Understanding patches + + Because MQ doesn't hide its patch-oriented nature, it is + helpful to understand what patches are, and a little about the + tools that work with them. + + The traditional Unix diff command + compares two files, and prints a list of differences between + them. The patch command understands these + differences as modifications to make to a + file. Take a look below for a simple example of these commands + in action. + + +$ echo 'this is my original thought' > oldfile +$ echo 'i have changed my mind' > newfile +$ diff -u oldfile newfile > tiny.patch +$ cat tiny.patch +--- oldfile 2009-08-16 14:05:06.000000000 +0000 ++++ newfile 2009-08-16 14:05:06.000000000 +0000 +@@ -1 +1 @@ +-this is my original thought ++i have changed my mind +$ patch < tiny.patch +patching file oldfile +$ cat oldfile +i have changed my mind + + + + + The type of file that diff generates (and + patch takes as input) is called a + patch or a diff; there is no + difference between a patch and a diff. (We'll use the term + patch, since it's more commonly used.) + + A patch file can start with arbitrary text; the + patch command ignores this text, but MQ uses + it as the commit message when creating changesets. To find the + beginning of the patch content, patch + searches for the first line that starts with the string + diff -. + + MQ works with unified diffs + (patch can accept several other diff formats, + but MQ doesn't). A unified diff contains two kinds of header. + The file header describes the file being + modified; it contains the name of the file to modify. When + patch sees a new file header, it looks for a + file with that name to start modifying. + + After the file header comes a series of + hunks. Each hunk starts with a header; + this identifies the range of line numbers within the file that + the hunk should modify. Following the header, a hunk starts and + ends with a few (usually three) lines of text from the + unmodified file; these are called the + context for the hunk. If there's only a + small amount of context between successive hunks, + diff doesn't print a new hunk header; it just + runs the hunks together, with a few lines of context between + modifications. + + Each line of context begins with a space character. Within + the hunk, a line that begins with + - means remove this + line, while a line that begins with + + means insert this + line. For example, a line that is modified is + represented by one deletion and one insertion. + + We will return to some of the more subtle aspects of patches + later (in ), but you + should have + enough information now to use MQ. + + + + Getting started with Mercurial Queues + + Because MQ is implemented as an extension, you must + explicitly enable before you can use it. (You don't need to + download anything; MQ ships with the standard Mercurial + distribution.) To enable MQ, edit your ~/.hgrc file, and add the lines + below. + + [extensions] +hgext.mq = + + Once the extension is enabled, it will make a number of new + commands available. To verify that the extension is working, + you can use hg help to see if + the qinit command is now + available. + + +$ hg help qinit +hg qinit [-c] + +init a new queue repository + + The queue repository is unversioned by default. If -c is + specified, qinit will create a separate nested repository + for patches (qinit -c may also be run later to convert + an unversioned patch repository into a versioned one). + You can use qcommit to commit changes to this queue repository. + +options: + + -c --create-repo create queue repository + +use "hg -v help qinit" to show global options + + + + + You can use MQ with any Mercurial + repository, and its commands only operate within that + repository. To get started, simply prepare the repository using + the qinit command. + + +$ hg init mq-sandbox +$ cd mq-sandbox +$ echo 'line 1' > file1 +$ echo 'another line 1' > file2 +$ hg add file1 file2 +$ hg commit -m'first change' +$ hg qinit + + + + + This command creates an empty directory called .hg/patches, where + MQ will keep its metadata. As with many Mercurial commands, the + qinit command prints nothing + if it succeeds. + + + Creating a new patch + + To begin work on a new patch, use the qnew command. This command takes + one argument, the name of the patch to create. + + MQ will use this as the name of an actual file in the + .hg/patches directory, as you + can see below. + + +$ hg tip +changeset: 0:5d84c303994b +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:11 2009 +0000 +summary: first change + +$ hg qnew first.patch +$ hg tip +changeset: 1:ba4d7a3f2149 +tag: qtip +tag: first.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:11 2009 +0000 +summary: [mq]: first.patch + +$ ls .hg/patches +first.patch series status + + + + + Also newly present in the .hg/patches directory are two + other files, series and + status. The series file lists all of the + patches that MQ knows about for this repository, with one + patch per line. Mercurial uses the status file for internal + book-keeping; it tracks all of the patches that MQ has + applied in this repository. + + + You may sometimes want to edit the series file by hand; for + example, to change the sequence in which some patches are + applied. However, manually editing the status file is almost always a + bad idea, as it's easy to corrupt MQ's idea of what is + happening. + + + Once you have created your new patch, you can edit files + in the working directory as you usually would. All of the + normal Mercurial commands, such as hg + diff and hg + annotate, work exactly as they did before. + + + + Refreshing a patch + + When you reach a point where you want to save your work, + use the qrefresh command + to update the patch you are working on. + + +$ echo 'line 2' >> file1 +$ hg diff +diff -r ba4d7a3f2149 file1 +--- a/file1 Sun Aug 16 14:05:11 2009 +0000 ++++ b/file1 Sun Aug 16 14:05:11 2009 +0000 +@@ -1,1 +1,2 @@ + line 1 ++line 2 +$ hg qrefresh +$ hg diff +$ hg tip --style=compact --patch +1[qtip,first.patch,tip,qbase] 1aa236e17e55 2009-08-16 14:05 +0000 bos + [mq]: first.patch + +diff -r 5d84c303994b -r 1aa236e17e55 file1 +--- a/file1 Sun Aug 16 14:05:11 2009 +0000 ++++ b/file1 Sun Aug 16 14:05:11 2009 +0000 +@@ -1,1 +1,2 @@ + line 1 ++line 2 + + + + + + This command folds the changes you have made in the + working directory into your patch, and updates its + corresponding changeset to contain those changes. + + You can run qrefresh + as often as you like, so it's a good way to + checkpoint your work. Refresh your patch at an + opportune time; try an experiment; and if the experiment + doesn't work out, hg revert + your modifications back to the last time you refreshed. + + +$ echo 'line 3' >> file1 +$ hg status +M file1 +$ hg qrefresh +$ hg tip --style=compact --patch +1[qtip,first.patch,tip,qbase] ebec7ce95e11 2009-08-16 14:05 +0000 bos + [mq]: first.patch + +diff -r 5d84c303994b -r ebec7ce95e11 file1 +--- a/file1 Sun Aug 16 14:05:11 2009 +0000 ++++ b/file1 Sun Aug 16 14:05:12 2009 +0000 +@@ -1,1 +1,3 @@ + line 1 ++line 2 ++line 3 + + + + + + + + Stacking and tracking patches + + Once you have finished working on a patch, or need to work + on another, you can use the qnew command again to create a + new patch. Mercurial will apply this patch on top of your + existing patch. + + +$ hg qnew second.patch +$ hg log --style=compact --limit=2 +2[qtip,second.patch,tip] dffbc4265523 2009-08-16 14:05 +0000 bos + [mq]: second.patch + +1[first.patch,qbase] ebec7ce95e11 2009-08-16 14:05 +0000 bos + [mq]: first.patch + +$ echo 'line 4' >> file1 +$ hg qrefresh +$ hg tip --style=compact --patch +2[qtip,second.patch,tip] fdacb9b232ac 2009-08-16 14:05 +0000 bos + [mq]: second.patch + +diff -r ebec7ce95e11 -r fdacb9b232ac file1 +--- a/file1 Sun Aug 16 14:05:12 2009 +0000 ++++ b/file1 Sun Aug 16 14:05:12 2009 +0000 +@@ -1,3 +1,4 @@ + line 1 + line 2 + line 3 ++line 4 + +$ hg annotate file1 +0: line 1 +1: line 2 +1: line 3 +2: line 4 + + + + + Notice that the patch contains the changes in our prior + patch as part of its context (you can see this more clearly in + the output of hg + annotate). + + So far, with the exception of qnew and qrefresh, we've been careful to + only use regular Mercurial commands. However, MQ provides + many commands that are easier to use when you are thinking + about patches, as illustrated below. + + +$ hg qseries +first.patch +second.patch +$ hg qapplied +first.patch +second.patch + + + + + + The qseries command lists every + patch that MQ knows about in this repository, from oldest + to newest (most recently + created). + + The qapplied command lists every + patch that MQ has applied in this + repository, again from oldest to newest (most recently + applied). + + + + + Manipulating the patch stack + + The previous discussion implied that there must be a + difference between known and + applied patches, and there is. MQ can manage a + patch without it being applied in the repository. + + An applied patch has a corresponding + changeset in the repository, and the effects of the patch and + changeset are visible in the working directory. You can undo + the application of a patch using the qpop command. MQ still + knows about, or manages, a popped patch, + but the patch no longer has a corresponding changeset in the + repository, and the working directory does not contain the + changes made by the patch. illustrates + the difference between applied and tracked patches. + +
+ Applied and unapplied patches in the MQ patch + stack + + + XXX add text + +
+ + You can reapply an unapplied, or popped, patch using the + qpush command. This + creates a new changeset to correspond to the patch, and the + patch's changes once again become present in the working + directory. See below for examples of qpop and qpush in action. + + +$ hg qapplied +first.patch +second.patch +$ hg qpop +now at: first.patch +$ hg qseries +first.patch +second.patch +$ hg qapplied +first.patch +$ cat file1 +line 1 +line 2 +line 3 + + + + + Notice that once we have popped a patch or two patches, + the output of qseries + remains the same, while that of qapplied has changed. + +
+ + + Pushing and popping many patches + + While qpush and + qpop each operate on a + single patch at a time by default, you can push and pop many + patches in one go. The option to + qpush causes it to push + all unapplied patches, while the option to qpop causes it to pop all applied + patches. (For some more ways to push and pop many patches, + see below.) + + +$ hg qpush -a +applying second.patch +now at: second.patch +$ cat file1 +line 1 +line 2 +line 3 +line 4 + + + + + + + Safety checks, and overriding them + + Several MQ commands check the working directory before + they do anything, and fail if they find any modifications. + They do this to ensure that you won't lose any changes that + you have made, but not yet incorporated into a patch. The + example below illustrates this; the qnew command will not create a + new patch if there are outstanding changes, caused in this + case by the hg add of + file3. + + +$ echo 'file 3, line 1' >> file3 +$ hg qnew add-file3.patch +$ hg qnew -f add-file3.patch +abort: patch "add-file3.patch" already exists + + + + + Commands that check the working directory all take an + I know what I'm doing option, which is always + named . The exact meaning of + depends on the command. For example, + hg qnew + will incorporate any outstanding changes into the new patch it + creates, but hg qpop + will revert modifications to any files affected by the patch + that it is popping. Be sure to read the documentation for a + command's option before you use it! + + + + Working on several patches at once + + The qrefresh command + always refreshes the topmost applied + patch. This means that you can suspend work on one patch (by + refreshing it), pop or push to make a different patch the top, + and work on that patch for a + while. + + Here's an example that illustrates how you can use this + ability. Let's say you're developing a new feature as two + patches. The first is a change to the core of your software, + and the second—layered on top of the + first—changes the user interface to use the code you + just added to the core. If you notice a bug in the core while + you're working on the UI patch, it's easy to fix the core. + Simply qrefresh the UI + patch to save your in-progress changes, and qpop down to the core patch. Fix + the core bug, qrefresh the + core patch, and qpush back + to the UI patch to continue where you left off. + +
+ + + More about patches + + MQ uses the GNU patch command to apply + patches, so it's helpful to know a few more detailed aspects of + how patch works, and about patches + themselves. + + + The strip count + + If you look at the file headers in a patch, you will + notice that the pathnames usually have an extra component on + the front that isn't present in the actual path name. This is + a holdover from the way that people used to generate patches + (people still do this, but it's somewhat rare with modern + revision control tools). + + Alice would unpack a tarball, edit her files, then decide + that she wanted to create a patch. So she'd rename her + working directory, unpack the tarball again (hence the need + for the rename), and use the and options to + diff to recursively generate a patch + between the unmodified directory and the modified one. The + result would be that the name of the unmodified directory + would be at the front of the left-hand path in every file + header, and the name of the modified directory would be at the + front of the right-hand path. + + Since someone receiving a patch from the Alices of the net + would be unlikely to have unmodified and modified directories + with exactly the same names, the patch + command has a option + that indicates the number of leading path name components to + strip when trying to apply a patch. This number is called the + strip count. + + An option of -p1 means + use a strip count of one. If + patch sees a file name + foo/bar/baz in a file header, it will + strip foo and try to patch a file named + bar/baz. (Strictly speaking, the strip + count refers to the number of path + separators (and the components that go with them + ) to strip. A strip count of one will turn + foo/bar into bar, + but /foo/bar (notice the extra leading + slash) into foo/bar.) + + The standard strip count for patches is + one; almost all patches contain one leading path name + component that needs to be stripped. Mercurial's hg diff command generates path names + in this form, and the hg + import command and MQ expect patches to have a + strip count of one. + + If you receive a patch from someone that you want to add + to your patch queue, and the patch needs a strip count other + than one, you cannot just qimport the patch, because + qimport does not yet have + a -p option (see issue + 311). Your best bet is to qnew a patch of your own, then + use patch -pN to apply their patch, + followed by hg addremove to + pick up any files added or removed by the patch, followed by + hg qrefresh. This + complexity may become unnecessary; see issue + 311 for details. + + + + + Strategies for applying a patch + + When patch applies a hunk, it tries a + handful of successively less accurate strategies to try to + make the hunk apply. This falling-back technique often makes + it possible to take a patch that was generated against an old + version of a file, and apply it against a newer version of + that file. + + First, patch tries an exact match, + where the line numbers, the context, and the text to be + modified must apply exactly. If it cannot make an exact + match, it tries to find an exact match for the context, + without honouring the line numbering information. If this + succeeds, it prints a line of output saying that the hunk was + applied, but at some offset from the + original line number. + + If a context-only match fails, patch + removes the first and last lines of the context, and tries a + reduced context-only match. If the hunk + with reduced context succeeds, it prints a message saying that + it applied the hunk with a fuzz factor + (the number after the fuzz factor indicates how many lines of + context patch had to trim before the patch + applied). + + When neither of these techniques works, + patch prints a message saying that the hunk + in question was rejected. It saves rejected hunks (also + simply called rejects) to a file with the same + name, and an added .rej + extension. It also saves an unmodified copy of the file with + a .orig extension; the + copy of the file without any extensions will contain any + changes made by hunks that did apply + cleanly. If you have a patch that modifies + foo with six hunks, and one of them fails + to apply, you will have: an unmodified + foo.orig, a foo.rej + containing one hunk, and foo, containing + the changes made by the five successful hunks. + + + + Some quirks of patch representation + + There are a few useful things to know about how + patch works with files. + + This should already be obvious, but + patch cannot handle binary + files. + + Neither does it care about the executable bit; + it creates new files as readable, but not + executable. + + patch treats the removal of + a file as a diff between the file to be removed and the + empty file. So your idea of I deleted this + file looks like every line of this file + was deleted in a patch. + + It treats the addition of a file as a diff + between the empty file and the file to be added. So in a + patch, your idea of I added this file looks + like every line of this file was + added. + + It treats a renamed file as the removal of the + old name, and the addition of the new name. This means + that renamed files have a big footprint in patches. (Note + also that Mercurial does not currently try to infer when + files have been renamed or copied in a patch.) + + patch cannot represent + empty files, so you cannot use a patch to represent the + notion I added this empty file to the + tree. + + + + + Beware the fuzz + + While applying a hunk at an offset, or with a fuzz factor, + will often be completely successful, these inexact techniques + naturally leave open the possibility of corrupting the patched + file. The most common cases typically involve applying a + patch twice, or at an incorrect location in the file. If + patch or qpush ever mentions an offset or + fuzz factor, you should make sure that the modified files are + correct afterwards. + + It's often a good idea to refresh a patch that has applied + with an offset or fuzz factor; refreshing the patch generates + new context information that will make it apply cleanly. I + say often, not always, because + sometimes refreshing a patch will make it fail to apply + against a different revision of the underlying files. In some + cases, such as when you're maintaining a patch that must sit + on top of multiple versions of a source tree, it's acceptable + to have a patch apply with some fuzz, provided you've verified + the results of the patching process in such cases. + + + + Handling rejection + + If qpush fails to + apply a patch, it will print an error message and exit. If it + has left .rej files + behind, it is usually best to fix up the rejected hunks before + you push more patches or do any further work. + + If your patch used to apply cleanly, + and no longer does because you've changed the underlying code + that your patches are based on, Mercurial Queues can help; see + for details. + + Unfortunately, there aren't any great techniques for + dealing with rejected hunks. Most often, you'll need to view + the .rej file and edit the + target file, applying the rejected hunks by hand. + + A Linux kernel hacker, Chris Mason (the author + of Mercurial Queues), wrote a tool called + mpatch (http://oss.oracle.com/~mason/mpatch/), + which takes a simple approach to automating the application of + hunks rejected by patch. The + mpatch command can help with four common + reasons that a hunk may be rejected: + + + The context in the middle of a hunk has + changed. + + A hunk is missing some context at the + beginning or end. + + A large hunk might apply better—either + entirely or in part—if it was broken up into + smaller hunks. + + A hunk removes lines with slightly different + content than those currently present in the file. + + + If you use mpatch, you + should be doubly careful to check your results when you're + done. In fact, mpatch enforces this method + of double-checking the tool's output, by automatically + dropping you into a merge program when it has done its job, so + that you can verify its work and finish off any remaining + merges. + + + + + More on patch management + + As you grow familiar with MQ, you will find yourself wanting + to perform other kinds of patch management operations. + + + Deleting unwanted patches + + If you want to get rid of a patch, use the hg qdelete command to delete the + patch file and remove its entry from the patch series. If you + try to delete a patch that is still applied, hg qdelete will refuse. + + +$ hg init myrepo +$ cd myrepo +$ hg qinit +$ hg qnew bad.patch +$ echo a > a +$ hg add a +$ hg qrefresh +$ hg qdelete bad.patch +abort: cannot delete applied patch bad.patch +$ hg qpop +patch queue now empty +$ hg qdelete bad.patch + + + + + + + Converting to and from permanent revisions + + Once you're done working on a patch and want to + turn it into a permanent changeset, use the hg qfinish command. Pass a revision + to the command to identify the patch that you want to turn into + a regular changeset; this patch must already be applied. + + +$ hg qnew good.patch +$ echo a > a +$ hg add a +$ hg qrefresh -m 'Good change' +$ hg qfinish tip +$ hg qapplied +$ hg tip --style=compact +0[tip] 32fc5ce6b092 2009-08-16 14:04 +0000 bos + Good change + + + + + + The hg qfinish command + accepts an or + option, which turns all applied patches into regular + changesets. + + It is also possible to turn an existing changeset into a + patch, by passing the option to hg qimport. + + +$ hg qimport -r tip +$ hg qapplied +0.diff + + + + + Note that it only makes sense to convert a changeset into + a patch if you have not propagated that changeset into any + other repositories. The imported changeset's ID will change + every time you refresh the patch, which will make Mercurial + treat it as unrelated to the original changeset if you have + pushed it somewhere else. + + + + + Getting the best performance out of MQ + + MQ is very efficient at handling a large number + of patches. I ran some performance experiments in mid-2006 for a + talk that I gave at the 2006 EuroPython conference (on modern + hardware, you should expect better performance than you'll see + below). I used as my data set the Linux 2.6.17-mm1 patch + series, which consists of 1,738 patches. I applied these on top + of a Linux kernel repository containing all 27,472 revisions + between Linux 2.6.12-rc2 and Linux 2.6.17. + + On my old, slow laptop, I was able to hg qpush all + 1,738 patches in 3.5 minutes, and hg qpop + + them all in 30 seconds. (On a newer laptop, the time to push + all patches dropped to two minutes.) I could qrefresh one of the biggest patches + (which made 22,779 lines of changes to 287 files) in 6.6 + seconds. + + Clearly, MQ is well suited to working in large trees, but + there are a few tricks you can use to get the best performance + of it. + + First of all, try to batch operations + together. Every time you run qpush or qpop, these commands scan the + working directory once to make sure you haven't made some + changes and then forgotten to run qrefresh. On a small tree, the + time that this scan takes is unnoticeable. However, on a + medium-sized tree (containing tens of thousands of files), it + can take a second or more. + + The qpush and qpop commands allow you to push and + pop multiple patches at a time. You can identify the + destination patch that you want to end up at. + When you qpush with a + destination specified, it will push patches until that patch is + at the top of the applied stack. When you qpop to a destination, MQ will pop + patches until the destination patch is at the top. + + You can identify a destination patch using either the name + of the patch, or by number. If you use numeric addressing, + patches are counted from zero; this means that the first patch + is zero, the second is one, and so on. + + + + Updating your patches when the underlying code + changes + + It's common to have a stack of patches on top of an + underlying repository that you don't modify directly. If you're + working on changes to third-party code, or on a feature that is + taking longer to develop than the rate of change of the code + beneath, you will often need to sync up with the underlying + code, and fix up any hunks in your patches that no longer apply. + This is called rebasing your patch + series. + + The simplest way to do this is to hg + qpop your patches, then hg pull changes into the underlying + repository, and finally hg qpush your + patches again. MQ will stop pushing any time it runs across a + patch that fails to apply during conflicts, allowing you to fix + your conflicts, qrefresh the + affected patch, and continue pushing until you have fixed your + entire stack. + + This approach is easy to use and works well if you don't + expect changes to the underlying code to affect how well your + patches apply. If your patch stack touches code that is modified + frequently or invasively in the underlying repository, however, + fixing up rejected hunks by hand quickly becomes + tiresome. + + It's possible to partially automate the rebasing process. + If your patches apply cleanly against some revision of the + underlying repo, MQ can use this information to help you to + resolve conflicts between your patches and a different + revision. + + The process is a little involved. + + To begin, hg qpush + -a all of your patches on top of the revision + where you know that they apply cleanly. + + Save a backup copy of your patch directory using + hg qsave . + This prints the name of the directory that it has saved the + patches in. It will save the patches to a directory called + .hg/patches.N, where + N is a small integer. It also commits a + save changeset on top of your applied + patches; this is for internal book-keeping, and records the + states of the series and + status files. + + Use hg pull to + bring new changes into the underlying repository. (Don't + run hg pull -u; see below + for why.) + + Update to the new tip revision, using hg update to override + the patches you have pushed. + + Merge all patches using hg qpush -m + -a. The option to + qpush tells MQ to + perform a three-way merge if the patch fails to + apply. + + + During the hg qpush , + each patch in the series + file is applied normally. If a patch applies with fuzz or + rejects, MQ looks at the queue you qsaved, and performs a three-way + merge with the corresponding changeset. This merge uses + Mercurial's normal merge machinery, so it may pop up a GUI merge + tool to help you to resolve problems. + + When you finish resolving the effects of a patch, MQ + refreshes your patch based on the result of the merge. + + At the end of this process, your repository will have one + extra head from the old patch queue, and a copy of the old patch + queue will be in .hg/patches.N. You can remove the + extra head using hg qpop -a -n + patches.N or hg + strip. You can delete .hg/patches.N once you are sure + that you no longer need it as a backup. + + + + Identifying patches + + MQ commands that work with patches let you refer to a patch + either by using its name or by a number. By name is obvious + enough; pass the name foo.patch to qpush, for example, and it will + push patches until foo.patch is + applied. + + As a shortcut, you can refer to a patch using both a name + and a numeric offset; foo.patch-2 means + two patches before foo.patch, + while bar.patch+4 means four patches + after bar.patch. + + Referring to a patch by index isn't much different. The + first patch printed in the output of qseries is patch zero (yes, it's + one of those start-at-zero counting systems); the second is + patch one; and so on. + + MQ also makes it easy to work with patches when you are + using normal Mercurial commands. Every command that accepts a + changeset ID will also accept the name of an applied patch. MQ + augments the tags normally in the repository with an eponymous + one for each applied patch. In addition, the special tags + qbase and + qtip identify + the bottom-most and topmost applied patches, + respectively. + + These additions to Mercurial's normal tagging capabilities + make dealing with patches even more of a breeze. + + Want to patchbomb a mailing list with your + latest series of changes? + hg email qbase:qtip + (Don't know what patchbombing is? See + .) + + Need to see all of the patches since + foo.patch that have touched files in a + subdirectory of your tree? + hg log -r foo.patch:qtip subdir + + + + Because MQ makes the names of patches available to the rest + of Mercurial through its normal internal tag machinery, you + don't need to type in the entire name of a patch when you want + to identify it by name. + + Another nice consequence of representing patch names as tags + is that when you run the hg log + command, it will display a patch's name as a tag, simply as part + of its normal output. This makes it easy to visually + distinguish applied patches from underlying + normal revisions. The following example shows a + few normal Mercurial commands in use with applied + patches. + + +$ hg qapplied +first.patch +second.patch +$ hg log -r qbase:qtip +changeset: 1:c3bcf3b7335a +tag: first.patch +tag: qbase +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:08 2009 +0000 +summary: [mq]: first.patch + +changeset: 2:d189ba63b5f7 +tag: qtip +tag: second.patch +tag: tip +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:09 2009 +0000 +summary: [mq]: second.patch + +$ hg export second.patch +# HG changeset patch +# User Bryan O'Sullivan <bos@serpentine.com> +# Date 1250431509 0 +# Node ID d189ba63b5f7427f9644663c01fc16fe80399c65 +# Parent c3bcf3b7335afc0a250e85c51a1266d35d43a545 +[mq]: second.patch + +diff -r c3bcf3b7335a -r d189ba63b5f7 other.c +--- /dev/null Thu Jan 01 00:00:00 1970 +0000 ++++ b/other.c Sun Aug 16 14:05:09 2009 +0000 +@@ -0,0 +1,1 @@ ++double u; + + + + + + + Useful things to know about + + There are a number of aspects of MQ usage that don't fit + tidily into sections of their own, but that are good to know. + Here they are, in one place. + + + Normally, when you qpop a patch and qpush it again, the changeset + that represents the patch after the pop/push will have a + different identity than the changeset + that represented the hash beforehand. See for + information as to why this is. + + It's not a good idea to hg merge changes from another + branch with a patch changeset, at least if you want to + maintain the patchiness of that changeset and + changesets below it on the patch stack. If you try to do + this, it will appear to succeed, but MQ will become + confused. + + + + + Managing patches in a repository + + Because MQ's .hg/patches directory resides + outside a Mercurial repository's working directory, the + underlying Mercurial repository knows nothing + about the management or presence of patches. + + This presents the interesting possibility of managing the + contents of the patch directory as a Mercurial repository in its + own right. This can be a useful way to work. For example, you + can work on a patch for a while, qrefresh it, then hg commit the current state of the + patch. This lets you roll back to that version + of the patch later on. + + You can then share different versions of the same patch + stack among multiple underlying repositories. I use this when I + am developing a Linux kernel feature. I have a pristine copy of + my kernel sources for each of several CPU architectures, and a + cloned repository under each that contains the patches I am + working on. When I want to test a change on a different + architecture, I push my current patches to the patch repository + associated with that kernel tree, pop and push all of my + patches, and build and test that kernel. + + Managing patches in a repository makes it possible for + multiple developers to work on the same patch series without + colliding with each other, all on top of an underlying source + base that they may or may not control. + + + MQ support for patch repositories + + MQ helps you to work with the .hg/patches directory as a + repository; when you prepare a repository for working with + patches using qinit, you + can pass the option to create the .hg/patches directory as a + Mercurial repository. + + + If you forget to use the option, you + can simply go into the .hg/patches directory at any + time and run hg init. + Don't forget to add an entry for the status file to the .hgignore file, though + + (hg qinit + does this for you automatically); you + really don't want to manage the + status file. + + + As a convenience, if MQ notices that the .hg/patches directory is a + repository, it will automatically hg + add every patch that you create and import. + + MQ provides a shortcut command, qcommit, that runs hg commit in the .hg/patches + directory. This saves some bothersome typing. + + Finally, as a convenience to manage the patch directory, + you can define the alias mq on Unix + systems. For example, on Linux systems using the + bash shell, you can include the following + snippet in your ~/.bashrc. + + alias mq=`hg -R $(hg root)/.hg/patches' + + You can then issue commands of the form mq + pull from the main repository. + + + + A few things to watch out for + + MQ's support for working with a repository full of patches + is limited in a few small respects. + + MQ cannot automatically detect changes that you make to + the patch directory. If you hg + pull, manually edit, or hg + update changes to patches or the series file, you will have to + hg qpop and + then hg qpush in + the underlying repository to see those changes show up there. + If you forget to do this, you can confuse MQ's idea of which + patches are applied. + + + + + Third party tools for working with patches + + Once you've been working with patches for a while, you'll + find yourself hungry for tools that will help you to understand + and manipulate the patches you're dealing with. + + The diffstat command + web:diffstat generates a histogram of the + modifications made to each file in a patch. It provides a good + way to get a sense of a patch—which files + it affects, and how much change it introduces to each file and + as a whole. (I find that it's a good idea to use + diffstat's option as a matter of + course, as otherwise it will try to do clever things with + prefixes of file names that inevitably confuse at least + me.) + + +$ diffstat -p1 remove-redundant-null-checks.patch + drivers/char/agp/sgi-agp.c | 5 ++--- + drivers/char/hvcs.c | 11 +++++------ + drivers/message/fusion/mptfc.c | 6 ++---- + drivers/message/fusion/mptsas.c | 3 +-- + drivers/net/fs_enet/fs_enet-mii.c | 3 +-- + drivers/net/wireless/ipw2200.c | 22 ++++++---------------- + drivers/scsi/libata-scsi.c | 4 +--- + drivers/video/au1100fb.c | 3 +-- + 8 files changed, 19 insertions(+), 38 deletions(-) +$ filterdiff -i '*/video/*' remove-redundant-null-checks.patch +--- a/drivers/video/au1100fb.c~remove-redundant-null-checks-before-free-in-drivers ++++ a/drivers/video/au1100fb.c +@@ -743,8 +743,7 @@ void __exit au1100fb_cleanup(void) + { + driver_unregister(&au1100fb_driver); + +- if (drv_info.opt_mode) +- kfree(drv_info.opt_mode); ++ kfree(drv_info.opt_mode); + } + + module_init(au1100fb_init); + + + + + The patchutils package + web:patchutils is invaluable. It provides a + set of small utilities that follow the Unix + philosophy; each does one useful thing with a patch. + The patchutils command I use + most is filterdiff, which extracts subsets + from a patch file. For example, given a patch that modifies + hundreds of files across dozens of directories, a single + invocation of filterdiff can generate a + smaller patch that only touches files whose names match a + particular glob pattern. See for another + example. + + + + Good ways to work with patches + + Whether you are working on a patch series to submit to a + free software or open source project, or a series that you + intend to treat as a sequence of regular changesets when you're + done, you can use some simple techniques to keep your work well + organized. + + Give your patches descriptive names. A good name for a + patch might be rework-device-alloc.patch, + because it will immediately give you a hint what the purpose of + the patch is. Long names shouldn't be a problem; you won't be + typing the names often, but you will be + running commands like qapplied and qtop over and over. Good naming + becomes especially important when you have a number of patches + to work with, or if you are juggling a number of different tasks + and your patches only get a fraction of your attention. + + Be aware of what patch you're working on. Use the qtop command and skim over the text + of your patches frequently—for example, using hg tip )—to be sure + of where you stand. I have several times worked on and qrefreshed a patch other than the + one I intended, and it's often tricky to migrate changes into + the right patch after making them in the wrong one. + + For this reason, it is very much worth investing a little + time to learn how to use some of the third-party tools I + described in , + particularly + diffstat and filterdiff. + The former will give you a quick idea of what changes your patch + is making, while the latter makes it easy to splice hunks + selectively out of one patch and into another. + + + + MQ cookbook + + + Manage <quote>trivial</quote> patches + + Because the overhead of dropping files into a new + Mercurial repository is so low, it makes a lot of sense to + manage patches this way even if you simply want to make a few + changes to a source tarball that you downloaded. + + Begin by downloading and unpacking the source tarball, and + turning it into a Mercurial repository. + + +$ download netplug-1.2.5.tar.bz2 +$ tar jxf netplug-1.2.5.tar.bz2 +$ cd netplug-1.2.5 +$ hg init +$ hg commit -q --addremove --message netplug-1.2.5 +$ cd .. +$ hg clone netplug-1.2.5 netplug +updating working directory +18 files updated, 0 files merged, 0 files removed, 0 files unresolved + + + + + Continue by creating a patch stack and making your + changes. + + +$ cd netplug +$ hg qinit +$ hg qnew -m 'fix build problem with gcc 4' build-fix.patch +$ perl -pi -e 's/int addr_len/socklen_t addr_len/' netlink.c +$ hg qrefresh +$ hg tip -p +changeset: 1:eeab56666c54 +tag: qtip +tag: build-fix.patch +tag: tip +tag: qbase +user: Bryan O'Sullivan <bos@serpentine.com> +date: Sun Aug 16 14:05:10 2009 +0000 +summary: fix build problem with gcc 4 + +diff -r 1f6afe9a2d68 -r eeab56666c54 netlink.c +--- a/netlink.c Sun Aug 16 14:05:09 2009 +0000 ++++ b/netlink.c Sun Aug 16 14:05:10 2009 +0000 +@@ -275,7 +275,7 @@ + exit(1); + } + +- int addr_len = sizeof(addr); ++ socklen_t addr_len = sizeof(addr); + + if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) { + do_log(LOG_ERR, "Could not get socket details: %m"); + + + + + + Let's say a few weeks or months pass, and your package + author releases a new version. First, bring their changes + into the repository. + + +$ hg qpop -a +patch queue now empty +$ cd .. +$ download netplug-1.2.8.tar.bz2 +$ hg clone netplug-1.2.5 netplug-1.2.8 +updating working directory +18 files updated, 0 files merged, 0 files removed, 0 files unresolved +$ cd netplug-1.2.8 +$ hg locate -0 | xargs -0 rm +$ cd .. +$ tar jxf netplug-1.2.8.tar.bz2 +$ cd netplug-1.2.8 +$ hg commit --addremove --message netplug-1.2.8 + + + + + The pipeline starting with hg + locate above deletes all files in the working + directory, so that hg + commit's option can + actually tell which files have really been removed in the + newer version of the source. + + Finally, you can apply your patches on top of the new + tree. + + +$ cd ../netplug +$ hg pull ../netplug-1.2.8 +pulling from ../netplug-1.2.8 +searching for changes +adding changesets +adding manifests +adding file changes +added 1 changesets with 12 changes to 12 files +(run 'hg update' to get a working copy) +$ hg qpush -a +(working directory not at tip) +applying build-fix.patch +now at: build-fix.patch + + + + + + + Combining entire patches + + MQ provides a command, qfold that lets you combine + entire patches. This folds the patches you + name, in the order you name them, into the topmost applied + patch, and concatenates their descriptions onto the end of its + description. The patches that you fold must be unapplied + before you fold them. + + The order in which you fold patches matters. If your + topmost applied patch is foo, and you + qfold + bar and quux into it, + you will end up with a patch that has the same effect as if + you applied first foo, then + bar, followed by + quux. + + + + Merging part of one patch into another + + Merging part of one patch into + another is more difficult than combining entire + patches. + + If you want to move changes to entire files, you can use + filterdiff's and options to choose the + modifications to snip out of one patch, concatenating its + output onto the end of the patch you want to merge into. You + usually won't need to modify the patch you've merged the + changes from. Instead, MQ will report some rejected hunks + when you qpush it (from + the hunks you moved into the other patch), and you can simply + qrefresh the patch to drop + the duplicate hunks. + + If you have a patch that has multiple hunks modifying a + file, and you only want to move a few of those hunks, the job + becomes more messy, but you can still partly automate it. Use + lsdiff -nvv to print some metadata about + the patch. + + +$ lsdiff -nvv remove-redundant-null-checks.patch +22 File #1 a/drivers/char/agp/sgi-agp.c + 24 Hunk #1 static int __devinit agp_sgi_init(void) +37 File #2 a/drivers/char/hvcs.c + 39 Hunk #1 static struct tty_operations hvcs_ops = + 53 Hunk #2 static int hvcs_alloc_index_list(int n) +69 File #3 a/drivers/message/fusion/mptfc.c + 71 Hunk #1 mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, in +85 File #4 a/drivers/message/fusion/mptsas.c + 87 Hunk #1 mptsas_probe_hba_phys(MPT_ADAPTER *ioc) +98 File #5 a/drivers/net/fs_enet/fs_enet-mii.c + 100 Hunk #1 static struct fs_enet_mii_bus *create_bu +111 File #6 a/drivers/net/wireless/ipw2200.c + 113 Hunk #1 static struct ipw_fw_error *ipw_alloc_er + 126 Hunk #2 static ssize_t clear_error(struct device + 140 Hunk #3 static void ipw_irq_tasklet(struct ipw_p + 150 Hunk #4 static void ipw_pci_remove(struct pci_de +164 File #7 a/drivers/scsi/libata-scsi.c + 166 Hunk #1 int ata_cmd_ioctl(struct scsi_device *sc +178 File #8 a/drivers/video/au1100fb.c + 180 Hunk #1 void __exit au1100fb_cleanup(void) + + + + + This command prints three different kinds of + number: + + (in the first column) a file + number to identify each file modified in the + patch; + + (on the next line, indented) the line number + within a modified file where a hunk starts; and + + (on the same line) a hunk + number to identify that hunk. + + + You'll have to use some visual inspection, and reading of + the patch, to identify the file and hunk numbers you'll want, + but you can then pass them to to + filterdiff's and options, to + select exactly the file and hunk you want to extract. + + Once you have this hunk, you can concatenate it onto the + end of your destination patch and continue with the remainder + of . + + + + + Differences between quilt and MQ + + If you are already familiar with quilt, MQ provides a + similar command set. There are a few differences in the way + that it works. + + You will already have noticed that most quilt commands have + MQ counterparts that simply begin with a + q. The exceptions are quilt's + add and remove commands, + the counterparts for which are the normal Mercurial hg add and hg + remove commands. There is no MQ equivalent of the + quilt edit command. + + +
+ + + + + + + + + Advanced uses of Mercurial Queues + + While it's easy to pick up straightforward uses of Mercurial + Queues, use of a little discipline and some of MQ's less + frequently used capabilities makes it possible to work in + complicated development environments. + + In this chapter, I will use as an example a technique I have + used to manage the development of an Infiniband device driver for + the Linux kernel. The driver in question is large (at least as + drivers go), with 25,000 lines of code spread across 35 source + files. It is maintained by a small team of developers. + + While much of the material in this chapter is specific to + Linux, the same principles apply to any code base for which you're + not the primary owner, and upon which you need to do a lot of + development. + + + The problem of many targets + + The Linux kernel changes rapidly, and has never been + internally stable; developers frequently make drastic changes + between releases. This means that a version of the driver that + works well with a particular released version of the kernel will + not even compile correctly against, + typically, any other version. + + To maintain a driver, we have to keep a number of distinct + versions of Linux in mind. + + One target is the main Linux kernel development + tree. Maintenance of the code is in this case partly shared + by other developers in the kernel community, who make + drive-by modifications to the driver as they + develop and refine kernel subsystems. + + We also maintain a number of + backports to older versions of the Linux + kernel, to support the needs of customers who are running + older Linux distributions that do not incorporate our + drivers. (To backport a piece of code + is to modify it to work in an older version of its target + environment than the version it was developed for.) + + Finally, we make software releases on a schedule + that is necessarily not aligned with those used by Linux + distributors and kernel developers, so that we can deliver + new features to customers without forcing them to upgrade + their entire kernels or distributions. + + + + Tempting approaches that don't work well + + There are two standard ways to maintain a + piece of software that has to target many different + environments. + + The first is to maintain a number of branches, each + intended for a single target. The trouble with this approach + is that you must maintain iron discipline in the flow of + changes between repositories. A new feature or bug fix must + start life in a pristine repository, then + percolate out to every backport repository. Backport changes + are more limited in the branches they should propagate to; a + backport change that is applied to a branch where it doesn't + belong will probably stop the driver from compiling. + + The second is to maintain a single source tree filled with + conditional statements that turn chunks of code on or off + depending on the intended target. Because these + ifdefs are not allowed in the Linux kernel + tree, a manual or automatic process must be followed to strip + them out and yield a clean tree. A code base maintained in + this fashion rapidly becomes a rat's nest of conditional + blocks that are difficult to understand and maintain. + + Neither of these approaches is well suited to a situation + where you don't own the canonical copy of a + source tree. In the case of a Linux driver that is + distributed with the standard kernel, Linus's tree contains + the copy of the code that will be treated by the world as + canonical. The upstream version of my driver + can be modified by people I don't know, without me even + finding out about it until after the changes show up in + Linus's tree. + + These approaches have the added weakness of making it + difficult to generate well-formed patches to submit + upstream. + + In principle, Mercurial Queues seems like a good candidate + to manage a development scenario such as the above. While + this is indeed the case, MQ contains a few added features that + make the job more pleasant. + + + + + Conditionally applying patches with guards + + Perhaps the best way to maintain sanity with so many targets + is to be able to choose specific patches to apply for a given + situation. MQ provides a feature called guards + (which originates with quilt's guards + command) that does just this. To start off, let's create a + simple repository for experimenting in. + + +$ hg qinit +$ hg qnew hello.patch +$ echo hello > hello +$ hg add hello +$ hg qrefresh +$ hg qnew goodbye.patch +$ echo goodbye > goodbye +$ hg add goodbye +$ hg qrefresh + + + + + This gives us a tiny repository that contains two patches + that don't have any dependencies on each other, because they + touch different files. + + The idea behind conditional application is that you can + tag a patch with a guard, + which is simply a text string of your choosing, then tell MQ to + select specific guards to use when applying patches. MQ will + then either apply, or skip over, a guarded patch, depending on + the guards that you have selected. + + A patch can have an arbitrary number of guards; each one is + positive (apply this patch if this + guard is selected) or negative + (skip this patch if this guard is selected). A + patch with no guards is always applied. + + + + Controlling the guards on a patch + + The qguard command lets + you determine which guards should apply to a patch, or display + the guards that are already in effect. Without any arguments, it + displays the guards on the current topmost patch. + + +$ hg qguard +goodbye.patch: unguarded + + + + + To set a positive guard on a patch, prefix the name of the + guard with a +. + + +$ hg qguard +foo +$ hg qguard +goodbye.patch: +foo + + + + + To set a negative guard + on a patch, prefix the name of the guard with a + -. + + +$ hg qguard -- hello.patch -quux +$ hg qguard hello.patch +hello.patch: -quux + + + + + Notice that we prefixed the arguments to the hg + qguard command with a -- here, so + that Mercurial would not interpret the text + -quux as an option. + + + Setting vs. modifying + + The qguard command + sets the guards on a patch; it doesn't + modify them. What this means is that if + you run hg qguard +a +b on a + patch, then hg qguard +c on + the same patch, the only guard that will + be set on it afterwards is +c. + + + Mercurial stores guards in the series file; the form in which they + are stored is easy both to understand and to edit by hand. (In + other words, you don't have to use the qguard command if you don't want + to; it's okay to simply edit the series file.) + + +$ cat .hg/patches/series +hello.patch #-quux +goodbye.patch #+foo + + + + + + + Selecting the guards to use + + The qselect command + determines which guards are active at a given time. The effect + of this is to determine which patches MQ will apply the next + time you run qpush. It has + no other effect; in particular, it doesn't do anything to + patches that are already applied. + + With no arguments, the qselect command lists the guards + currently in effect, one per line of output. Each argument is + treated as the name of a guard to apply. + + +$ hg qpop -a +patch queue now empty +$ hg qselect +no active guards +$ hg qselect foo +number of unguarded, unapplied patches has changed from 1 to 2 +$ hg qselect +foo + + + + + In case you're interested, the currently selected guards are + stored in the guards file. + + +$ cat .hg/patches/guards +foo + + + + + We can see the effect the selected guards have when we run + qpush. + + +$ hg qpush -a +applying hello.patch +applying goodbye.patch +now at: goodbye.patch + + + + + A guard cannot start with a + + or + - character. The name of a + guard must not contain white space, but most other characters + are acceptable. If you try to use a guard with an invalid name, + MQ will complain: + + +$ hg qselect +foo +abort: guard '+foo' starts with invalid character: '+' + + + + + Changing the selected guards changes the patches that are + applied. + + +$ hg qselect quux +number of guarded, applied patches has changed from 0 to 2 +$ hg qpop -a +patch queue now empty +$ hg qpush -a +patch series already fully applied + + + + + You can see in the example below that negative guards take + precedence over positive guards. + + +$ hg qselect foo bar +number of unguarded, unapplied patches has changed from 0 to 2 +$ hg qpop -a +no patches applied +$ hg qpush -a +applying hello.patch +applying goodbye.patch +now at: goodbye.patch + + + + + + + MQ's rules for applying patches + + The rules that MQ uses when deciding whether to apply a + patch are as follows. + + A patch that has no guards is always + applied. + + If the patch has any negative guard that matches + any currently selected guard, the patch is skipped. + + If the patch has any positive guard that matches + any currently selected guard, the patch is applied. + + If the patch has positive or negative guards, + but none matches any currently selected guard, the patch is + skipped. + + + + + Trimming the work environment + + In working on the device driver I mentioned earlier, I don't + apply the patches to a normal Linux kernel tree. Instead, I use + a repository that contains only a snapshot of the source files + and headers that are relevant to Infiniband development. This + repository is 1% the size of a kernel repository, so it's easier + to work with. + + I then choose a base version on top of which + the patches are applied. This is a snapshot of the Linux kernel + tree as of a revision of my choosing. When I take the snapshot, + I record the changeset ID from the kernel repository in the + commit message. Since the snapshot preserves the + shape and content of the relevant parts of the + kernel tree, I can apply my patches on top of either my tiny + repository or a normal kernel tree. + + Normally, the base tree atop which the patches apply should + be a snapshot of a very recent upstream tree. This best + facilitates the development of patches that can easily be + submitted upstream with few or no modifications. + + + + Dividing up the <filename role="special" moreinfo="none">series</filename> + file + + I categorise the patches in the series file into a number of logical + groups. Each section of like patches begins with a block of + comments that describes the purpose of the patches that + follow. + + The sequence of patch groups that I maintain follows. The + ordering of these groups is important; I'll describe why after I + introduce the groups. + + The accepted group. Patches that + the development team has submitted to the maintainer of the + Infiniband subsystem, and which he has accepted, but which + are not present in the snapshot that the tiny repository is + based on. These are read only patches, + present only to transform the tree into a similar state as + it is in the upstream maintainer's repository. + + The rework group. Patches that I + have submitted, but that the upstream maintainer has + requested modifications to before he will accept + them. + + The pending group. Patches that + I have not yet submitted to the upstream maintainer, but + which we have finished working on. These will be read + only for a while. If the upstream maintainer + accepts them upon submission, I'll move them to the end of + the accepted group. If he requests that I + modify any, I'll move them to the beginning of the + rework group. + + The in progress group. Patches + that are actively being developed, and should not be + submitted anywhere yet. + + The backport group. Patches that + adapt the source tree to older versions of the kernel + tree. + + The do not ship group. Patches + that for some reason should never be submitted upstream. + For example, one such patch might change embedded driver + identification strings to make it easier to distinguish, in + the field, between an out-of-tree version of the driver and + a version shipped by a distribution vendor. + + + Now to return to the reasons for ordering groups of patches + in this way. We would like the lowest patches in the stack to + be as stable as possible, so that we will not need to rework + higher patches due to changes in context. Putting patches that + will never be changed first in the series file serves this + purpose. + + We would also like the patches that we know we'll need to + modify to be applied on top of a source tree that resembles the + upstream tree as closely as possible. This is why we keep + accepted patches around for a while. + + The backport and do not ship + patches float at the end of the series file. The backport patches + must be applied on top of all other patches, and the do + not ship patches might as well stay out of harm's + way. + + + + Maintaining the patch series + + In my work, I use a number of guards to control which + patches are to be applied. + + + Accepted patches are guarded with + accepted. I enable this guard most of + the time. When I'm applying the patches on top of a tree + where the patches are already present, I can turn this patch + off, and the patches that follow it will apply + cleanly. + + Patches that are finished, but + not yet submitted, have no guards. If I'm applying the + patch stack to a copy of the upstream tree, I don't need to + enable any guards in order to get a reasonably safe source + tree. + + Those patches that need reworking before being + resubmitted are guarded with + rework. + + For those patches that are still under + development, I use devel. + + A backport patch may have several guards, one + for each version of the kernel to which it applies. For + example, a patch that backports a piece of code to 2.6.9 + will have a 2.6.9 guard. + + This variety of guards gives me considerable flexibility in + determining what kind of source tree I want to end up with. For + most situations, the selection of appropriate guards is + automated during the build process, but I can manually tune the + guards to use for less common circumstances. + + + The art of writing backport patches + + Using MQ, writing a backport patch is a simple process. + All such a patch has to do is modify a piece of code that uses + a kernel feature not present in the older version of the + kernel, so that the driver continues to work correctly under + that older version. + + A useful goal when writing a good backport patch is to + make your code look as if it was written for the older version + of the kernel you're targeting. The less obtrusive the patch, + the easier it will be to understand and maintain. If you're + writing a collection of backport patches to avoid the + rat's nest effect of lots of + #ifdefs (hunks of source code that are only + used conditionally) in your code, don't introduce + version-dependent #ifdefs into the patches. + Instead, write several patches, each of which makes + unconditional changes, and control their application using + guards. + + There are two reasons to divide backport patches into a + distinct group, away from the regular patches + whose effects they modify. The first is that intermingling the + two makes it more difficult to use a tool like the patchbomb extension to automate the + process of submitting the patches to an upstream maintainer. + The second is that a backport patch could perturb the context + in which a subsequent regular patch is applied, making it + impossible to apply the regular patch cleanly + without the earlier backport patch + already being applied. + + + + + Useful tips for developing with MQ + + + Organising patches in directories + + If you're working on a substantial project with MQ, it's + not difficult to accumulate a large number of patches. For + example, I have one patch repository that contains over 250 + patches. + + If you can group these patches into separate logical + categories, you can if you like store them in different + directories; MQ has no problems with patch names that contain + path separators. + + + + Viewing the history of a patch + + If you're developing a set of patches over a long time, + it's a good idea to maintain them in a repository, as + discussed in . If you do + so, you'll quickly + discover that using the hg + diff command to look at the history of changes to + a patch is unworkable. This is in part because you're looking + at the second derivative of the real code (a diff of a diff), + but also because MQ adds noise to the process by modifying + time stamps and directory names when it updates a + patch. + + However, you can use the extdiff extension, which is bundled + with Mercurial, to turn a diff of two versions of a patch into + something readable. To do this, you will need a third-party + package called patchutils + web:patchutils. This provides a command + named interdiff, which shows the + differences between two diffs as a diff. Used on two versions + of the same diff, it generates a diff that represents the diff + from the first to the second version. + + You can enable the extdiff extension in the usual way, + by adding a line to the extensions section of your + ~/.hgrc. + [extensions] +extdiff = + The interdiff command expects to be + passed the names of two files, but the extdiff extension passes the program + it runs a pair of directories, each of which can contain an + arbitrary number of files. We thus need a small program that + will run interdiff on each pair of files in + these two directories. This program is available as hg-interdiff in the examples directory of the + source code repository that accompanies this book. + + With the hg-interdiff + program in your shell's search path, you can run it as + follows, from inside an MQ patch directory: + hg extdiff -p hg-interdiff -r A:B my-change.patch + Since you'll probably want to use this long-winded command + a lot, you can get hgext to + make it available as a normal Mercurial command, again by + editing your ~/.hgrc. + [extdiff] +cmd.interdiff = hg-interdiff + This directs hgext to + make an interdiff command available, so you + can now shorten the previous invocation of extdiff to something a + little more wieldy. + hg interdiff -r A:B my-change.patch + + + The interdiff command works well + only if the underlying files against which versions of a + patch are generated remain the same. If you create a patch, + modify the underlying files, and then regenerate the patch, + interdiff may not produce useful + output. + + + The extdiff extension is + useful for more than merely improving the presentation of MQ + patches. To read more about it, go to . + + + + + + + + + + + + + Adding functionality with extensions + + While the core of Mercurial is quite complete from a + functionality standpoint, it's deliberately shorn of fancy + features. This approach of preserving simplicity keeps the + software easy to deal with for both maintainers and users. + + However, Mercurial doesn't box you in with an inflexible + command set: you can add features to it as + extensions (sometimes known as + plugins). We've already discussed a few of + these extensions in earlier chapters. + + + covers the fetch extension; + this combines pulling new changes and merging them with local + changes into a single command, fetch. + + In , we covered + several extensions that are useful for hook-related + functionality: acl adds + access control lists; bugzilla adds integration with the + Bugzilla bug tracking system; and notify sends notification emails on + new changes. + + The Mercurial Queues patch management extension is + so invaluable that it merits two chapters and an appendix all + to itself. covers the + basics; discusses advanced topics; + and goes into detail on + each + command. + + + In this chapter, we'll cover some of the other extensions that + are available for Mercurial, and briefly touch on some of the + machinery you'll need to know about if you want to write an + extension of your own. + + In , + we'll discuss the possibility of huge + performance improvements using the inotify extension. + + + + Improve performance with the <literal role="hg-ext" moreinfo="none">inotify</literal> extension + + Are you interested in having some of the most common + Mercurial operations run as much as a hundred times faster? + Read on! + + Mercurial has great performance under normal circumstances. + For example, when you run the hg + status command, Mercurial has to scan almost every + directory and file in your repository so that it can display + file status. Many other Mercurial commands need to do the same + work behind the scenes; for example, the hg diff command uses the status + machinery to avoid doing an expensive comparison operation on + files that obviously haven't changed. + + Because obtaining file status is crucial to good + performance, the authors of Mercurial have optimised this code + to within an inch of its life. However, there's no avoiding the + fact that when you run hg + status, Mercurial is going to have to perform at + least one expensive system call for each managed file to + determine whether it's changed since the last time Mercurial + checked. For a sufficiently large repository, this can take a + long time. + + To put a number on the magnitude of this effect, I created a + repository containing 150,000 managed files. I timed hg status as taking ten seconds to + run, even when none of those files had been + modified. + + Many modern operating systems contain a file notification + facility. If a program signs up to an appropriate service, the + operating system will notify it every time a file of interest is + created, modified, or deleted. On Linux systems, the kernel + component that does this is called + inotify. + + Mercurial's inotify + extension talks to the kernel's inotify + component to optimise hg status + commands. The extension has two components. A daemon sits in + the background and receives notifications from the + inotify subsystem. It also listens for + connections from a regular Mercurial command. The extension + modifies Mercurial's behavior so that instead of scanning the + filesystem, it queries the daemon. Since the daemon has perfect + information about the state of the repository, it can respond + with a result instantaneously, avoiding the need to scan every + directory and file in the repository. + + Recall the ten seconds that I measured plain Mercurial as + taking to run hg status on a + 150,000 file repository. With the inotify extension enabled, the time + dropped to 0.1 seconds, a factor of one + hundred faster. + + Before we continue, please pay attention to some + caveats. + + The inotify + extension is Linux-specific. Because it interfaces directly + to the Linux kernel's inotify subsystem, + it does not work on other operating systems. + + It should work on any Linux distribution that + was released after early 2005. Older distributions are + likely to have a kernel that lacks + inotify, or a version of + glibc that does not have the necessary + interfacing support. + + Not all filesystems are suitable for use with + the inotify extension. + Network filesystems such as NFS are a non-starter, for + example, particularly if you're running Mercurial on several + systems, all mounting the same network filesystem. The + kernel's inotify system has no way of + knowing about changes made on another system. Most local + filesystems (e.g. ext3, XFS, ReiserFS) should work + fine. + + + The inotify extension is + not yet shipped with Mercurial as of May 2007, so it's a little + more involved to set up than other extensions. But the + performance improvement is worth it! + + The extension currently comes in two parts: a set of patches + to the Mercurial source code, and a library of Python bindings + to the inotify subsystem. + + There are two Python + inotify binding libraries. One of them is + called pyinotify, and is packaged by some + Linux distributions as python-inotify. + This is not the one you'll need, as it is + too buggy and inefficient to be practical. + + To get going, it's best to already have a functioning copy + of Mercurial installed. + + If you follow the instructions below, you'll be + replacing and overwriting any existing + installation of Mercurial that you might already have, using + the latest bleeding edge Mercurial code. Don't + say you weren't warned! + + + Clone the Python inotify + binding repository. Build and install it. + hg clone http://hg.kublai.com/python/inotify +cd inotify +python setup.py build --force +sudo python setup.py install --skip-build + + Clone the crew Mercurial repository. + Clone the inotify patch + repository so that Mercurial Queues will be able to apply + patches to your cope of the crew repository. + hg clone http://hg.intevation.org/mercurial/crew +hg clone crew inotify +hg clone http://hg.kublai.com/mercurial/patches/inotify inotify/.hg/patches + + Make sure that you have the Mercurial Queues + extension, mq, enabled. If + you've never used MQ, read to get started + quickly. + + Go into the inotify repo, and apply all + of the inotify patches + using the option to the qpush command. + cd inotify +hg qpush -a + + If you get an error message from qpush, you should not continue. + Instead, ask for help. + + Build and install the patched version of + Mercurial. + python setup.py build --force +sudo python setup.py install --skip-build + + + Once you've build a suitably patched version of Mercurial, + all you need to do to enable the inotify extension is add an entry to + your ~/.hgrc. + [extensions] inotify = + When the inotify extension + is enabled, Mercurial will automatically and transparently start + the status daemon the first time you run a command that needs + status in a repository. It runs one status daemon per + repository. + + The status daemon is started silently, and runs in the + background. If you look at a list of running processes after + you've enabled the inotify + extension and run a few commands in different repositories, + you'll thus see a few hg processes sitting + around, waiting for updates from the kernel and queries from + Mercurial. + + The first time you run a Mercurial command in a repository + when you have the inotify + extension enabled, it will run with about the same performance + as a normal Mercurial command. This is because the status + daemon needs to perform a normal status scan so that it has a + baseline against which to apply later updates from the kernel. + However, every subsequent command that does + any kind of status check should be noticeably faster on + repositories of even fairly modest size. Better yet, the bigger + your repository is, the greater a performance advantage you'll + see. The inotify daemon makes + status operations almost instantaneous on repositories of all + sizes! + + If you like, you can manually start a status daemon using + the inserve command. + This gives you slightly finer control over how the daemon ought + to run. This command will of course only be available when the + inotify extension is + enabled. + + When you're using the inotify extension, you should notice + no difference at all in Mercurial's + behavior, with the sole exception of status-related commands + running a whole lot faster than they used to. You should + specifically expect that commands will not print different + output; neither should they give different results. If either of + these situations occurs, please report a bug. + + + + Flexible diff support with the <literal role="hg-ext" moreinfo="none">extdiff</literal> extension + + Mercurial's built-in hg + diff command outputs plaintext unified diffs. + + +$ hg diff +diff -r 801b35c37d8b myfile +--- a/myfile Sun Aug 16 14:05:02 2009 +0000 ++++ b/myfile Sun Aug 16 14:05:02 2009 +0000 +@@ -1,1 +1,2 @@ + The first line. ++The second line. + + + + + If you would like to use an external tool to display + modifications, you'll want to use the extdiff extension. This will let you + use, for example, a graphical diff tool. + + The extdiff extension is + bundled with Mercurial, so it's easy to set up. In the extensions section of your + ~/.hgrc, simply add a + one-line entry to enable the extension. + [extensions] +extdiff = + This introduces a command named extdiff, which by default uses + your system's diff command to generate a + unified diff in the same form as the built-in hg diff command. + + +$ hg extdiff +--- a.801b35c37d8b/myfile 2009-08-16 14:05:02.000000000 +0000 ++++ /tmp/extdiffl1y_s9/a/myfile 2009-08-16 14:05:02.000000000 +0000 +@@ -1 +1,2 @@ + The first line. ++The second line. + + + + + The result won't be exactly the same as with the built-in + hg diff variations, because the + output of diff varies from one system to + another, even when passed the same options. + + As the making snapshot + lines of output above imply, the extdiff command works by + creating two snapshots of your source tree. The first snapshot + is of the source revision; the second, of the target revision or + working directory. The extdiff command generates + these snapshots in a temporary directory, passes the name of + each directory to an external diff viewer, then deletes the + temporary directory. For efficiency, it only snapshots the + directories and files that have changed between the two + revisions. + + Snapshot directory names have the same base name as your + repository. If your repository path is /quux/bar/foo, then foo will be the name of each + snapshot directory. Each snapshot directory name has its + changeset ID appended, if appropriate. If a snapshot is of + revision a631aca1083f, the directory will be + named foo.a631aca1083f. + A snapshot of the working directory won't have a changeset ID + appended, so it would just be foo in this example. To see what + this looks like in practice, look again at the extdiff example above. Notice + that the diff has the snapshot directory names embedded in its + header. + + The extdiff command + accepts two important options. The option + lets you choose a program to view differences with, instead of + diff. With the option, + you can change the options that extdiff passes to the program + (by default, these options are + -Npru, which only make sense + if you're running diff). In other respects, + the extdiff command + acts similarly to the built-in hg + diff command: you use the same option names, syntax, + and arguments to specify the revisions you want, the files you + want, and so on. + + As an example, here's how to run the normal system + diff command, getting it to generate context + diffs (using the option) + instead of unified diffs, and five lines of context instead of + the default three (passing 5 as the argument + to the option). + + +$ hg extdiff -o -NprcC5 +*** a.801b35c37d8b/myfile Sun Aug 16 14:05:02 2009 +--- /tmp/extdiffl1y_s9/a/myfile Sun Aug 16 14:05:02 2009 +*************** +*** 1 **** +--- 1,2 ---- + The first line. ++ The second line. + + + + + Launching a visual diff tool is just as easy. Here's how to + launch the kdiff3 viewer. + hg extdiff -p kdiff3 -o + + If your diff viewing command can't deal with directories, + you can easily work around this with a little scripting. For an + example of such scripting in action with the mq extension and the + interdiff command, see . + + + Defining command aliases + + It can be cumbersome to remember the options to both the + extdiff command and + the diff viewer you want to use, so the extdiff extension lets you define + new commands that will invoke your diff + viewer with exactly the right options. + + All you need to do is edit your ~/.hgrc, and add a section named + extdiff. Inside this + section, you can define multiple commands. Here's how to add + a kdiff3 command. Once you've defined + this, you can type hg kdiff3 + and the extdiff extension + will run kdiff3 for you. + [extdiff] +cmd.kdiff3 = + If you leave the right hand side of the definition empty, + as above, the extdiff + extension uses the name of the command you defined as the name + of the external program to run. But these names don't have to + be the same. Here, we define a command named + hg wibble, which runs + kdiff3. + [extdiff] + cmd.wibble = kdiff3 + + You can also specify the default options that you want to + invoke your diff viewing program with. The prefix to use is + opts., followed by the name + of the command to which the options apply. This example + defines a hg vimdiff command + that runs the vim editor's + DirDiff extension. + [extdiff] + cmd.vimdiff = vim +opts.vimdiff = -f '+next' '+execute "DirDiff" argv(0) argv(1)' + + + + + Cherrypicking changes with the <literal role="hg-ext" moreinfo="none">transplant</literal> extension + + Need to have a long chat with Brendan about this. + + + + Send changes via email with the <literal role="hg-ext" moreinfo="none">patchbomb</literal> extension + + Many projects have a culture of change + review, in which people send their modifications to a + mailing list for others to read and comment on before they + commit the final version to a shared repository. Some projects + have people who act as gatekeepers; they apply changes from + other people to a repository to which those others don't have + access. + + Mercurial makes it easy to send changes over email for + review or application, via its patchbomb extension. The extension is + so named because changes are formatted as patches, and it's usual + to send one changeset per email message. Sending a long series + of changes by email is thus much like bombing the + recipient's inbox, hence patchbomb. + + As usual, the basic configuration of the patchbomb extension takes just one or + two lines in your + /.hgrc. + [extensions] +patchbomb = + Once you've enabled the extension, you will have a new + command available, named email. + + The safest and best way to invoke the email command is to + always run it first with the option. + This will show you what the command would + send, without actually sending anything. Once you've had a + quick glance over the changes and verified that you are sending + the right ones, you can rerun the same command, with the option + removed. + + The email command + accepts the same kind of revision syntax as every other + Mercurial command. For example, this command will send every + revision between 7 and tip, inclusive. + hg email -n 7:tip + You can also specify a repository to + compare with. If you provide a repository but no revisions, the + email command will + send all revisions in the local repository that are not present + in the remote repository. If you additionally specify revisions + or a branch name (the latter using the option), + this will constrain the revisions sent. + + It's perfectly safe to run the email command without the + names of the people you want to send to: if you do this, it will + just prompt you for those values interactively. (If you're + using a Linux or Unix-like system, you should have enhanced + readline-style editing capabilities when + entering those headers, too, which is useful.) + + When you are sending just one revision, the email command will by + default use the first line of the changeset description as the + subject of the single email message it sends. + + If you send multiple revisions, the email command will usually + send one message per changeset. It will preface the series with + an introductory message, in which you should describe the + purpose of the series of changes you're sending. + + + Changing the behavior of patchbombs + + Not every project has exactly the same conventions for + sending changes in email; the patchbomb extension tries to + accommodate a number of variations through command line + options. + + You can write a subject for the introductory + message on the command line using the + option. This takes one argument, the text of the subject + to use. + + To change the email address from which the + messages originate, use the + option. This takes one argument, the email address to + use. + + The default behavior is to send unified diffs + (see for a + description of the + format), one per message. You can send a binary bundle + instead with the + option. + + Unified diffs are normally prefaced with a + metadata header. You can omit this, and send unadorned + diffs, with the option. + + Diffs are normally sent inline, + in the same body part as the description of a patch. This + makes it easiest for the largest number of readers to + quote and respond to parts of a diff, as some mail clients + will only quote the first MIME body part in a message. If + you'd prefer to send the description and the diff in + separate body parts, use the + option. + + Instead of sending mail messages, you can + write them to an mbox-format mail + folder using the + option. That option takes one argument, the name of the + file to write to. + + If you would like to add a + diffstat-format summary to each patch, + and one to the introductory message, use the + option. The diffstat command displays + a table containing the name of each file patched, the + number of lines affected, and a histogram showing how much + each file is modified. This gives readers a qualitative + glance at how complex a patch is. + + + + + + + + + + + + + +Migrer vers Mercurial + + Une manière courante de s'essayer à un nouveau + gestionnaire de révisions est d'expérimenter en migrant un + projet existant, plutôt que le faire avec un nouveau projet. + + + Dans cette annexe, nous discuterons comment importer + l'historique d'un projet dans Mercurial, et à quoi faire attention + si vous êtes habitués à un autre outil de gestion de révisions. + + + + Importer l'historique depuis un autre système + + Mercurial est livré avec une extension nommée + convert, qui permet d'importer un historique + depuis les gestionnaire de révisions les plus courants. Au moment de + l'écriture de ce livre, il pouvait importer l'historique depuis: + + + + Subversion + + + CVS + + + git + + + Darcs + + + Bazaar + + + Monotone + + + GNU Arch + + + Mercurial + + + + (Pour savoir pourquoi Mercurial lui même est supporté + comme source, voir .) + + Vous pouvez activer l'extension de la manière + habituelle, en éditant votre fichier ~/.hgrc + + [extensions] +convert = + + Ceci rendra la commande hg convert + disponible. La commande est facile à utiliser. Par exemple, la + commande suivante va importer l'historique Subversion du framework de test Nose Unit dans Mercurial. + + + $ hg convert http://python-nose.googlecode.com/svn/trunk + + L'extension convert opère de + manière incrémentale. En d'autres mots, après une première exécution de + la commande hg convert, les exécutions ultérieures + importeront les révisions ultérieures à l'exécution précédente. + La conversion incrémentale ne réussira que si + vous exécutez hg convert dans le même dépôt que vous + aviez utilisé à l'origine, ceci parce que l'extension convert + sauvegarde un certain nombre de méta-données privées dans le fichier + .hg/shamap (non versioné) au sein du dépôt cible. + + + Lorsque vous voulez faire des modifications en utilisant + Mercurial, le mieux est de faire un clone de l'ensemble de l'arborescence + que vous souhaitez convertir, et de laisser l'arborescence d'origine pour + de futures conversions incrémentales. C'est la manière la plus sûre pour vous laisser + récupérer et fusionner les modifications futures depuis l'outil de gestion + de révisions dans votre nouveau dépôt Mercurial. + + + Convertir plusieurs branches + + La commande hg convert citée + ci-dessus convertit seulement l'historique de la branche + principale (trunk) du dépôt Subversion. Si nous utilisons + à la place l'URL http://python-nose.googlecode.com/svn, + Mercurial va automatiquement détecter la + branche principale (trunk), les étiquettes + (tags), et les branches que les dépôts + Subversion utilisent généralement, et les importera chacun dans + une branche Mercurial distincte. + + Par défaut, chaque branche Subversion importée + dans Mercurial se voit attribuer un nom de branche. Une fois la + conversion achevée, vous pouvez obtenir la liste des noms des branches + actives dans le dépôt Mercurial en utilisant la commande + hg branches -a. Si vous préférez importer les + branches Subversion sans noms, ajoutez l'option à la commande + hg convert. + + Une fois votre arborescence convertie, + si vous souhaitez travailler selon la pratique habituelle sous Mercurial + avec une arborescence qui ne contient qu'une seule branche, vous pouvez cloner + cette seule branche en utilisant + hg clone -r nomdemabranche. + + + + Associer les noms d'utilisateurs + + Certains outils de gestion de révisions + ne sauvegardent, avec les modifications, que les noms + d'utilisateurs raccourcis. Ceux-ci peuvent être difficiles à + interpréter. La norme avec Mercurial est de sauvegarder le + nom du committeur et son adresse + mail, ce qui est beaucoup plus utile pour discuter avec lui + par la suite. + + Si vous convertissez une arborescence depuis + un gestionnaire de révisions qui utilise seulement les noms + raccourcis, vous pouvez associer ces noms à des équivalents + plus détaillés en passant l'option + à la commande hg convert. Cette option + attend un fichier qui contient des entrées sous la forme suivante: + + + arist = Aristotle <aristotle@phil.example.gr> +soc = Socrates <socrates@phil.example.gr> + + Quand convert trouve une + modification associée au nom arist dans le + dépôt de source, il va utiliser le nom Aristotle + <aristotle@phil.example.gr> dans les révisions + Mercurial. Si aucune correspondance n'est trouvé, il utilise + le nom tel quel. + + + + Nettoyer l'arboresence + + Tous les projets n'ont pas un historique parfait. + Il peut y avoir des répertoires qui n'auraient jamais dû être ajoutés, + un fichier qui est trop volumineux, ou même une partie de la + hiérarchie qui devrait être réorganisée. + + L'extension convert permet + d'utiliser un fichier d'association qui peut + réorganiser les fichiers et les répertoires dans un projet lors de + l'importation de son historique. Ceci est utile non seulement quand vous + importez l'historique d'un autre gestionnaire de révisions, mais + aussi pour nettoyer ou réorganiser l'arborescence d'un projet + Mercurial. + + Pour indiquer le fichier d'association, on utilise + l'option en lui fournissant un nom de + fichier. Le fichier d'association contient des lignes de la forme + suivante : + + # Ceci est un commentaire. +# Les lignes vides sont ignorées. + +include path/to/file + +exclude path/to/file + +rename from/some/path to/some/other/place + + + La directive include inclut un + fichier, ou l'ensemble des fichiers d'un répertoire, dans le dépôt + de destination. La directive exclude omet les + fichiers ou répertoires du dépôt. Ceci inclut aussi les autres + fichiers et répertoires qui ne sont pas explicitement inclus. + La directive exclude entraine l'omission + des fichiers ou répertoires, et autres fichiers qui ne sont pas + explicitement inclus. + + Pour déplacer un fichier ou un répertoire d'un + emplacement à un autre, utilisez la directive + rename. Si vous avez besoin de déplacer un + fichier ou un répertoire depuis un sous répertoire dans la racine + du dépôt, utilisez . comme second argument de + la directive rename. + + + + Améliorer les performances de la conversion Subversion + + Vous aurez souvent besoin de plusieurs essais + avant d'arriver à la parfaite combinaison de fichier d'association de fichiers, + de fichier d'association de noms d'utilisateurs et des autres paramètres. Or, + convertir un dépôt Mercurial via un protocole comme ssh + ou http peut être des milliers de fois plus long + que ce dont le système d'exploitation est en fait capable de faire, + à cause des latence réseau. Ceci peut rendre la conception de cette + combinaison parfaite très douloureuse. + + La commande svnsync + peut grandement améliorer la vitesse de conversion d'un dépôt + Subversion. Il s'agit d'un programme de miroir de dépôt Subversion + en lecture seule. L'idée est de créer un miroir local d'une + arborescence Subversion, puis de convertir ce miroir en dépôt + Mercurial. + + Supposez que nous voulions convertir le dépôt + Subversion du populaire projet Memcached en une arborescence Mercurial. + Tout d'abord, nous créons un dépôt Subversion local. + + $ svnadmin create memcached-mirror + + Puis, nous allons mettre en place un hook Subversion + dont svnsync a besoin. + + $ echo '#!/bin/sh' > memcached-mirror/hooks/pre-revprop-change +$ chmod +x memcached-mirror/hooks/pre-revprop-change + + Nous initialisons ensuite svnsync dans ce + dépôt. + + $ svnsync --init file://`pwd`/memcached-mirror \ + http://code.sixapart.com/svn/memcached + + La prochaine étape est de commencer le processus de + mirroring de svnsync. + + $ svnsync sync file://`pwd`/memcached-mirror + + Enfin, nous importons l'historique de notre dépôt + local Subversion dans Mercurial. + + $ hg convert memcached-mirror + + Nous pouvons utiliser ce processus de manière + incrémentale, si le dépôt Subversion est toujours en activité. + Il suffit d'exécuter de nouveau svnsync pour + récupérer les récentes modifications dans notre miroir, puis hg + convert + les importe dans notre arborescence Mercurial. + + Il y a deux avantages à utiliser un import à deux + étages comme avec svnsync. Le premier + est qu'il utilise du code de synchronisation réseau de Subversion + plus efficace que la commande hg convert, + et donc transfère moins de données par le réseau. Le deuxième + est que l'import depuis un dépôt Subversion local est si rapide que + vous pouvez peaufiner et réitérer les paramètres de conversion de + ce dernier sans souffrir de la qualité de la connexion réseau. + + + + + Migrer depuis Subversion + + Subversion est le système de gestion de versions + open source le plus populaire aujourd'hui. Bien qu'il y ait des + différences entre Mercurial et Subversion, faire la transition de + l'un à l'autre n'est pas très difficile. Les deux disposent en effet + de jeux de commandes similaires et d'interfaces similaires. + + + Différences philosophiques + + La différence fondamentale entre Subversion et + Mercurial est bien évidement que Subversion est centralisé, alors + que Mercurial est distribué. Puisque que Mercurial enregistre tout + l'historique d'un projet sur votre disque dur local, il n'a besoin + d'effectuer des accès au réseau que lorsque vous voulez + explicitement communiquer avec un autre dépôt. Subversion, par contre, + ne conserve que peu d'informations localement, et le client + doit donc communiquer avec le serveur central pour la + plupart des opérations communes. + + Subversion s'en tire plus ou moins bien sans notion + de branche réellement bien définie : quelle portion de l'espace de nommage + du serveur est une branche est une simple question de convention, le + logiciel n'imposant rien à ce sujet. Mercurial considère + un dépôt comme un élément de la gestion des branches. + + + Portée des commandes + + Puisque que Subversion ne sait pas réellement + quelle partie de son espace de nommage est en fait une branche, il + traite la plupart des commandes comme des requêtes à exécuter sur le + répertoire où vous vous situez, et ses sous répertoires. Par exemple, + si vous exécutez svn log, vous verrez l'historique + de la partie de l'arborescence où vous vous situez, et non de la + hiérarchie entière. + + Les commandes de Mercurial ont un comportement + différent : toutes les commandes s'appliquent à l'ensemble de l'arborescence + du dépôt. Exécutez la commande hg log et elle vous + donnera l'historique de l'ensemble de l'arborescence, quel que soit le + sous-répertoire où vous vous situez. Si + vous souhaitez obtenir l'historique d'un répertoire ou seulement d'un + fichier, ajouter simplement le nom de celui-ci à la commande, par + exemple hg log src. + + De ma propre expérience, cette différence dans leur + comportement par défaut est probablement ce qui risque de vous + surprendre le plus si vous passez régulièrement d'un outil à l'autre. + + + + Opération multi utilisateur et sécurité + + Avec Subversion, il est normal (bien que légèrement + désapprouvé) que différentes personnes collaborent sur une seule + branche. Si Alice et Bob travaillent ensemble, et Alice ajoute ses + modifications à leur branche partagée, Bob doit alors mettre à jour + sa vue de la branche avant de pouvoir appliquer un commit. + Puisqu'il n'a, à ce moment, pas effectué de commit + des modifications qu'il a faites, il se peut qu'il ne corrompe + ou ne perde + ses modifications pendant ou après la mise à jour. + + Mercurial encourage, à l'inverse, un modèle + "commit-puis-merge". Avant de récupérer des modifications depuis le + serveur, ou avant d'y envoyer les siennes, Bob enregistre ses + modifications de manière locale en appliquant un commit. C'est à dire + que si Alice avait envoyé ses modifications sur le serveur avant + que Bob n'envoie les siennes, ce dernier ne pourra le faire + qu'après avoir récupéré et fusionné celles d'Alice avec les siennes. + Si Bob fait alors une + erreur lors de la fusion, il pourra toujours restaurer sa version, pour + laquelle il avait appliqué le commit. + + Il est important de souligner qu'il s'agit de la + manière habituelle de travailler avec ces outils. Subversion propose + une manière plus sûre de "travailler-dans-votre-propre-branche", mais elle + est assez complexe pour que, en pratique, elle ne soit que rarement utilisé. + Mercurial propose de son côté un mode un peu moins sûr, permettant de + récupérer des modifications par dessus des modifications non + committées, qui reste toutefois très peu répandu. + + + + Publication vs changement locaux + + Une commande Subversion svn + commit publie immédiatement les modifications sur le + serveur, où elles peuvent être vu par n'importe qui doté d'un privilège + de lecture. + + Avec Mercurial, les modifications sont toujours d'abord + enregistrées localement, et doivent être par la suite transférés par + la commande hg push. + + Chaque approche a ses avantages et ses inconvénients. + Le modèle Subversion implique que les modifications soient publiées, et + donc disponibles immédiatement. D'un autre coté, cela implique aussi + que, pour pouvoir utiliser le logiciel normalement, un utilisateur doit + avoir les droits d'écriture dans le dépôt, et ce privilège n'est pas concédé + facilement par la plupart des projets Open Source. + + L'approche de Mercurial permet à quiconque de faire + un clone du dépôt et d'y ajouter ses modifications sans jamais avoir + besoin de la permission de quiconque, et l'on peut même publier ses + modifications et continuer à participer comme on le désire. Toutefois, la + distinction entre les commits et le transfert de ces derniers présente + le risque que quelqu'un applique ses modifications par un commit local + sur son portable et parte se promener pendant quelques jours en ayant + oublié de les transférer, ce qui peut, dans certains rares cas, + bloquer temporairement ses collaborateurs. + + + + + Références des commandes + + + Commandes Subversion et leurs équivalents Mercurial + + + + Subversion + Mercurial + Notes + + + + + svn add + hg add + + + + svn blame + hg annotate + + + + svn cat + hg cat + + + + svn checkout + hg clone + + + + svn cleanup + n/a + Aucun nettoyage nécessaire. + + + svn commit + hg commit; hg + push + hg push publie les modifications + après un commit. + + + svn copy + hg clone + Pour créer une nouvelle branche + + + svn copy + hg copy + Pour copier des fichiers ou des répertoires + + + svn delete (svn + remove) + hg remove + + + + svn diff + hg diff + + + + svn export + hg archive + + + + svn help + hg help + + + + svn import + hg addremove; hg + commit + + + + svn info + hg parents + Affiche la version sur la base de laquelle on travaille + + + svn info + hg showconfig + paths.default + Affiche de quelle URL est extrait ce dépôt + + + svn list + hg manifest + + + + svn log + hg log + + + + svn merge + hg merge + + + + svn mkdir + n/a + Mercurial ne versionne pas les répertoires + + + svn move (svn + rename) + hg rename + + + + svn resolved + hg resolve -m + + + + svn revert + hg revert + + + + svn status + hg status + + + + svn update + hg pull -u + + + + +
+
+
+ + + Conseils utiles pour les débutants + + Avec la plupart des gestionnaire de versions, afficher + un diff associé à une révision peut être assez douloureux. Par exemple, + avec Subversion, pour voir ce qui a été modifiée dans la révision 104654, + vous devez saisir svn diff -r104653:104654. Mercurial + élimine le besoin de saisir l'identifiant d'une révision deux fois dans + ce cas classique. Pour un simple diff, hg + export 104654 suffit. Pour obtenir une entrée du journal suivie d'un diff, + hg log -r104654 -p. + + Quand vous exécutez la commande hg status + sans aucun argument, elle affiche l'état de l'ensemble de l'arborescence, + avec des chemins relatifs partant de la racine du dépôt. Ceci rend + difficile de copier un nom de fichier depuis la sortie de la commande + hg status dans une autre ligne de commande. Si vous + fournissez un fichier ou un répertoire à la commande hg + status, elle va afficher les chemins relatif depuis votre + répertoire courant à la place. Ainsi, pour avoir un état sur l'ensemble + de l'arborescence à l'aide de hg status, avec des + chemins relatifs à votre répertoire courant, et non la racine du dépôt, + ajoutez la sortie de hg root à la commande + hg status. Vous pouvez le faire aisément sur un + système Unix ainsi : + + $ hg status `hg root` + +
+ + + + + + + + + Mercurial Queues reference + + + MQ command reference + + For an overview of the commands provided by MQ, use the + command hg help mq. + + + <command role="hg-ext-mq" moreinfo="none">qapplied</command>—print + applied patches + + The qapplied command + prints the current stack of applied patches. Patches are + printed in oldest-to-newest order, so the last patch in the + list is the top patch. + + + + <command role="hg-ext-mq" moreinfo="none">qcommit</command>—commit + changes in the queue repository + + The qcommit command + commits any outstanding changes in the .hg/patches + repository. This command only works if the .hg/patches + directory is a repository, i.e. you created the directory + using hg qinit or + ran hg init in the directory + after running qinit. + + This command is shorthand for hg + commit --cwd .hg/patches. + + + <command role="hg-ext-mq" moreinfo="none">qdelete</command>—delete a patch + from the <filename role="special" moreinfo="none">series</filename> + file + + The qdelete command + removes the entry for a patch from the series file in the .hg/patches + directory. It does not pop the patch if the patch is already + applied. By default, it does not delete the patch file; use + the option + to do that. + + Options: + + : Delete the + patch file. + + + + + <command role="hg-ext-mq" moreinfo="none">qdiff</command>—print a + diff of the topmost applied patch + + The qdiff command + prints a diff of the topmost applied patch. It is equivalent + to hg diff -r-2:-1. + + + + <command role="hg-ext-mq" moreinfo="none">qfold</command>—move + applied patches into repository history + + The hg qfinish command converts the + specified applied patches into permanent changes by moving + them out of MQ's control so that they will be treated as + normal repository history. + + + + <command role="hg-ext-mq" moreinfo="none">qfold</command>—merge + (<quote>fold</quote>) several patches into one + + The qfold command + merges multiple patches into the topmost applied patch, so + that the topmost applied patch makes the union of all of the + changes in the patches in question. + + The patches to fold must not be applied; qfold will exit with an error if + any is. The order in which patches are folded is significant; + hg qfold a b means + apply the current topmost patch, followed by + a, followed by + b. + + The comments from the folded patches are appended to the + comments of the destination patch, with each block of comments + separated by three asterisk + (*) characters. Use the + option to + edit the commit message for the combined patch/changeset after + the folding has completed. + + Options: + + : Edit the + commit message and patch description for the newly folded + patch. + + : Use the + contents of the given file as the new commit message and + patch description for the folded patch. + + : Use the + given text as the new commit message and patch description + for the folded patch. + + + + + <command role="hg-ext-mq" moreinfo="none">qheader</command>—display the + header/description of a patch + + The qheader command + prints the header, or description, of a patch. By default, it + prints the header of the topmost applied patch. Given an + argument, it prints the header of the named patch. + + + + <command role="hg-ext-mq" moreinfo="none">qimport</command>—import + a third-party patch into the queue + + The qimport command + adds an entry for an external patch to the series file, and copies the patch + into the .hg/patches directory. It adds + the entry immediately after the topmost applied patch, but + does not push the patch. + + If the .hg/patches directory is a + repository, qimport + automatically does an hg add + of the imported patch. + + + + <command role="hg-ext-mq" moreinfo="none">qinit</command>—prepare + a repository to work with MQ + + The qinit command + prepares a repository to work with MQ. It creates a directory + called .hg/patches. + + Options: + + : Create + .hg/patches as a repository + in its own right. Also creates a .hgignore file that will + ignore the status + file. + + + When the .hg/patches directory is a + repository, the qimport + and qnew commands + automatically hg add new + patches. + + + + <command role="hg-ext-mq" moreinfo="none">qnew</command>—create a + new patch + + The qnew command + creates a new patch. It takes one mandatory argument, the + name to use for the patch file. The newly created patch is + created empty by default. It is added to the series file after the current + topmost applied patch, and is immediately pushed on top of + that patch. + + If qnew finds modified + files in the working directory, it will refuse to create a new + patch unless the option is used + (see below). This behavior allows you to qrefresh your topmost applied + patch before you apply a new patch on top of it. + + Options: + + : Create a new + patch if the contents of the working directory are + modified. Any outstanding modifications are added to the + newly created patch, so after this command completes, the + working directory will no longer be modified. + + : Use the given + text as the commit message. This text will be stored at + the beginning of the patch file, before the patch + data. + + + + + <command role="hg-ext-mq" moreinfo="none">qnext</command>—print + the name of the next patch + + The qnext command + prints the name name of the next patch in the series file after the topmost + applied patch. This patch will become the topmost applied + patch if you run qpush. + + + + <command role="hg-ext-mq" moreinfo="none">qpop</command>—pop + patches off the stack + + The qpop command + removes applied patches from the top of the stack of applied + patches. By default, it removes only one patch. + + This command removes the changesets that represent the + popped patches from the repository, and updates the working + directory to undo the effects of the patches. + + This command takes an optional argument, which it uses as + the name or index of the patch to pop to. If given a name, it + will pop patches until the named patch is the topmost applied + patch. If given a number, qpop treats the number as an + index into the entries in the series file, counting from zero + (empty lines and lines containing only comments do not count). + It pops patches until the patch identified by the given index + is the topmost applied patch. + + The qpop command does + not read or write patches or the series file. It is thus safe to + qpop a patch that you have + removed from the series + file, or a patch that you have renamed or deleted entirely. + In the latter two cases, use the name of the patch as it was + when you applied it. + + By default, the qpop + command will not pop any patches if the working directory has + been modified. You can override this behavior using the + option, + which reverts all modifications in the working + directory. + + Options: + + : Pop all + applied patches. This returns the repository to its state + before you applied any patches. + + : Forcibly + revert any modifications to the working directory when + popping. + + : Pop a patch + from the named queue. + + + The qpop command + removes one line from the end of the status file for each patch that it + pops. + + + + <command role="hg-ext-mq" moreinfo="none">qprev</command>—print + the name of the previous patch + + The qprev command + prints the name of the patch in the series file that comes before the + topmost applied patch. This will become the topmost applied + patch if you run qpop. + + + + <command role="hg-ext-mq" moreinfo="none">qpush</command>—push + patches onto the stack + + The qpush command adds + patches onto the applied stack. By default, it adds only one + patch. + + This command creates a new changeset to represent each + applied patch, and updates the working directory to apply the + effects of the patches. + + The default data used when creating a changeset are as + follows: + + The commit date and time zone are the current + date and time zone. Because these data are used to + compute the identity of a changeset, this means that if + you qpop a patch and + qpush it again, the + changeset that you push will have a different identity + than the changeset you popped. + + The author is the same as the default used by + the hg commit + command. + + The commit message is any text from the patch + file that comes before the first diff header. If there is + no such text, a default commit message is used that + identifies the name of the patch. + + If a patch contains a Mercurial patch header, + the information in the patch header overrides these + defaults. + + Options: + + : Push all + unapplied patches from the series file until there are + none left to push. + + : Add the name + of the patch to the end of the commit message. + + : If a patch + fails to apply cleanly, use the entry for the patch in + another saved queue to compute the parameters for a + three-way merge, and perform a three-way merge using the + normal Mercurial merge machinery. Use the resolution of + the merge as the new patch content. + + : Use the + named queue if merging while pushing. + + + The qpush command + reads, but does not modify, the series file. It appends one line + to the hg status file for + each patch that it pushes. + + + + <command role="hg-ext-mq" moreinfo="none">qrefresh</command>—update the + topmost applied patch + + The qrefresh command + updates the topmost applied patch. It modifies the patch, + removes the old changeset that represented the patch, and + creates a new changeset to represent the modified + patch. + + The qrefresh command + looks for the following modifications: + + Changes to the commit message, i.e. the text + before the first diff header in the patch file, are + reflected in the new changeset that represents the + patch. + + Modifications to tracked files in the working + directory are added to the patch. + + Changes to the files tracked using hg add, hg copy, hg remove, or hg rename. Added files and copy + and rename destinations are added to the patch, while + removed files and rename sources are removed. + + + Even if qrefresh + detects no changes, it still recreates the changeset that + represents the patch. This causes the identity of the + changeset to differ from the previous changeset that + identified the patch. + + Options: + + : Modify + the commit and patch description, using the preferred text + editor. + + : Modify + the commit message and patch description, using the given + text. + + : Modify + the commit message and patch description, using text from + the given file. + + + + + <command role="hg-ext-mq" moreinfo="none">qrename</command>—rename + a patch + + The qrename command + renames a patch, and changes the entry for the patch in the + series file. + + With a single argument, qrename renames the topmost + applied patch. With two arguments, it renames its first + argument to its second. + + + + <command role="hg-ext-mq" moreinfo="none">qseries</command>—print + the entire patch series + + The qseries command + prints the entire patch series from the series file. It prints only patch + names, not empty lines or comments. It prints in order from + first to be applied to last. + + + + <command role="hg-ext-mq" moreinfo="none">qtop</command>—print the + name of the current patch + + The qtop prints the + name of the topmost currently applied patch. + + + + <command role="hg-ext-mq" moreinfo="none">qunapplied</command>—print patches + not yet applied + + The qunapplied command + prints the names of patches from the series file that are not yet + applied. It prints them in order from the next patch that + will be pushed to the last. + + + + <command role="hg-cmd" moreinfo="none">hg strip</command>—remove a + revision and descendants + + The hg strip command + removes a revision, and all of its descendants, from the + repository. It undoes the effects of the removed revisions + from the repository, and updates the working directory to the + first parent of the removed revision. + + The hg strip command + saves a backup of the removed changesets in a bundle, so that + they can be reapplied if removed in error. + + Options: + + : Save + unrelated changesets that are intermixed with the stripped + changesets in the backup bundle. + + : If a + branch has multiple heads, remove all heads. + + : Do + not save a backup bundle. + + + + + + MQ file reference + + + The <filename role="special" moreinfo="none">series</filename> + file + + The series file + contains a list of the names of all patches that MQ can apply. + It is represented as a list of names, with one name saved per + line. Leading and trailing white space in each line are + ignored. + + Lines may contain comments. A comment begins with the + # character, and extends to + the end of the line. Empty lines, and lines that contain only + comments, are ignored. + + You will often need to edit the series file by hand, hence the + support for comments and empty lines noted above. For + example, you can comment out a patch temporarily, and qpush will skip over that patch + when applying patches. You can also change the order in which + patches are applied by reordering their entries in the + series file. + + Placing the series + file under revision control is also supported; it is a good + idea to place all of the patches that it refers to under + revision control, as well. If you create a patch directory + using the + option to qinit, this will + be done for you automatically. + + + + The <filename role="special" moreinfo="none">status</filename> + file + + The status file + contains the names and changeset hashes of all patches that MQ + currently has applied. Unlike the series file, this file is not + intended for editing. You should not place this file under + revision control, or modify it in any way. It is used by MQ + strictly for internal book-keeping. + + + + + + + + + + + + + Installer Mercurial à partir des sources + + + Pour un système Unix ou similaire + + Si vous utilisez un système Unix ou similaire, pour lequel + une version récente de Python (2.3 ou plus) est disponible, l'installation + de Mercurial à partir des sources est simple. + + Téléchargez un paquet récent depuis http://www.selenic.com/mercurial/download. + + Extrayez le paquet : + gzip -dc mercurial-MYVERSION.tar.gz | tar xf - + + Allez dans le répertoires où les sources ont + été extraites et exécutez le script d'installation. Ce dernier compilera + Mercurial et l'installera dans votre répertoire utilisateur. + cd mercurial-MYVERSION +python setup.py install --force --home=$HOME + + + Lorsque l'installation est terminée, Mercurial se + trouvera dans le répertoire bin de votre répertoire + utilisateur. + N'oubliez pas de vérifier que ce répertoire se trouve dans la liste + des répertoires où votre shell recherche les exécutables. + + Vous devrez vraisemblablement définir la variable + d'environnement PYTHONPATH de manière à ce que + l'exécutable de Mercurial puisse trouver le reste des paquets logiciels. + Par exemple, sur mon ordinateur portable, je dois le définir ainsi: + /home/bos/lib/python. Le chemin exact à utiliser + dépendra de la manière dont Python aura été construit pour votre + système. Il ne devrait pas être difficile de le trouver. En cas de + doute, lisez le texte généré lors de l'installation ci-dessus, et + recherchez l'emplacement où le contenu du répertoire + mercurial a été installé. + + + + Pour Windows + + Construire et installer Mercurial sous Windows nécessite + des outils logiciels divers, une certaine connaissance technique et une + bonne dose de patience. Je vous déconseille fortement + de tenter de le faire si vous êtes un simple utilisateur. + A moins que vous n'ayez l'intention de "hacker" Mercurial, je vous + suggère d'avoir recours à un paquet d'installation de la version binaire. + + Si vous avez vraiment l'intention de construire + Mercurial à partir des sources sous Windows, suivez les indications pour + ce chemin laborieux sur le wiki de Mercurial : http://www.selenic.com/mercurial/wiki/index.cgi/WindowsInstall, + et préparez vous à un travail épineux. + + + + + + + + + + + + Open Publication License + + Version 1.0, 8 June 1999 + + + Requirements on both unmodified and modified + versions + + The Open Publication works may be reproduced and distributed + in whole or in part, in any medium physical or electronic, + provided that the terms of this license are adhered to, and that + this license or an incorporation of it by reference (with any + options elected by the author(s) and/or publisher) is displayed + in the reproduction. + + Proper form for an incorporation by reference is as + follows: + +
+ Copyright (c) year by + author's name or designee. This material + may be distributed only subject to the terms and conditions + set forth in the Open Publication License, + vx.y or later (the latest version is + presently available at http://www.opencontent.org/openpub/). +
+ + The reference must be immediately followed with any options + elected by the author(s) and/or publisher of the document (see + ). + + Commercial redistribution of Open Publication-licensed + material is permitted. + + Any publication in standard (paper) book form shall require + the citation of the original publisher and author. The publisher + and author's names shall appear on all outer surfaces of the + book. On all outer surfaces of the book the original publisher's + name shall be as large as the title of the work and cited as + possessive with respect to the title. + +
+ + Copyright + + The copyright to each Open Publication is owned by its + author(s) or designee. + + + + Scope of license + + The following license terms apply to all Open Publication + works, unless otherwise explicitly stated in the + document. + + Mere aggregation of Open Publication works or a portion of + an Open Publication work with other works or programs on the + same media shall not cause this license to apply to those other + works. The aggregate work shall contain a notice specifying the + inclusion of the Open Publication material and appropriate + copyright notice. + + Severability. If any part + of this license is found to be unenforceable in any + jurisdiction, the remaining portions of the license remain in + force. + + No warranty. Open + Publication works are licensed and provided as is + without warranty of any kind, express or implied, including, but + not limited to, the implied warranties of merchantability and + fitness for a particular purpose or a warranty of + non-infringement. + + + + Requirements on modified works + + All modified versions of documents covered by this license, + including translations, anthologies, compilations and partial + documents, must meet the following requirements: + + + The modified version must be labeled as + such. + + The person making the modifications must be + identified and the modifications dated. + + Acknowledgement of the original author and + publisher if applicable must be retained according to normal + academic citation practices. + + The location of the original unmodified document + must be identified. + + The original author's (or authors') name(s) may + not be used to assert or imply endorsement of the resulting + document without the original author's (or authors') + permission. + + + + + Good-practice recommendations + + In addition to the requirements of this license, it is + requested from and strongly recommended of redistributors + that: + + + If you are distributing Open Publication works + on hardcopy or CD-ROM, you provide email notification to the + authors of your intent to redistribute at least thirty days + before your manuscript or media freeze, to give the authors + time to provide updated documents. This notification should + describe modifications, if any, made to the document. + + All substantive modifications (including + deletions) be either clearly marked up in the document or + else described in an attachment to the document. + + Finally, while it is not mandatory under this + license, it is considered good form to offer a free copy of + any hardcopy and CD-ROM expression of an Open + Publication-licensed work to its author(s). + + + + + License options + + The author(s) and/or publisher of an Open + Publication-licensed document may elect certain options by + appending language to the reference to or copy of the license. + These options are considered part of the license instance and + must be included with the license (or its incorporation by + reference) in derived works. + + + To prohibit distribution of substantively + modified versions without the explicit permission of the + author(s). Substantive modification is + defined as a change to the semantic content of the document, + and excludes mere changes in format or typographical + corrections. + + To accomplish this, add the phrase + Distribution of substantively modified versions of + this document is prohibited without the explicit + permission of the copyright holder. to the license + reference or copy. + + To prohibit any publication of this work or + derivative works in whole or in part in standard (paper) + book form for commercial purposes is prohibited unless prior + permission is obtained from the copyright holder. + + To accomplish this, add the phrase + Distribution of the work or derivative of the work in + any standard (paper) book form is prohibited unless prior + permission is obtained from the copyright holder. + to the license reference or copy. + + + +
+ + + +